diff --git a/docs/reference/domains/file-domain.md b/docs/reference/domains/file-domain.md index ef0738fb..0cb1ef03 100644 --- a/docs/reference/domains/file-domain.md +++ b/docs/reference/domains/file-domain.md @@ -2,7 +2,7 @@ The File domain allows for validation of arbitrary file contents. The file domain can evaluate local files and network files. Files are copied to a temporary directory for evaluation and deleted afterwards. ## Specification -The File domain specification accepts a descriptive name for the file as well as it's path: +The File domain specification accepts a descriptive name for the file as well as it's path. The names must be unique. ```yaml domain: @@ -14,7 +14,7 @@ domain: ``` ## Supported File Types -The file domain use's OPA's [conftest](https://conftest.dev) to parse files into a json-compatible format for validations. Both OPA and kyverno (using [kyverno-json](https://kyverno.github.io/kyverno-json/latest/)) can validate files parsed by the file domain. +The file domain use's OPA's [conftest](https://conftest.dev) to parse files into a json-compatible format for validations. ∑Both OPA and kyverno (using [kyverno-json](https://kyverno.github.io/kyverno-json/latest/)) can validate files parsed by the file domain. The file domain supports the following file formats for validation: * CUE @@ -34,4 +34,42 @@ The file domain supports the following file formats for validation: * TOML * VCL * XML -* YAML \ No newline at end of file +* YAML + +## Validations +When writing validations against files, the filepath Name must be included as the top-level key in the validation, in this example below `check`: + +```yaml +metadata: + name: check-grafana-protocol + uuid: ad38ef57-99f6-4ac6-862e-e0bc9f55eebe +domain: + type: file + file-spec: + filepaths: + - name: 'grafana' + path: 'custom.ini' +provider: + type: kyverno + kyverno-spec: + policy: + apiVersion: json.kyverno.io/v1alpha1 + kind: ValidatingPolicy + metadata: + name: grafana-config + spec: + rules: + - name: protocol-is-https + assert: + all: + - check: + grafana: + server: + protocol: https +``` + +```grafana.ini +[server] +# Protocol (http, https, socket) +protocol = http +``` \ No newline at end of file diff --git a/src/pkg/domains/files/files.go b/src/pkg/domains/files/files.go index f76578ef..71618e78 100644 --- a/src/pkg/domains/files/files.go +++ b/src/pkg/domains/files/files.go @@ -28,13 +28,25 @@ func (d Domain) GetResources() (types.DomainResources, error) { // removed. defer os.RemoveAll(dst) + // make a map of rel filepaths to the user-supplied name, so we can re-key the DomainResources later on. + filenames := make(map[string]string, len(d.Spec.Filepaths)) + // Copy files to a temporary location for _, path := range d.Spec.Filepaths { bytes, err := network.Fetch(path.Path) if err != nil { return nil, fmt.Errorf("error getting source files: %w", err) } - os.WriteFile(filepath.Join(dst, path.Name), bytes, 0666) + + // We'll just use the filename when writing the file so it's easier to reference later + relname := filepath.Base(path.Path) + + err = os.WriteFile(filepath.Join(dst, relname), bytes, 0666) + if err != nil { + return nil, fmt.Errorf("error writing local files: %w", err) + } + // and save this info for later + filenames[relname] = path.Name } // get a list of all the files we just downloaded in the temporary directory @@ -56,14 +68,15 @@ func (d Domain) GetResources() (types.DomainResources, error) { return nil, err } - // clean up the resources so it's just using the filename + // clean up the resources so it's using the filepath.Name as the map key, + // istead of the file path.src/pkg/domains/files/files.go drs := make(types.DomainResources, len(config)) for k, v := range config { rel, err := filepath.Rel(dst, k) if err != nil { return nil, fmt.Errorf("error determining relative file path: %w", err) } - drs[rel] = v + drs[filenames[rel]] = v } return drs, nil } diff --git a/src/pkg/domains/files/files_test.go b/src/pkg/domains/files/files_test.go index 6ec115e0..1cff7f92 100644 --- a/src/pkg/domains/files/files_test.go +++ b/src/pkg/domains/files/files_test.go @@ -15,15 +15,19 @@ func TestGetResource(t *testing.T) { d := Domain{Spec: &Spec{Filepaths: []FileInfo{ {Name: "foo.yaml", Path: "testdata/foo.yaml"}, {Name: "bar.json", Path: "testdata/bar.json"}, + {Name: "arbitraryname", Path: "testdata/nested-directory/baz.hcl2"}, }}} resources, err := d.GetResources() require.NoError(t, err) - if diff := cmp.Diff(resources, types.DomainResources{"bar.json": map[string]interface{}{"cat": "Cheetarah"}, "foo.yaml": "cat = Li Shou"}); diff != "" { + if diff := cmp.Diff(resources, types.DomainResources{ + "bar.json": map[string]interface{}{"cat": "Cheetarah"}, + "foo.yaml": "cat = Li Shou", + "arbitraryname": map[string]any{ + "resource": map[string]any{"catname": map[string]any{"blackcat": map[string]any{"name": "robin"}}}, + }, + }); diff != "" { t.Fatalf("wrong result:\n%s\n", diff) } }) - - //remote files - // TODO } diff --git a/src/pkg/domains/files/testdata/nested-directory/baz.hcl2 b/src/pkg/domains/files/testdata/nested-directory/baz.hcl2 new file mode 100644 index 00000000..0635204e --- /dev/null +++ b/src/pkg/domains/files/testdata/nested-directory/baz.hcl2 @@ -0,0 +1,3 @@ +resource "catname" "blackcat" { + name = "robin" +} \ No newline at end of file