From 0b077894ef235bb95c12ae51cbcd233833ac4e6f Mon Sep 17 00:00:00 2001 From: Luiz Carvalho Date: Tue, 1 Oct 2024 12:59:29 -0400 Subject: [PATCH] Remove `validate definition` command Fixes #2031 Ref: EC-896 Signed-off-by: Luiz Carvalho --- cmd/validate/definition.go | 148 ---------- cmd/validate/definition_test.go | 269 ------------------ cmd/validate/validate.go | 2 - .../__snapshots__/validate_definition.snap | 49 ---- features/validate_definition.feature | 48 ---- internal/definition/report.go | 116 -------- internal/definition/report_test.go | 167 ----------- internal/definition/validate.go | 133 --------- internal/definition/validate_test.go | 158 ---------- 9 files changed, 1090 deletions(-) delete mode 100644 cmd/validate/definition.go delete mode 100644 cmd/validate/definition_test.go delete mode 100755 features/__snapshots__/validate_definition.snap delete mode 100644 features/validate_definition.feature delete mode 100644 internal/definition/report.go delete mode 100644 internal/definition/report_test.go delete mode 100644 internal/definition/validate.go delete mode 100644 internal/definition/validate_test.go diff --git a/cmd/validate/definition.go b/cmd/validate/definition.go deleted file mode 100644 index 5efa88030..000000000 --- a/cmd/validate/definition.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright The Enterprise Contract Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -package validate - -import ( - "context" - "errors" - - hd "github.com/MakeNowJust/heredoc" - "github.com/spf13/cobra" - - "github.com/enterprise-contract/ec-cli/internal/definition" - "github.com/enterprise-contract/ec-cli/internal/evaluator" - "github.com/enterprise-contract/ec-cli/internal/format" - "github.com/enterprise-contract/ec-cli/internal/output" - "github.com/enterprise-contract/ec-cli/internal/policy/source" - "github.com/enterprise-contract/ec-cli/internal/utils" -) - -type definitionValidationFn func(context.Context, string, []source.PolicySource, []string) (*output.Output, error) - -func validateDefinitionCmd(validate definitionValidationFn) *cobra.Command { - data := struct { - filePaths []string - policyURLs []string - dataURLs []string - output []string - namespaces []string - strict bool - }{ - filePaths: []string{}, - policyURLs: []string{"oci::quay.io/enterprise-contract/ec-pipeline-policy:latest"}, - dataURLs: []string{"git::https://github.com/enterprise-contract/ec-policies//example/data"}, - output: []string{"json"}, - namespaces: []string{}, - } - cmd := &cobra.Command{ - Use: "definition", - Short: "Validate definition file conformance with the Enterprise Contract", - - Long: hd.Doc(` - Validate definition file conformance with the Enterprise Contract - - Validate Kubernetes definition files conforms to the rego policies - defined in the given policy repository. - `), - - Example: hd.Doc(` - Validate multiple definition files via comma-separated value: - - ec validate definition --file , - - Validate multiple definition files by repeating --file: - - ec validate definition --file --file /path/to/other.file - - Specify --file as JSON - - ec validate definition --file '{"Kind": "Task"}' - - Specify different policy and data sources: - - ec validate definition --file \ - --policy git::https://github.com/enterprise-contract/ec-policies//policy/lib \ - --policy git::https://github.com/enterprise-contract/ec-policies//policy/pipeline \ - --data git::https://github.com/enterprise-contract/ec-policies//example/data - `), - - Deprecated: "please use \"ec validate input\" instead.", - RunE: func(cmd *cobra.Command, args []string) error { - var allErrors error - report := definition.NewReport() - showSuccesses, _ := cmd.Flags().GetBool("show-successes") - for i := range data.filePaths { - fpath := data.filePaths[i] - var sources []source.PolicySource - for _, url := range data.policyURLs { - sources = append(sources, &source.PolicyUrl{Url: url, Kind: source.PolicyKind}) - } - for _, url := range data.dataURLs { - sources = append(sources, &source.PolicyUrl{Url: url, Kind: source.DataKind}) - } - ctx := cmd.Context() - if out, err := validate(ctx, fpath, sources, data.namespaces); err != nil { - allErrors = errors.Join(allErrors, err) - } else { - if !showSuccesses { - for i := range out.PolicyCheck { - out.PolicyCheck[i].Successes = []evaluator.Result{} - } - } - report.Add(*out) - } - } - p := format.NewTargetParser(definition.JSONReport, format.Options{ShowSuccesses: showSuccesses}, cmd.OutOrStdout(), utils.FS(cmd.Context())) - for _, target := range data.output { - if err := report.Write(target, p); err != nil { - allErrors = errors.Join(allErrors, err) - } - } - if allErrors != nil { - return allErrors - } - if data.strict && !report.Success { - return errors.New("success criteria not met") - } - return nil - }, - } - - cmd.Flags().StringArrayVarP(&data.filePaths, "file", "f", data.filePaths, - "path to definition YAML/JSON file (required)") - - cmd.Flags().StringSliceVar(&data.policyURLs, "policy", data.policyURLs, - "url for policies, go-getter style. May be used multiple times") - - cmd.Flags().StringSliceVar(&data.dataURLs, "data", data.dataURLs, - "url for policy data, go-getter style. May be used multiple times") - - cmd.Flags().StringSliceVarP(&data.output, "output", "o", data.output, hd.Doc(` - write output to a file in a specific format, e.g. yaml=/tmp/output.yaml. Use empty string - path for stdout, e.g. yaml. May be used multiple times. Possible formats are json and yaml - `)) - cmd.Flags().StringSliceVar(&data.namespaces, "namespace", data.namespaces, - "the namespace containing the policy to run. May be used multiple times") - cmd.Flags().BoolVarP(&data.strict, "strict", "s", data.strict, - "return non-zero status on non-successful validation") - - if err := cmd.MarkFlagRequired("file"); err != nil { - panic(err) - } - - return cmd -} diff --git a/cmd/validate/definition_test.go b/cmd/validate/definition_test.go deleted file mode 100644 index 5cba8cc75..000000000 --- a/cmd/validate/definition_test.go +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright The Enterprise Contract Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -//go:build unit - -package validate - -import ( - "bytes" - "context" - "errors" - "fmt" - "strings" - "testing" - - hd "github.com/MakeNowJust/heredoc" - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - - "github.com/enterprise-contract/ec-cli/internal/evaluator" - output2 "github.com/enterprise-contract/ec-cli/internal/output" - "github.com/enterprise-contract/ec-cli/internal/policy/source" - "github.com/enterprise-contract/ec-cli/internal/utils" -) - -func TestValidateDefinitionFileCommandOutput(t *testing.T) { - validate := func(_ context.Context, fpath string, _ []source.PolicySource, _ []string) (*output2.Output, error) { - return &output2.Output{PolicyCheck: []evaluator.Outcome{{FileName: fpath}}}, nil - } - - validateDefinitionCmd := validateDefinitionCmd(validate) - cmd := setUpCobra(validateDefinitionCmd) - - var out bytes.Buffer - cmd.SetOut(&out) - - cmd.SetArgs([]string{ - "validate", - "definition", - "--file", - "/path/file1.yaml", - "--file", - "/path/file2.yaml", - }) - - err := cmd.Execute() - assert.NoError(t, err) - - assert.JSONEq(t, `{"definitions": [ - { - "filename": "/path/file1.yaml", - "successes": [], - "violations": [], - "warnings": [] - }, - { - "filename": "/path/file2.yaml", - "successes": [], - "violations": [], - "warnings": [] - } - ], - "success": true, - "ec-version": "development" - }`, strings.Split(out.String(), "\n")[1]) -} - -func TestValidateDefinitionFilePolicySources(t *testing.T) { - expected := []source.PolicySource{ - &source.PolicyUrl{Url: "spam-policy-source", Kind: source.PolicyKind}, - &source.PolicyUrl{Url: "ham-policy-source", Kind: source.PolicyKind}, - &source.PolicyUrl{Url: "bacon-data-source", Kind: source.DataKind}, - &source.PolicyUrl{Url: "eggs-data-source", Kind: source.DataKind}, - } - validate := func(_ context.Context, fpath string, sources []source.PolicySource, _ []string) (*output2.Output, error) { - assert.Equal(t, expected, sources) - return &output2.Output{}, nil - } - - validateDefinitionCmd := validateDefinitionCmd(validate) - cmd := setUpCobra(validateDefinitionCmd) - - var out bytes.Buffer - cmd.SetOut(&out) - - cmd.SetArgs([]string{ - "validate", - "definition", - "--file", - "/path/file1.yaml", - "--policy", - "spam-policy-source", - "--policy", - "ham-policy-source", - "--data", - "bacon-data-source", - "--data", - "eggs-data-source", - }) - - err := cmd.Execute() - assert.NoError(t, err) -} - -func TestDefinitionFileOutputFormats(t *testing.T) { - testJSONText := `{"definitions":[{"filename":"/path/file1.yaml","violations":[],"warnings":[],"successes":[]}],"success":true,"ec-version":"development"}` - - testYAMLTest := hd.Doc(` - definitions: - - filename: /path/file1.yaml - successes: [] - violations: [] - warnings: [] - ec-version: development - success: true - `) - - cases := []struct { - name string - output []string - expectedFiles map[string]string - expectedStdout string - }{ - { - name: "default output", - expectedStdout: fmt.Sprintf("Command \"definition\" is deprecated, please use \"ec validate input\" instead.\n%s", testJSONText), - }, - { - name: "json stdout", - output: []string{"--output", "json"}, - expectedStdout: fmt.Sprintf("Command \"definition\" is deprecated, please use \"ec validate input\" instead.\n%s", testJSONText), - }, - { - name: "yaml stdout", - output: []string{"--output", "yaml"}, - expectedStdout: fmt.Sprintf("Command \"definition\" is deprecated, please use \"ec validate input\" instead.\n%s", testYAMLTest), - }, - { - name: "json and yaml to file", - output: []string{"--output", "json=out.json", "--output", "yaml=out.yaml"}, - expectedStdout: "Command \"definition\" is deprecated, please use \"ec validate input\" instead.\n", - expectedFiles: map[string]string{ - "out.json": testJSONText, - "out.yaml": testYAMLTest, - }, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - fs := afero.NewMemMapFs() - validate := func(_ context.Context, fpath string, sources []source.PolicySource, _ []string) (*output2.Output, error) { - return &output2.Output{PolicyCheck: []evaluator.Outcome{{FileName: fpath}}}, nil - } - - validateDefinitionCmd := validateDefinitionCmd(validate) - cmd := setUpCobra(validateDefinitionCmd) - - var out bytes.Buffer - cmd.SetOut(&out) - - cmd.SetArgs(append([]string{ - "validate", - "definition", - "--file", - "/path/file1.yaml", - }, c.output...)) - - cmd.SetContext(utils.WithFS(context.Background(), fs)) - - err := cmd.Execute() - assert.NoError(t, err) - assert.Equal(t, c.expectedStdout, out.String()) - - for name, expectedText := range c.expectedFiles { - actualText, err := afero.ReadFile(fs, name) - assert.NoError(t, err) - assert.Equal(t, expectedText, string(actualText)) - } - }) - } -} - -func TestValidateDefinitionFileCommandErrors(t *testing.T) { - validate := func(_ context.Context, fpath string, _ []source.PolicySource, _ []string) (*output2.Output, error) { - return nil, errors.New(fpath) - } - - validateDefinitionCmd := validateDefinitionCmd(validate) - cmd := setUpCobra(validateDefinitionCmd) - - var out bytes.Buffer - cmd.SetOut(&out) - cmd.SilenceUsage = true - - cmd.SetArgs([]string{ - "validate", - "definition", - "--file", - "/path/file1.yaml", - "--file", - "/path/file2.yaml", - }) - - err := cmd.Execute() - assert.Error(t, err, "/path/file1.yaml\n/path/file2.yaml\n") - assert.Equal(t, "Command \"definition\" is deprecated, please use \"ec validate input\" instead.\n", out.String()) -} - -func TestStrictOutput(t *testing.T) { - validate := func(_ context.Context, fpath string, _ []source.PolicySource, _ []string) (*output2.Output, error) { - failureResult := evaluator.Outcome{ - FileName: fpath, - Failures: []evaluator.Result{ - { - Message: "failure", - }, - }, - } - return &output2.Output{PolicyCheck: []evaluator.Outcome{failureResult}}, nil - } - - cases := []struct { - name string - args []string - expectedError error - }{ - { - name: "hide strict output", - args: []string{ - "--file", - "/path/file1.yaml", - }, - expectedError: nil, - }, - { - name: "show strict output", - args: []string{ - "--file", - "/path/file1.yaml", - "--strict", - }, - expectedError: errors.New("success criteria not met"), - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - validateDefinitionCmd := validateDefinitionCmd(validate) - cmd := setUpCobra(validateDefinitionCmd) - cmd.SetArgs(append([]string{"validate", "definition"}, c.args...)) - err := cmd.Execute() - assert.Equal(t, c.expectedError, err) - }) - } -} diff --git a/cmd/validate/validate.go b/cmd/validate/validate.go index 4f32b61c1..28f2cdd2c 100644 --- a/cmd/validate/validate.go +++ b/cmd/validate/validate.go @@ -19,7 +19,6 @@ package validate import ( "github.com/spf13/cobra" - "github.com/enterprise-contract/ec-cli/internal/definition" "github.com/enterprise-contract/ec-cli/internal/image" "github.com/enterprise-contract/ec-cli/internal/input" "github.com/enterprise-contract/ec-cli/internal/policy" @@ -34,7 +33,6 @@ func init() { func init() { ValidateCmd.AddCommand(validateImageCmd(image.ValidateImage)) - ValidateCmd.AddCommand(validateDefinitionCmd(definition.ValidateDefinition)) ValidateCmd.AddCommand(validateInputCmd(input.ValidateInput)) ValidateCmd.AddCommand(ValidatePolicyCmd(policy.ValidatePolicy)) } diff --git a/features/__snapshots__/validate_definition.snap b/features/__snapshots__/validate_definition.snap deleted file mode 100755 index 15390df3d..000000000 --- a/features/__snapshots__/validate_definition.snap +++ /dev/null @@ -1,49 +0,0 @@ - -[:stdout - 1] -{ - "definitions": [ - { - "filename": "pipeline_definition.yaml", - "violations": [], - "warnings": [], - "successes": [] - } - ], - "success": true, - "ec-version": "${EC_VERSION}" -} ---- - -[:stderr - 1] -Command "definition" is deprecated, please use "ec validate input" instead. - ---- - -[Showing successes:stdout - 1] -{ - "definitions": [ - { - "filename": "pipeline_definition.yaml", - "violations": [], - "warnings": [], - "successes": [ - { - "msg": "Pass", - "metadata": { - "code": "main.expected_kind", - "description": "Check that the pipeline is a kind of \"Pipeline\"", - "title": "Pipeline kind is expected" - } - } - ] - } - ], - "success": true, - "ec-version": "${EC_VERSION}" -} ---- - -[Showing successes:stderr - 1] -Command "definition" is deprecated, please use "ec validate input" instead. - ---- diff --git a/features/validate_definition.feature b/features/validate_definition.feature deleted file mode 100644 index 992f7f36e..000000000 --- a/features/validate_definition.feature +++ /dev/null @@ -1,48 +0,0 @@ -Feature: validate pipeline definition - The ec command line can validate pipeline definitions - - Background: - Given a stub cluster running - Given stub git daemon running - - Scenario: - Given a git repository named "happy-day-policy" with - | main.rego | examples/pipeline_basic.rego | - Given a pipeline definition file named "pipeline_definition.yaml" containing - """ - --- - apiVersion: tekton.dev/v1 - kind: Pipeline - metadata: - name: basic-build - spec: - tasks: - - name: appstudio-init - taskRef: - name: init - version: "0.1" - """ - When ec command is run with "validate definition --file pipeline_definition.yaml --policy git::https://${GITHOST}/git/happy-day-policy.git" - Then the exit status should be 0 - Then the output should match the snapshot - - Scenario: Showing successes - Given a git repository named "happy-day-policy" with - | main.rego | examples/pipeline_basic.rego | - Given a pipeline definition file named "pipeline_definition.yaml" containing - """ - --- - apiVersion: tekton.dev/v1 - kind: Pipeline - metadata: - name: basic-build - spec: - tasks: - - name: appstudio-init - taskRef: - name: init - version: "0.1" - """ - When ec command is run with "validate definition --file pipeline_definition.yaml --policy git::https://${GITHOST}/git/happy-day-policy.git --show-successes" - Then the exit status should be 0 - Then the output should match the snapshot diff --git a/internal/definition/report.go b/internal/definition/report.go deleted file mode 100644 index e162b5d0c..000000000 --- a/internal/definition/report.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright The Enterprise Contract Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.` -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -package definition - -import ( - "encoding/json" - "fmt" - - "sigs.k8s.io/yaml" - - "github.com/enterprise-contract/ec-cli/internal/evaluator" - "github.com/enterprise-contract/ec-cli/internal/format" - "github.com/enterprise-contract/ec-cli/internal/output" - "github.com/enterprise-contract/ec-cli/internal/version" -) - -type ReportItem struct { - Filename string `json:"filename"` - Violations []evaluator.Result `json:"violations"` - Warnings []evaluator.Result `json:"warnings"` - Successes []evaluator.Result `json:"successes"` -} - -type ReportFormat string - -const ( - JSONReport string = "json" - YAMLReport string = "yaml" -) - -type Report struct { - Definitions []ReportItem `json:"definitions"` - Success bool `json:"success"` - EcVersion string `json:"ec-version"` -} - -func NewReport() Report { - info, _ := version.ComputeInfo() - return Report{ - Success: true, - EcVersion: info.Version, - } -} - -func (r *Report) Add(o output.Output) { - // group the results by filename. If multiple files are passed - // to the testRunner, conftest evaluates all files against each namespace. - itemsByFile := make(map[string]ReportItem) - - // check contains results from each namespace evaluated per definition - for _, check := range o.PolicyCheck { - if _, ok := itemsByFile[check.FileName]; !ok { - itemsByFile[check.FileName] = ReportItem{ - Violations: []evaluator.Result{}, - Warnings: []evaluator.Result{}, - Successes: []evaluator.Result{}, - } - } - item := itemsByFile[check.FileName] - item.Violations = append(item.Violations, check.Failures...) - item.Warnings = append(item.Warnings, check.Warnings...) - item.Successes = append(item.Successes, check.Successes...) - item.Filename = check.FileName - itemsByFile[check.FileName] = item - } - - for _, value := range itemsByFile { - if len(value.Violations) > 0 { - // set Report.Success to false if any violations - r.Success = false - } - r.Definitions = append(r.Definitions, value) - } -} - -func (r *Report) Write(targetName string, p format.TargetParser) error { - if len(r.Definitions) == 0 { - return nil - } - - target, err := p.Parse(targetName) - if err != nil { - return err - } - - var data []byte - switch target.Format { - case JSONReport: - if data, err = json.Marshal(r); err != nil { - return err - } - case YAMLReport: - if data, err = yaml.Marshal(r); err != nil { - return err - } - default: - return fmt.Errorf("unexpected report format: %s", target.Format) - } - - _, err = target.Write(data) - return err -} diff --git a/internal/definition/report_test.go b/internal/definition/report_test.go deleted file mode 100644 index 25421ede4..000000000 --- a/internal/definition/report_test.go +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright The Enterprise Contract Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.` -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -//go:build unit - -package definition - -import ( - "testing" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - - "github.com/enterprise-contract/ec-cli/internal/evaluator" - "github.com/enterprise-contract/ec-cli/internal/format" - "github.com/enterprise-contract/ec-cli/internal/output" -) - -func TestReport(t *testing.T) { - cases := []struct { - name string - output []output.Output - expect string - skipped bool - }{ - { - name: "success", - output: []output.Output{ - {PolicyCheck: []evaluator.Outcome{ - {FileName: "/path/to/pipeline.json"}, - }}, - }, - expect: `{"definitions": [{ - "filename": "/path/to/pipeline.json", - "violations": [], - "warnings": [], - "successes": [], - }], - "ec-version": "development", - "success": true - }`, - }, - { - name: "warnings", - output: []output.Output{ - { - PolicyCheck: []evaluator.Outcome{ - { - FileName: "/path/to/pipeline.json", - Warnings: []evaluator.Result{ - {Message: "running low in spam"}, - {Message: "not all like spam"}, - }, - }, - }, - }, - }, - expect: `{"definitions": [{ - "filename": "/path/to/pipeline.json", - "violations": [], - "warnings": [{"msg": "running low in spam"},{"msg": "not all like spam"}], - "successes": [], - }], - "ec-version": "development", - "success": true - }`, - }, - { - name: "violations", - output: []output.Output{ - { - PolicyCheck: []evaluator.Outcome{ - { - FileName: "/path/to/pipeline.json", - Failures: []evaluator.Result{ - {Message: "out of spam!"}, - {Message: "spam 💔"}, - }, - }, - }, - }, - }, - expect: `{"definitions": [{ - "filename": "/path/to/pipeline.json", - "violations": [{"msg": "out of spam!"},{"msg": "spam 💔"}], - "warnings": [], - "successes": [], - }], - "ec-version": "development", - "success": false - }`, - }, - { - name: "successes", - output: []output.Output{ - { - PolicyCheck: []evaluator.Outcome{ - { - FileName: "/path/to/pipeline.json", - Successes: []evaluator.Result{ - {Message: "Nice"}, - {Message: "Day"}, - }, - }, - }, - }, - }, - expect: `{"definitions": [{ - "filename": "/path/to/pipeline.json", - "violations": [], - "warnings": [], - "successes": [{"msg": "Nice"},{"msg": "Day"}], - }], - "ec-version": "development", - "success": true - }`, - }, - { - name: "empty output", - output: []output.Output{}, - skipped: true, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - r := NewReport() - for _, o := range c.output { - r.Add(o) - } - fs := afero.NewMemMapFs() - parser := format.NewTargetParser("ignored", format.Options{}, nil, fs) - - for _, format := range []string{"json", "yaml"} { - fname := "out." + format - target := format + "=" + fname - err := r.Write(target, parser) - assert.NoError(t, err) - - if c.skipped { - exists, err := afero.Exists(fs, fname) - assert.NoError(t, err) - assert.False(t, exists) - - } else { - - actualText, err := afero.ReadFile(fs, fname) - assert.NoError(t, err) - assert.YAMLEq(t, c.expect, string(actualText)) - } - } - }) - } -} diff --git a/internal/definition/validate.go b/internal/definition/validate.go deleted file mode 100644 index 85c6c25e2..000000000 --- a/internal/definition/validate.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright The Enterprise Contract Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.` -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -package definition - -import ( - "context" - "fmt" - "path/filepath" - - log "github.com/sirupsen/logrus" - "github.com/spf13/afero" - - "github.com/enterprise-contract/ec-cli/internal/evaluation_target/definition" - "github.com/enterprise-contract/ec-cli/internal/evaluator" - "github.com/enterprise-contract/ec-cli/internal/output" - "github.com/enterprise-contract/ec-cli/internal/policy/source" - "github.com/enterprise-contract/ec-cli/internal/utils" -) - -var definitionFile = definition.NewDefinition - -// ValidatePipeline calls NewPipelineEvaluator to obtain an PipelineEvaluator. It then executes the associated TestRunner -// which tests the associated pipeline file(s) against the associated policies, and displays the output. -func ValidateDefinition(ctx context.Context, fpath string, sources []source.PolicySource, namespace []string) (*output.Output, error) { - defFiles, err := detectInput(ctx, fpath) - if err != nil { - return nil, err - } - p, err := definitionFile(ctx, defFiles, sources, namespace) - if err != nil { - log.Debug("Failed to create definition file!") - return nil, err - } - - results, _, err := p.Evaluator.Evaluate(ctx, evaluator.EvaluationTarget{Inputs: defFiles}) - if err != nil { - log.Debug("Problem running conftest policy check!") - return nil, err - } - log.Debug("Conftest policy check complete") - return &output.Output{PolicyCheck: results}, nil -} - -// detect if a file or directory was passed. if a directory, gather all files in it -// the order is file lookup, json lookup then yaml -func detectInput(ctx context.Context, fpath string) ([]string, error) { - if utils.IsJson(fpath) { - log.Debug("valid JSON found for definition file") - return definitionFromString(ctx, fpath) - } - log.Debug("unable to detect input as JSON") - - // this is narrowed down to map[string]interface{} - // since a provided filename that does not exist could be considered valid yaml - if utils.IsYamlMap(fpath) { - log.Debug("valid YAML map found for definition file") - return definitionFromString(ctx, fpath) - } - log.Debug("unable to detect input as YAML") - - fileExists, err := utils.IsFile(ctx, fpath) - if err != nil { - return nil, err - } - - if fileExists { - return fileLookup(ctx, fpath) - } - log.Debugf("unable to detect a file at path %v", fpath) - - return nil, fmt.Errorf("unable to parse the provided definition file: %v", fpath) -} - -// if a single file is provided, return it -// if the file is a directory, return the files inside the directory -func fileLookup(ctx context.Context, path string) ([]string, error) { - fs := utils.FS(ctx) - var defFiles []string - - file, err := fs.Open(path) - if err != nil { - return nil, err - } - - defer file.Close() - - dir, err := afero.IsDir(fs, path) - if err != nil { - return nil, err - } - - if dir { - files, err := afero.ReadDir(fs, path) - if err != nil { - return nil, err - } - // a directory was provided, but contained no files - if len(files) == 0 { - return nil, fmt.Errorf("the directory %v contained no files", path) - } - - for _, f := range files { - defFiles = append(defFiles, filepath.Join(path, f.Name())) - } - } else { - defFiles = append(defFiles, path) - } - - return defFiles, nil -} - -// write the definition file if a json or yaml string is provided -func definitionFromString(ctx context.Context, data string) ([]string, error) { - data, err := utils.WriteTempFile(ctx, data, "definition-file-") - if err != nil { - return nil, err - } - return []string{data}, nil -} diff --git a/internal/definition/validate_test.go b/internal/definition/validate_test.go deleted file mode 100644 index c0e871000..000000000 --- a/internal/definition/validate_test.go +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright The Enterprise Contract Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.` -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -//go:build unit - -package definition - -import ( - "context" - "errors" - "fmt" - "path/filepath" - "testing" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - - "github.com/enterprise-contract/ec-cli/internal/evaluation_target/definition" - "github.com/enterprise-contract/ec-cli/internal/evaluator" - "github.com/enterprise-contract/ec-cli/internal/output" - "github.com/enterprise-contract/ec-cli/internal/policy/source" - "github.com/enterprise-contract/ec-cli/internal/utils" -) - -type ( - mockEvaluator struct{} - badMockEvaluator struct{} -) - -func (e mockEvaluator) Evaluate(ctx context.Context, target evaluator.EvaluationTarget) ([]evaluator.Outcome, evaluator.Data, error) { - return []evaluator.Outcome{}, nil, nil -} - -func (e mockEvaluator) Destroy() { -} - -func (e mockEvaluator) CapabilitiesPath() string { - return "" -} - -func (b badMockEvaluator) Evaluate(ctx context.Context, target evaluator.EvaluationTarget) ([]evaluator.Outcome, evaluator.Data, error) { - return nil, nil, errors.New("Evaluator error") -} - -func (e badMockEvaluator) Destroy() { -} - -func (e badMockEvaluator) CapabilitiesPath() string { - return "" -} - -func mockNewPipelineDefinitionFile(ctx context.Context, fpath []string, sources []source.PolicySource, namespace []string) (*definition.Definition, error) { - return &definition.Definition{ - Evaluator: mockEvaluator{}, - }, nil -} - -func badMockNewPipelineDefinitionFile(ctx context.Context, fpath []string, sources []source.PolicySource, namespace []string) (*definition.Definition, error) { - return &definition.Definition{ - Evaluator: badMockEvaluator{}, - }, nil -} - -func Test_ValidatePipeline(t *testing.T) { - emptyDir := "/empty" - nonEmptyDir := "/nonEmpty" - validFile := filepath.Join(nonEmptyDir, "file.json") - badPath := "bad" - - tests := []struct { - name string - fpath string - err error - output *output.Output - defFunc func(ctx context.Context, fpath []string, sources []source.PolicySource, namespace []string) (*definition.Definition, error) - }{ - { - name: "validation succeeds", - fpath: validFile, - err: nil, - output: &output.Output{PolicyCheck: []evaluator.Outcome{}}, - defFunc: mockNewPipelineDefinitionFile, - }, - { - name: "validation fails on empty directory", - fpath: emptyDir, - err: fmt.Errorf("the directory %v contained no files", emptyDir), - output: nil, - defFunc: mockNewPipelineDefinitionFile, - }, - { - name: "validation fails on bad path", - fpath: badPath, - err: fmt.Errorf("unable to parse the provided definition file: %v", badPath), - output: nil, - defFunc: mockNewPipelineDefinitionFile, - }, - { - name: "valid file, but evaluator fails", - fpath: validFile, - err: errors.New("Evaluator error"), - output: nil, - defFunc: badMockNewPipelineDefinitionFile, - }, - { - name: "validation succeeds with json input", - fpath: "{\"json\": 1}", - err: nil, - output: &output.Output{PolicyCheck: []evaluator.Outcome{}}, - defFunc: mockNewPipelineDefinitionFile, - }, - { - name: "validation succeeds with yaml input", - fpath: "kind: task", - err: nil, - output: &output.Output{PolicyCheck: []evaluator.Outcome{}}, - defFunc: mockNewPipelineDefinitionFile, - }, - { - name: "validation fails with only an array of strings as yaml", - fpath: "- test1\n- test2", - err: fmt.Errorf("unable to parse the provided definition file: %v", "- test1\n- test2"), - output: nil, - defFunc: mockNewPipelineDefinitionFile, - }, - } - - appFS := afero.NewMemMapFs() - errEmptyDir := appFS.MkdirAll(emptyDir, 0777) - assert.NoError(t, errEmptyDir) - errDir := appFS.MkdirAll(nonEmptyDir, 0777) - assert.NoError(t, errDir) - errFile := afero.WriteFile(appFS, validFile, []byte("data"), 0777) - assert.NoError(t, errFile) - ctx := utils.WithFS(context.Background(), appFS) - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - definitionFile = tt.defFunc - output, err := ValidateDefinition(ctx, tt.fpath, []source.PolicySource{}, []string{}) - assert.Equal(t, tt.err, err) - assert.Equal(t, tt.output, output) - }) - } -}