Skip to content

Commit 00ab4c1

Browse files
authored
Define new field dynamic_sygnal_types for otel input packages (#1067)
Add a new manifest field dynamic_signal_types under policy_template.
1 parent cc1b159 commit 00ab4c1

File tree

38 files changed

+542
-0
lines changed

38 files changed

+542
-0
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
// or more contributor license agreements. Licensed under the Elastic License;
3+
// you may not use this file except in compliance with the Elastic License.
4+
5+
package semantic
6+
7+
import (
8+
"io/fs"
9+
10+
"gopkg.in/yaml.v3"
11+
12+
"github.com/elastic/package-spec/v3/code/go/internal/fspath"
13+
"github.com/elastic/package-spec/v3/code/go/pkg/specerrors"
14+
)
15+
16+
const (
17+
otelcolInputType string = "otelcol"
18+
)
19+
20+
type inputPolicyTemplateWithDynamic struct {
21+
Name string `yaml:"name"`
22+
Input string `yaml:"input"`
23+
DynamicSignalTypes bool `yaml:"dynamic_signal_types"` // true or false
24+
}
25+
26+
type inputPackageManifestDynamic struct {
27+
Type string `yaml:"type"`
28+
PolicyTemplates []inputPolicyTemplateWithDynamic `yaml:"policy_templates"`
29+
}
30+
31+
// ValidateInputDynamicSignalTypes validates that dynamic_signal_types field is only used with otelcol input type
32+
func ValidateInputDynamicSignalTypes(fsys fspath.FS) specerrors.ValidationErrors {
33+
var errs specerrors.ValidationErrors
34+
35+
manifestPath := "manifest.yml"
36+
data, err := fs.ReadFile(fsys, manifestPath)
37+
if err != nil {
38+
return specerrors.ValidationErrors{
39+
specerrors.NewStructuredErrorf("file \"%s\" is invalid: failed to read manifest: %w", fsys.Path(manifestPath), err)}
40+
}
41+
42+
var manifest inputPackageManifestDynamic
43+
err = yaml.Unmarshal(data, &manifest)
44+
if err != nil {
45+
return specerrors.ValidationErrors{
46+
specerrors.NewStructuredErrorf("file \"%s\" is invalid: failed to parse manifest: %w", fsys.Path(manifestPath), err)}
47+
}
48+
49+
for _, policyTemplate := range manifest.PolicyTemplates {
50+
// Check if dynamic_signal_types is explicitly set
51+
if policyTemplate.DynamicSignalTypes {
52+
// Must be input package type
53+
if manifest.Type != inputPackageType {
54+
errs = append(errs, specerrors.NewStructuredErrorf(
55+
"file \"%s\" is invalid: policy template \"%s\": dynamic_signal_types is only allowed for input type packages",
56+
fsys.Path(manifestPath), policyTemplate.Name))
57+
continue
58+
}
59+
// Must be otelcol input
60+
if policyTemplate.Input != otelcolInputType {
61+
errs = append(errs, specerrors.NewStructuredErrorf(
62+
"file \"%s\" is invalid: policy template \"%s\": dynamic_signal_types is only allowed when input is 'otelcol', got '%s'",
63+
fsys.Path(manifestPath), policyTemplate.Name, policyTemplate.Input))
64+
}
65+
}
66+
}
67+
68+
return errs
69+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
// or more contributor license agreements. Licensed under the Elastic License;
3+
// you may not use this file except in compliance with the Elastic License.
4+
5+
package semantic
6+
7+
import (
8+
"os"
9+
"testing"
10+
11+
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
13+
14+
"github.com/elastic/package-spec/v3/code/go/internal/fspath"
15+
)
16+
17+
func TestValidateInputDynamicSignalTypes(t *testing.T) {
18+
19+
t.Run("valid_otelcol_with_dynamic_signal_types_true", func(t *testing.T) {
20+
d := t.TempDir()
21+
22+
err := os.WriteFile(d+"/manifest.yml", []byte(`
23+
type: input
24+
policy_templates:
25+
- name: otel_logs
26+
input: otelcol
27+
template_path: input.yml.hbs
28+
dynamic_signal_types: true
29+
`), 0o644)
30+
require.NoError(t, err)
31+
32+
errs := ValidateInputDynamicSignalTypes(fspath.DirFS(d))
33+
require.Empty(t, errs, "expected no validation errors")
34+
})
35+
36+
t.Run("valid_otelcol_with_dynamic_signal_types_false", func(t *testing.T) {
37+
d := t.TempDir()
38+
39+
err := os.WriteFile(d+"/manifest.yml", []byte(`
40+
type: input
41+
policy_templates:
42+
- name: otel_logs
43+
input: otelcol
44+
template_path: input.yml.hbs
45+
dynamic_signal_types: false
46+
`), 0o644)
47+
require.NoError(t, err)
48+
49+
errs := ValidateInputDynamicSignalTypes(fspath.DirFS(d))
50+
require.Empty(t, errs, "expected no validation errors")
51+
})
52+
53+
t.Run("valid_otelcol_without_dynamic_signal_types", func(t *testing.T) {
54+
d := t.TempDir()
55+
56+
err := os.WriteFile(d+"/manifest.yml", []byte(`
57+
type: input
58+
policy_templates:
59+
- name: otel_logs
60+
input: otelcol
61+
template_path: input.yml.hbs
62+
`), 0o644)
63+
require.NoError(t, err)
64+
65+
errs := ValidateInputDynamicSignalTypes(fspath.DirFS(d))
66+
require.Empty(t, errs, "expected no validation errors")
67+
})
68+
69+
t.Run("invalid_non_otelcol_with_dynamic_signal_types_true", func(t *testing.T) {
70+
d := t.TempDir()
71+
72+
err := os.WriteFile(d+"/manifest.yml", []byte(`
73+
type: input
74+
policy_templates:
75+
- name: logfile
76+
input: logfile
77+
template_path: input.yml.hbs
78+
dynamic_signal_types: true
79+
`), 0o644)
80+
require.NoError(t, err)
81+
82+
errs := ValidateInputDynamicSignalTypes(fspath.DirFS(d))
83+
require.NotEmpty(t, errs, "expected validation errors")
84+
assert.Len(t, errs, 1)
85+
assert.Contains(t, errs[0].Error(), "dynamic_signal_types is only allowed when input is 'otelcol'")
86+
assert.Contains(t, errs[0].Error(), "got 'logfile'")
87+
})
88+
89+
t.Run("invalid_non_input_package_with_dynamic_signal_types", func(t *testing.T) {
90+
d := t.TempDir()
91+
92+
err := os.WriteFile(d+"/manifest.yml", []byte(`
93+
type: integration
94+
policy_templates:
95+
- name: apache
96+
dynamic_signal_types: true
97+
`), 0o644)
98+
require.NoError(t, err)
99+
100+
errs := ValidateInputDynamicSignalTypes(fspath.DirFS(d))
101+
require.NotEmpty(t, errs, "expected validation errors for non-input packages")
102+
assert.Len(t, errs, 1)
103+
assert.Contains(t, errs[0].Error(), "dynamic_signal_types is only allowed for input type packages")
104+
})
105+
106+
t.Run("valid_non_input_package_without_dynamic_signal_types", func(t *testing.T) {
107+
d := t.TempDir()
108+
109+
err := os.WriteFile(d+"/manifest.yml", []byte(`
110+
type: integration
111+
policy_templates:
112+
- name: apache
113+
`), 0o644)
114+
require.NoError(t, err)
115+
116+
errs := ValidateInputDynamicSignalTypes(fspath.DirFS(d))
117+
require.Empty(t, errs, "expected no validation errors for non-input packages without field")
118+
})
119+
120+
t.Run("valid_non_otelcol_without_dynamic_signal_types", func(t *testing.T) {
121+
d := t.TempDir()
122+
123+
err := os.WriteFile(d+"/manifest.yml", []byte(`
124+
type: input
125+
policy_templates:
126+
- name: logfile
127+
input: logfile
128+
template_path: input.yml.hbs
129+
`), 0o644)
130+
require.NoError(t, err)
131+
132+
errs := ValidateInputDynamicSignalTypes(fspath.DirFS(d))
133+
require.Empty(t, errs, "expected no validation errors")
134+
})
135+
136+
t.Run("invalid_multiple_templates_one_invalid", func(t *testing.T) {
137+
d := t.TempDir()
138+
139+
err := os.WriteFile(d+"/manifest.yml", []byte(`
140+
type: input
141+
policy_templates:
142+
- name: otel_logs
143+
input: otelcol
144+
template_path: input.yml.hbs
145+
dynamic_signal_types: true
146+
- name: file_logs
147+
input: logfile
148+
template_path: input2.yml.hbs
149+
dynamic_signal_types: true
150+
`), 0o644)
151+
require.NoError(t, err)
152+
153+
errs := ValidateInputDynamicSignalTypes(fspath.DirFS(d))
154+
require.NotEmpty(t, errs, "expected validation errors")
155+
assert.Len(t, errs, 1)
156+
assert.Contains(t, errs[0].Error(), "dynamic_signal_types is only allowed when input is 'otelcol'")
157+
assert.Contains(t, errs[0].Error(), "file_logs")
158+
assert.Contains(t, errs[0].Error(), "got 'logfile'")
159+
})
160+
}

‎code/go/internal/validator/spec.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ func (s Spec) rules(pkgType string, rootSpec spectypes.ItemSpec) validationRules
223223
{fn: semantic.ValidateDeploymentModes, types: []string{"integration"}},
224224
{fn: semantic.ValidateDurationVariables, since: semver.MustParse("3.5.0")},
225225
{fn: semantic.ValidateInputPackagesPolicyTemplates, types: []string{"input"}},
226+
{fn: semantic.ValidateInputDynamicSignalTypes},
226227
{fn: semantic.ValidateMinimumAgentVersion},
227228
{fn: semantic.ValidateIntegrationPolicyTemplates, types: []string{"integration"}},
228229
{fn: semantic.ValidatePipelineTags, types: []string{"integration"}, since: semver.MustParse("3.6.0")},

‎code/go/pkg/validator/validator_test.go‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func TestValidateFile(t *testing.T) {
3636
"good_var_groups": {},
3737
"good_input": {},
3838
"good_input_otel": {},
39+
"good_input_dynamic_signal_type": {},
3940
"good_content": {},
4041
"good_lookup_index": {},
4142
"good_alert_rule_templates": {},
@@ -336,6 +337,25 @@ func TestValidateFile(t *testing.T) {
336337
"field policy_templates.0.input: Must not be present",
337338
},
338339
},
340+
"bad_input_dynamic_signal_types_non_otel": {
341+
"manifest.yml",
342+
[]string{
343+
"policy template \"sample\": dynamic_signal_types is only allowed when input is 'otelcol', got 'logfile'",
344+
},
345+
},
346+
"bad_integration_dynamic_signal_types": {
347+
"manifest.yml",
348+
[]string{
349+
"field policy_templates.0: Additional property dynamic_signal_types is not allowed",
350+
"policy template \"sample\": dynamic_signal_types is only allowed for input type packages",
351+
},
352+
},
353+
"bad_input_dynamic_signal_types_old_version": {
354+
"manifest.yml",
355+
[]string{
356+
"field policy_templates.0: Additional property dynamic_signal_types is not allowed",
357+
},
358+
},
339359
"bad_input_template_path": {
340360
"manifest.yml",
341361
[]string{

‎spec/changelog.yml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
- description: Allow to set time series index mode in input packages.
3131
type: enhancement
3232
link: https://github.com/elastic/package-spec/pull/1066
33+
- description: Add support for dynamic_signal_types field in input package policy templates for OTel input type.
34+
type: enhancement
35+
link: https://github.com/elastic/package-spec/pull/1067
3336
- version: 3.5.6-next
3437
changes:
3538
- description: Add validation for Kibana tag duplicates.

‎spec/input/manifest.spec.yml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ spec:
8888
template_path:
8989
description: "Path to Elasticsearch index template for stream."
9090
type: string
91+
dynamic_signal_types:
92+
description: "When enabled, decides the transforms and index templates that need to be created depending on the pipelines specified in the configuration."
93+
type: boolean
94+
default: false
9195
deprecated:
9296
$ref: "../integration/manifest.spec.yml#/definitions/deprecated"
9397
required:
@@ -131,6 +135,8 @@ spec:
131135
versions:
132136
- before: 3.6.0
133137
patch:
138+
- op: remove
139+
path: "/properties/policy_templates/items/properties/dynamic_signal_types" # removes dynamic_signal_types field for policy templates
134140
- op: remove
135141
path: "/properties/elasticsearch/properties/index_mode" # Fleet needs to check that it is not enabled in non-metrics data streams
136142
- op: remove
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Apache License 2.0
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Test package
2+
3+
This is a test package for validating dynamic_signal_types field validation.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
data_stream:
2+
dataset: {{data_stream.dataset}}
3+
paths:
4+
{{#each paths as |path i|}}
5+
- {{path}}
6+
{{/each}}
7+
exclude_files: [".gz$"]
8+
processors:
9+
- add_locale: ~
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# newer versions go on top
2+
- version: "0.0.1"
3+
changes:
4+
- description: Initial draft of the package
5+
type: enhancement
6+
link: https://github.com/elastic/integrations/pull/1

0 commit comments

Comments
 (0)