Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
acedfd0
Support OTel config in policy tests
jsoriano Aug 1, 2025
2c5c553
Add test package
mrodm Sep 4, 2025
6f8de38
Add kibana feature flag
mrodm Sep 4, 2025
b19a6d8
Remove debug level for fleet plugin
mrodm Sep 4, 2025
304ef01
Fix error in kibana config server.ssl for stack versions < 8.15.0
mrodm Sep 4, 2025
6b25a60
Move comments
mrodm Sep 4, 2025
b649672
Add namespaces field to test package
mrodm Sep 4, 2025
0ad6db6
Set specific version
mrodm Sep 4, 2025
37b53c9
Do not replace values in memberReplace case
mrodm Sep 4, 2025
5fe45aa
Revert "Add namespaces field to test package"
mrodm Sep 4, 2025
94ed714
Skip system test for httpcheck test package
mrodm Sep 4, 2025
ad580ae
Update spec version for httpcheck
mrodm Sep 4, 2025
b5ac0b5
Do not replace strings in memberReplace
mrodm Sep 4, 2025
a5e135a
Update tests including more than one OTEL processor
mrodm Sep 5, 2025
2e85b42
Update otel IDs in test
mrodm Sep 8, 2025
50de2aa
Replace otel IDs definitions and usages using regexes
mrodm Sep 8, 2025
075be2a
Allow to be otel IDs with empty maps
mrodm Sep 8, 2025
68f0e09
Add comments and remove unused code
mrodm Sep 8, 2025
6c0ccf4
Merge remote-tracking branch 'upstream/main' into support-policy-test…
mrodm Sep 8, 2025
ea59a6b
Update function documentation
mrodm Sep 8, 2025
3750339
Add connectors to OTEL components regex
mrodm Sep 9, 2025
c68ae5c
Remove unnecessary if
mrodm Sep 9, 2025
640a700
Use strings.Cut instead of strings.SplitN
mrodm Sep 9, 2025
0c5483c
Use scanner to read matching lines of the given policy
mrodm Sep 9, 2025
7a7dc37
Add variable to system test - httpcheck package
mrodm Sep 9, 2025
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions internal/stack/_static/kibana.yml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,18 @@ xpack.fleet.registryUrl: "https://package-registry:8080"
xpack.fleet.agents.enabled: true
xpack.fleet.agents.fleet_server.hosts: ["{{ fact "fleet_url" }}"]

{{ if and (not (semverLessThan $version "8.7.0")) (semverLessThan $version "8.10.0-SNAPSHOT") }}
xpack.fleet.enableExperimental: ["experimentalDataStreamSettings"] # Enable experimental toggles in Fleet UI
{{ end }}
{{/* Feature flags introduced in 8.3.0 https://github.com/elastic/kibana/pull/130253 */}}
{{- if (not (semverLessThan $version "8.3.0")) }}
xpack.fleet.enableExperimental: [
{{- if and (not (semverLessThan $version "8.7.0")) (semverLessThan $version "8.10.0-SNAPSHOT") }}
# Enable experimental toggles in Fleet UI
"experimentalDataStreamSettings"
{{- else if (not (semverLessThan $version "9.2.0-SNAPSHOT")) }}
# Support for otelcol input
"enableOtelIntegrations"
{{- end }}
]
{{- end }}

xpack.encryptedSavedObjects.encryptionKey: "12345678901234567890123456789012"

Expand Down
80 changes: 78 additions & 2 deletions internal/testrunner/runners/policy/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package policy

