Skip to content

Commit edad324

Browse files
authored
Include the validation of transform fields (#1027)
Include the validation of the fields defined in transforms as part of the rest of the fields (data streams and input packages). Thereby, fields in transforms will run the same validations as the other fields with one exception about the required fields. It is not required to defined `data_stream.{type,namespace,dataset}` and `@timestamp` fields in transforms.
1 parent ee697d2 commit edad324

File tree

35 files changed

+834
-52
lines changed

35 files changed

+834
-52
lines changed

code/go/internal/validator/semantic/types.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ type field struct {
107107

108108
type fieldFileMetadata struct {
109109
dataStream string
110+
transform string
110111
filePath string
111112
fullFilePath string
112113
}
@@ -180,6 +181,7 @@ func listFieldsFiles(fsys fspath.FS) ([]fieldFileMetadata, error) {
180181
filePath: file,
181182
fullFilePath: fsys.Path(file),
182183
dataStream: dataStream,
184+
transform: "",
183185
})
184186
}
185187
}
@@ -197,9 +199,35 @@ func listFieldsFiles(fsys fspath.FS) ([]fieldFileMetadata, error) {
197199
filePath: file,
198200
fullFilePath: fsys.Path(file),
199201
dataStream: "",
202+
transform: "",
200203
})
201204
}
202205

206+
// transform definitions
207+
transforms, err := listTransforms(fsys)
208+
if err != nil {
209+
return nil, err
210+
}
211+
212+
for _, transform := range transforms {
213+
fieldsDir := path.Join("elasticsearch", "transform", transform, "fields")
214+
transformFieldsFiles, err := readFieldsFolder(fsys, fieldsDir)
215+
if err != nil {
216+
return nil, fmt.Errorf("cannot read fields file from integration packages: %w", err)
217+
}
218+
219+
for _, file := range transformFieldsFiles {
220+
fieldsFilesMetadata = append(
221+
fieldsFilesMetadata,
222+
fieldFileMetadata{
223+
filePath: file,
224+
fullFilePath: fsys.Path(file),
225+
dataStream: "",
226+
transform: transform,
227+
})
228+
}
229+
}
230+
203231
return fieldsFilesMetadata, nil
204232
}
205233

@@ -248,3 +276,20 @@ func listDataStreams(fsys fspath.FS) ([]string, error) {
248276
}
249277
return list, nil
250278
}
279+
280+
func listTransforms(fsys fspath.FS) ([]string, error) {
281+
transformDirectory := path.Join("elasticsearch", "transform")
282+
transforms, err := fs.ReadDir(fsys, transformDirectory)
283+
if errors.Is(err, os.ErrNotExist) {
284+
return nil, nil
285+
}
286+
if err != nil {
287+
return nil, fmt.Errorf("can't list transforms directory: %w", err)
288+
}
289+
290+
list := make([]string, len(transforms))
291+
for i, transform := range transforms {
292+
list[i] = transform.Name()
293+
}
294+
return list, nil
295+
}