import (
"bufio"
"bytes"
"context"
"errors"
Expand All @@ -13,13 +14,15 @@ import (
"path/filepath"
"regexp"
"slices"
"strconv"
"strings"

"github.com/pmezard/go-difflib/difflib"
"gopkg.in/yaml.v3"

"github.com/elastic/elastic-package/internal/common"
"github.com/elastic/elastic-package/internal/kibana"
"github.com/elastic/elastic-package/internal/logger"
)

func dumpExpectedAgentPolicy(ctx context.Context, kibanaClient *kibana.Client, testPath string, policyID string) error {
Expand Down Expand Up @@ -71,6 +74,9 @@ func comparePolicies(expected, found []byte) (string, error) {
if err != nil {
return "", fmt.Errorf("failed to prepare found policy: %w", err)
}
logger.Tracef("expected policy after cleaning:\n%s", want)
logger.Tracef("found policy after cleaning:\n%s", got)

if bytes.Equal(want, got) {
return "", nil
}
Expand Down Expand Up @@ -166,10 +172,35 @@ var policyEntryFilters = []policyEntryFilter{
}},
}

var uniqueOtelComponentIDReplace = policyEntryReplace{
regexp: regexp.MustCompile(`^(\s{2,})([^/]+)/([^:]+):(\s\{\}|\s*)$`),
replace: "$1$2/componentid-%s:$4",
}

// otelComponentIDsRegexp is the regex to find otel components sections and their IDs to replace them with controlled values.
// It matches sections like:
//
// extensions:
// health_check/4391d954-1ffe-4014-a256-5eda78a71828: {}
//
// receivers:
// httpcheck/b0f518d6-4e2d-4c5d-bda7-f9808df537b7:
// collection_interval: 1m
// targets:
// - endpoints:
// - https://epr.elastic.co
// method: GET
var otelComponentIDsRegexp = regexp.MustCompile(`(?m)^(?:extensions|receivers|processors|connectors|exporters):(?:\s\{\}\n|\n(?:\s{2,}.+\n)+)`)

// cleanPolicy prepares a policy YAML as returned by the download API to be compared with other
// policies. This preparation is based on removing contents that are generated, or replace them
// by controlled values.
func cleanPolicy(policy []byte, entriesToClean []policyEntryFilter) ([]byte, error) {
// Replacement of the OTEL component IDs needs to be done before unmarshalling the YAML.
// The OTEL IDs are keys in maps, and using the policyEntryFilter with memberReplace does
// not ensure to keep the same ordering.
policy = replaceOtelComponentIDs(policy)

var policyMap common.MapStr
err := yaml.Unmarshal(policy, &policyMap)
if err != nil {
Expand All @@ -184,6 +215,47 @@ func cleanPolicy(policy []byte, entriesToClean []policyEntryFilter) ([]byte, err
return yaml.Marshal(policyMap)
}

// replaceOtelComponentIDs finds OTel Collector component IDs in the policy and replaces them with controlled values.
// It also replaces references to those IDs in service.extensions and service.pipelines.
func replaceOtelComponentIDs(policy []byte) []byte {
replacementsDone := map[string]string{}

policy = otelComponentIDsRegexp.ReplaceAllFunc(policy, func(match []byte) []byte {
count := 0
scanner := bufio.NewScanner(bytes.NewReader(match))
var section strings.Builder
for scanner.Scan() {
line := scanner.Text()
if uniqueOtelComponentIDReplace.regexp.MatchString(line) {
originalOtelID, _, _ := strings.Cut(strings.TrimSpace(line), ":")

replacement := fmt.Sprintf(uniqueOtelComponentIDReplace.replace, strconv.Itoa(count))
count++
line = uniqueOtelComponentIDReplace.regexp.ReplaceAllString(line, replacement)

// store the otel ID replaced without the space indentation and the colon to be replaced later
// (e.g. http_check/4391d954-1ffe-4014-a256-5eda78a71828 replaced by http_check/componentid-0)
replacementsDone[originalOtelID], _, _ = strings.Cut(strings.TrimSpace(string(line)), ":")
}
section.WriteString(line + "\n")
}

return []byte(section.String())
})

// Replace references in arrays to the otel component IDs replaced before.
// These references can be in:
// service.extensions
// service.pipelines.<signal>.(receivers|processors|exporters)
for original, replacement := range replacementsDone {
originalArrayItem := fmt.Sprintf("- %s", original)
replacedArrayItem := fmt.Sprintf("- %s", replacement)

policy = bytes.ReplaceAll(policy, []byte(originalArrayItem), []byte(replacedArrayItem))
}
return policy
}

func cleanPolicyMap(policyMap common.MapStr, entries []policyEntryFilter) (common.MapStr, error) {
for _, entry := range entries {
v, err := policyMap.GetValue(entry.name)
Expand Down Expand Up @@ -218,10 +290,14 @@ func cleanPolicyMap(policyMap common.MapStr, entries []policyEntryFilter) (commo
if !ok {
return nil, fmt.Errorf("expected map, found %T", v)
}
regexp := entry.memberReplace.regexp
replacement := entry.memberReplace.replace
for k, e := range m {
if entry.memberReplace.regexp.MatchString(k) {
key := k
if regexp.MatchString(k) {
delete(m, k)
m[entry.memberReplace.replace] = e
key = regexp.ReplaceAllString(k, replacement)
m[key] = e
}
}
default:
Expand Down
192 changes: 190 additions & 2 deletions internal/testrunner/runners/policy/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ output_permissions:
- monitor
_elastic_agent_monitoring:
indices: []
uuid-for-permissions-on-related-indices:
8d024b11-4e82-4192-8e7f-be71d1b13aac:
indices:
- names:
- metrics-*-*
Expand Down Expand Up @@ -221,7 +221,7 @@ output_permissions:
- monitor
_elastic_agent_monitoring:
indices: []
uuid-for-permissions-on-related-indices:
bfe4f402-df02-4673-8a71-fd5b29f1e2f3:
indices:
- names:
- metrics-*-*
Expand Down Expand Up @@ -285,6 +285,194 @@ secret_references:
`,
equal: false,
},
{
title: "otel ids",
expected: `
inputs: []
output_permissions:
default:
_elastic_agent_checks:
cluster:
- monitor
_elastic_agent_monitoring:
indices: []
05c98f91-203c-44a9-bee7-dd621c9bd37e:
indices:
- names:
- logs-*-*
privileges:
- auto_configure
- create_doc
extensions:
health_check/31c94f44-214a-4778-8a36-acc2634096f7: {}
processors:
batch/11c35ad0-4351-49d4-9c78-fa679ce9d950:
send_batch_size: 10
timeout: 1s
batch/e6e379c5-6446-4090-af10-a9e5f8fc4640:
send_batch_size: 10000
timeout: 10s
Comment on lines +308 to +314
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the current implementation, this could fail in some test executions:

--- FAIL: TestComparePolicies (0.00s) --- FAIL: TestComparePolicies/otel_ids (0.00s) policy_test.go:472:	Error Trace:	/home/mariorodriguez/Coding/work/elastic-package-otel/internal/testrunner/runners/policy/policy_test.go:472	Error:	Should be empty, but was --- want	+++ got	@@ -17,4 +17,4 @@ batch/componentid:	- send_batch_size: 10	- timeout: 1s	+ send_batch_size: 10000	+ timeout: 10s receivers:	Test:	TestComparePolicies/otel_ids FAIL FAIL	github.com/elastic/elastic-package/internal/testrunner/runners/policy	0.061s FAIL 

Working to replace the OTEL ids (directly on the []byte variable) before running the cleanup process.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully, this is solved by replacing the OTEL IDs before cleaning up the policy with the current filters. This was added in 50de2aa

receivers:
httpcheck/4bae34b3-8f66-49c1-b04f-d58af1b5f743:
collection_interval: 1m
targets:
- endpoints:
- https://epr.elastic.co
method: GET
secret_references: []
service:
extensions:
- health_check/31c94f44-214a-4778-8a36-acc2634096f7
pipelines:
logs:
receivers:
- httpcheck/4bae34b3-8f66-49c1-b04f-d58af1b5f743
processors:
- batch/11c35ad0-4351-49d4-9c78-fa679ce9d950
- batch/e6e379c5-6446-4090-af10-a9e5f8fc4640
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is likely going to change as more plumbing is needed, see elastic/kibana#233090.

But we can go on with this change and regenerate files later.


`,
found: `
inputs: []
output_permissions:
default:
_elastic_agent_checks:
cluster:
- monitor
aeb4d606-2d90-4b41-b231-27bfad6dea09:
indices:
- names:
- logs-*-*
privileges:
- auto_configure
- create_doc
_elastic_agent_monitoring:
indices: []
extensions:
health_check/4391d954-1ffe-4014-a256-5eda78a71829: {}
processors:
batch/567fce7a-ff2e-4a6c-a32a-0abb4671b39b:
send_batch_size: 10
timeout: 1s
batch/8ec6ee99-2176-4231-9668-908069c77784:
send_batch_size: 10000
timeout: 10s
receivers:
httpcheck/b0f518d6-4e2d-4c5d-bda7-f9808df537b7:
collection_interval: 1m
targets:
- endpoints:
- https://epr.elastic.co
method: GET
secret_references: []
service:
extensions:
- health_check/4391d954-1ffe-4014-a256-5eda78a71829
pipelines:
logs:
receivers:
- httpcheck/b0f518d6-4e2d-4c5d-bda7-f9808df537b7
processors:
- batch/567fce7a-ff2e-4a6c-a32a-0abb4671b39b
- batch/8ec6ee99-2176-4231-9668-908069c77784

`,
equal: true,
},
{
title: "otel hardcode expected ids",
expected: `
inputs: []
output_permissions:
default:
_elastic_agent_checks:
cluster:
- monitor
_elastic_agent_monitoring:
indices: []
05c98f91-203c-44a9-bee7-dd621c9bd37e:
indices:
- names:
- logs-*-*
privileges:
- auto_configure
- create_doc
extensions:
health_check/componentid-0: {}
processors:
batch/componentid-0:
send_batch_size: 10
timeout: 1s
batch/componentid-1:
send_batch_size: 10000
timeout: 10s
receivers:
httpcheck/componentid-0:
collection_interval: 1m
targets:
- endpoints:
- https://epr.elastic.co
method: GET
secret_references: []
service:
extensions:
- health_check/componentid-0
pipelines:
logs:
receivers:
- httpcheck/componentid-0
processors:
- batch/componentid-0
- batch/componentid-1

`,
found: `
inputs: []
output_permissions:
default:
_elastic_agent_checks:
cluster:
- monitor
aeb4d606-2d90-4b41-b231-27bfad6dea09:
indices:
- names:
- logs-*-*
privileges:
- auto_configure
- create_doc
_elastic_agent_monitoring:
indices: []
extensions:
health_check/4391d954-1ffe-4014-a256-5eda78a71828: {}
processors:
batch/567fce7a-ff2e-4a6c-a32a-0abb4671b39b:
send_batch_size: 10
timeout: 1s
batch/8ec6ee99-2176-4231-9668-908069c77784:
send_batch_size: 10000
timeout: 10s
receivers:
httpcheck/b0f518d6-4e2d-4c5d-bda7-f9808df537b7:
collection_interval: 1m
targets:
- endpoints:
- https://epr.elastic.co
method: GET
Comment on lines +454 to +460
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jsoriano in these policies, could it be more than one receiver/exporter/extensions/processors?
Focusing on receiver as example, if there is another receiver defined, would it start also with httpcheck ?

receivers: httpcheck/b0f518d6-4e2d-4c5d-bda7-f9808df537b7: collection_interval: 1m targets: - endpoints: - https://epr.elastic.co method: GET httpcheck/b0f518d6-4e2d-4c5d-bda7-11111111111: collection_interval: 2m targets: - endpoints: - https://example.com method: GET 

If that could happen, the resulting yaml would contain just one receiver httpcheck/componentid

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In practice I don't think this has a real use case for receivers, but in theory this will be allowed, yes. And it will be definitely possible with processors.

So you are right that we may need some replacement that is not always the same to avoid collisions, but is still predictable.

secret_references: []
service:
extensions:
- health_check/4391d954-1ffe-4014-a256-5eda78a71828
pipelines:
logs:
receivers:
- httpcheck/b0f518d6-4e2d-4c5d-bda7-f9808df537b7
processors:
- batch/567fce7a-ff2e-4a6c-a32a-0abb4671b39b
- batch/8ec6ee99-2176-4231-9668-908069c77784

`,
equal: true,
},
}

for _, c := range cases {
Expand Down
1 change: 1 addition & 0 deletions test/packages/parallel/httpcheck.stack_version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
9.2.0-SNAPSHOT
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forced to run this package with 9.2.0-SNAPSHOT.

This should be removed once the default version is 9.2.0 or higher.

Loading