code/go/internal/validator/semantic/types_test.go

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,87 +25,103 @@ func TestListFieldsFiles(t *testing.T) {
2525
{
2626
pkgName: "good_v2",
2727
expected: []fieldFileMetadata{
28-
fieldFileMetadata{
28+
{
2929
filePath: "data_stream/foo/fields/base-fields.yml",
3030
fullFilePath: filepath.FromSlash("../../../../../test/packages/good_v2/data_stream/foo/fields/base-fields.yml"),
3131
dataStream: "foo",
3232
},
33-
fieldFileMetadata{
33+
{
3434
filePath: "data_stream/foo/fields/external-fields.yml",
3535
fullFilePath: filepath.FromSlash("../../../../../test/packages/good_v2/data_stream/foo/fields/external-fields.yml"),
3636
dataStream: "foo",
3737
},
38-
fieldFileMetadata{
38+
{
3939
filePath: "data_stream/foo/fields/some_fields.yml",
4040
fullFilePath: filepath.FromSlash("../../../../../test/packages/good_v2/data_stream/foo/fields/some_fields.yml"),
4141
dataStream: "foo",
4242
},
43-
fieldFileMetadata{
43+
{
4444
filePath: "data_stream/hidden_data_stream/fields/base-fields.yml",
4545
fullFilePath: filepath.FromSlash("../../../../../test/packages/good_v2/data_stream/hidden_data_stream/fields/base-fields.yml"),
4646
dataStream: "hidden_data_stream",
4747
},
48-
fieldFileMetadata{
48+
{
4949
filePath: "data_stream/hidden_data_stream/fields/some_fields.yml",
5050
fullFilePath: filepath.FromSlash("../../../../../test/packages/good_v2/data_stream/hidden_data_stream/fields/some_fields.yml"),
5151
dataStream: "hidden_data_stream",
5252
},
53-
fieldFileMetadata{
53+
{
5454
filePath: "data_stream/ilm_policy/fields/base-fields.yml",
5555
fullFilePath: filepath.FromSlash("../../../../../test/packages/good_v2/data_stream/ilm_policy/fields/base-fields.yml"),
5656
dataStream: "ilm_policy",
5757
},
58-
fieldFileMetadata{
58+
{
5959
filePath: "data_stream/ilm_policy/fields/some_fields.yml",
6060
fullFilePath: filepath.FromSlash("../../../../../test/packages/good_v2/data_stream/ilm_policy/fields/some_fields.yml"),
6161
dataStream: "ilm_policy",
6262
},
63-
fieldFileMetadata{
63+
{
6464
filePath: "data_stream/k8s_data_stream/fields/base-fields.yml",
6565
fullFilePath: filepath.FromSlash("../../../../../test/packages/good_v2/data_stream/k8s_data_stream/fields/base-fields.yml"),
6666
dataStream: "k8s_data_stream",
6767
},
68-
fieldFileMetadata{
68+
{
6969
filePath: "data_stream/k8s_data_stream_no_definitions/fields/base-fields.yml",
7070
fullFilePath: filepath.FromSlash("../../../../../test/packages/good_v2/data_stream/k8s_data_stream_no_definitions/fields/base-fields.yml"),
7171
dataStream: "k8s_data_stream_no_definitions",
7272
},
73-
fieldFileMetadata{
73+
{
7474
filePath: "data_stream/pe/fields/base-fields.yml",
7575
fullFilePath: filepath.FromSlash("../../../../../test/packages/good_v2/data_stream/pe/fields/base-fields.yml"),
7676
dataStream: "pe",
7777
},
78-
fieldFileMetadata{
78+
{
7979
filePath: "data_stream/pe/fields/some_fields.yml",
8080
fullFilePath: filepath.FromSlash("../../../../../test/packages/good_v2/data_stream/pe/fields/some_fields.yml"),
8181
dataStream: "pe",
8282
},
83-
fieldFileMetadata{
83+
{
8484
filePath: "data_stream/routing_rules/fields/base-fields.yml",
8585
fullFilePath: filepath.FromSlash("../../../../../test/packages/good_v2/data_stream/routing_rules/fields/base-fields.yml"),
8686
dataStream: "routing_rules",
8787
},
88-
fieldFileMetadata{
88+
{
8989
filePath: "data_stream/skipped_tests/fields/base-fields.yml",
9090
fullFilePath: filepath.FromSlash("../../../../../test/packages/good_v2/data_stream/skipped_tests/fields/base-fields.yml"),
9191
dataStream: "skipped_tests",
9292
},
93-
fieldFileMetadata{
93+
{
9494
filePath: "data_stream/skipped_tests/fields/some_fields.yml",
9595
fullFilePath: filepath.FromSlash("../../../../../test/packages/good_v2/data_stream/skipped_tests/fields/some_fields.yml"),
9696
dataStream: "skipped_tests",
9797
},
98+
// transforms
99+
{
100+
filePath: "elasticsearch/transform/metadata_current/fields/fields.yml",
101+
fullFilePath: filepath.FromSlash("../../../../../test/packages/good_v2/elasticsearch/transform/metadata_current/fields/fields.yml"),
102+
transform: "metadata_current",
103+
},
104+
{
105+
filePath: "elasticsearch/transform/metadata_united/fields/base-fields.yml",
106+
fullFilePath: filepath.FromSlash("../../../../../test/packages/good_v2/elasticsearch/transform/metadata_united/fields/base-fields.yml"),
107+
transform: "metadata_united",
108+
},
109+
{
110+
filePath: "elasticsearch/transform/metadata_united/fields/fields.yml",
111+
fullFilePath: filepath.FromSlash("../../../../../test/packages/good_v2/elasticsearch/transform/metadata_united/fields/fields.yml"),
112+
transform: "metadata_united",
113+
},
98114
},
99115
},
100116
{
101117
pkgName: "good_input",
102118
expected: []fieldFileMetadata{
103-
fieldFileMetadata{
119+
{
104120
filePath: "fields/base-fields.yml",
105121
fullFilePath: filepath.FromSlash("../../../../../test/packages/good_input/fields/base-fields.yml"),
106122
dataStream: "",
107123
},
108-
fieldFileMetadata{
124+
{
109125
filePath: "fields/input.yml",
110126
fullFilePath: filepath.FromSlash("../../../../../test/packages/good_input/fields/input.yml"),
111127
dataStream: "",

code/go/internal/validator/semantic/validate_fields_limits.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,20 @@ func ValidateFieldsLimits(limit int) func(fspath.FS) specerrors.ValidationErrors
1818

1919
func validateFieldsLimits(fsys fspath.FS, limit int) specerrors.ValidationErrors {
2020
counts := make(map[string]int)
21+
// Created a new map to avoid collisions with data stream names
22+
transformCounts := make(map[string]int)
2123
countField := func(metadata fieldFileMetadata, f field) specerrors.ValidationErrors {
2224
if len(f.Fields) > 0 {
2325
// Don't count groups
2426
return nil
2527
}
2628

29+
if metadata.transform != "" {
30+
count := transformCounts[metadata.transform]
31+
transformCounts[metadata.transform] = count + 1
32+
return nil
33+
}
34+
2735
count := counts[metadata.dataStream]
2836
counts[metadata.dataStream] = count + 1
2937
return nil
@@ -33,11 +41,19 @@ func validateFieldsLimits(fsys fspath.FS, limit int) specerrors.ValidationErrors
3341
if err != nil {
3442
return err
3543
}
36-
3744
var errs specerrors.ValidationErrors
3845
for id, count := range counts {
3946
if count > limit {
40-
errs = append(errs, specerrors.NewStructuredErrorf("data stream %s has more than %d fields (%d)", id, limit, count))
47+
if id != "" {
48+
errs = append(errs, specerrors.NewStructuredErrorf("data stream %s has more than %d fields (%d)", id, limit, count))
49+
} else {
50+
errs = append(errs, specerrors.NewStructuredErrorf("input package has more than %d fields (%d)", limit, count))
51+
}
52+
}
53+
}
54+
for id, count := range transformCounts {
55+
if count > limit {
56+
errs = append(errs, specerrors.NewStructuredErrorf("transform %s has more than %d fields (%d)", id, limit, count))
4157
}
4258
}
4359
return errs
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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+
"path/filepath"
10+
"testing"
11+
12+
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/require"
14+
15+
"github.com/elastic/package-spec/v3/code/go/internal/fspath"
16+
)
17+
18+
func TestValidateFieldsLimits(t *testing.T) {
19+
t.Run("one data stream with too many fields", func(t *testing.T) {
20+
d := t.TempDir()
21+
22+
err := os.MkdirAll(filepath.Join(d, "data_stream", "test", "fields"), 0o755)
23+
require.NoError(t, err)
24+
err = os.MkdirAll(filepath.Join(d, "data_stream", "foo", "fields"), 0o755)
25+
require.NoError(t, err)
26+
err = os.WriteFile(filepath.Join(d, "data_stream", "foo", "fields", "fields.yml"), []byte(`
27+
- name: field1
28+
type: keyword
29+
- name: field2
30+
type: keyword
31+
- name: field3
32+
type: keyword
33+
`), 0o644)
34+
require.NoError(t, err)
35+
err = os.WriteFile(filepath.Join(d, "data_stream", "foo", "fields", "more-fields.yml"), []byte(`
36+
- name: field1
37+
type: keyword
38+
`), 0o644)
39+
require.NoError(t, err)
40+
err = os.WriteFile(filepath.Join(d, "data_stream", "test", "fields", "fields.yml"), []byte(`
41+
- name: field1
42+
type: keyword
43+
`), 0o644)
44+
require.NoError(t, err)
45+
46+
errs := validateFieldsLimits(fspath.DirFS(d), 1)
47+
require.Len(t, errs, 1)
48+
assert.EqualError(t, errs[0], "data stream foo has more than 1 fields (4)")
49+
})
50+
t.Run("one transform with too many fields", func(t *testing.T) {
51+
d := t.TempDir()
52+
53+
err := os.MkdirAll(filepath.Join(d, "elasticsearch", "transform", "foo", "fields"), 0o755)
54+
require.NoError(t, err)
55+
err = os.MkdirAll(filepath.Join(d, "elasticsearch", "transform", "bar", "fields"), 0o755)
56+
require.NoError(t, err)
57+
err = os.WriteFile(filepath.Join(d, "elasticsearch", "transform", "foo", "fields", "fields.yml"), []byte(`
58+
- name: field1
59+
type: keyword
60+
- name: field2
61+
type: keyword
62+
- name: field3
63+
type: keyword
64+
`), 0o644)
65+
require.NoError(t, err)
66+
err = os.WriteFile(filepath.Join(d, "elasticsearch", "transform", "foo", "fields", "more-fields.yml"), []byte(`
67+
- name: field1
68+
type: keyword
69+
`), 0o644)
70+
require.NoError(t, err)
71+
err = os.WriteFile(filepath.Join(d, "elasticsearch", "transform", "bar", "fields", "fields.yml"), []byte(`
72+
- name: field1
73+
type: keyword
74+
`), 0o644)
75+
require.NoError(t, err)
76+
77+
errs := validateFieldsLimits(fspath.DirFS(d), 1)
78+
require.Len(t, errs, 1)
79+
assert.EqualError(t, errs[0], "transform foo has more than 1 fields (4)")
80+
})
81+
t.Run("input package with too many fields", func(t *testing.T) {
82+
d := t.TempDir()
83+
84+
err := os.MkdirAll(filepath.Join(d, "fields"), 0o755)
85+
require.NoError(t, err)
86+
err = os.WriteFile(filepath.Join(d, "fields", "fields.yml"), []byte(`
87+
- name: field1
88+
type: keyword
89+
- name: field2
90+
type: keyword
91+
- name: field3
92+
type: keyword
93+
`), 0o644)
94+
require.NoError(t, err)
95+
err = os.WriteFile(filepath.Join(d, "fields", "more-fields.yml"), []byte(`
96+
- name: field1
97+
type: keyword
98+
`), 0o644)
99+
require.NoError(t, err)
100+
101+
errs := validateFieldsLimits(fspath.DirFS(d), 1)
102+
require.Len(t, errs, 1)
103+
assert.EqualError(t, errs[0], "input package has more than 1 fields (4)")
104+
})
105+
}

0 commit comments

Comments
 (0)