diff --git a/cmd/validate/image.go b/cmd/validate/image.go index e3aa3504e..0653e0309 100644 --- a/cmd/validate/image.go +++ b/cmd/validate/image.go @@ -45,6 +45,7 @@ import ( type imageValidationFunc func(context.Context, app.SnapshotComponent, *app.SnapshotSpec, policy.Policy, []evaluator.Evaluator, bool) (*output.Output, error) var newConftestEvaluator = evaluator.NewConftestEvaluator +var newOPAEvaluator = evaluator.NewOPAEvaluator func validateImageCmd(validate imageValidationFunc) *cobra.Command { data := struct { @@ -318,7 +319,14 @@ func validateImageCmd(validate imageValidationFunc) *cobra.Command { log.Debugf("policySource: %#v", policySource) } - c, err := newConftestEvaluator(cmd.Context(), policySources, data.policy, sourceGroup) + var c evaluator.Evaluator + var err error + if utils.IsOpaEnabled() { + c, err = newOPAEvaluator() + } else { + c, err = newConftestEvaluator(cmd.Context(), policySources, data.policy, sourceGroup) + } + if err != nil { log.Debug("Failed to initialize the conftest evaluator!") return err diff --git a/internal/evaluator/opa_evaluator.go b/internal/evaluator/opa_evaluator.go new file mode 100644 index 000000000..37525c761 --- /dev/null +++ b/internal/evaluator/opa_evaluator.go @@ -0,0 +1,49 @@ +// 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 evaluator + +import ( + "context" + "os" + "path" + + "github.com/spf13/afero" +) + +// not sure what the properties will be yet, so setting the minimum. +type opaEvaluator struct { + workDir string + fs afero.Fs +} + +func NewOPAEvaluator() (Evaluator, error) { + return opaEvaluator{}, nil +} + +func (o opaEvaluator) Evaluate(ctx context.Context, target EvaluationTarget) ([]Outcome, Data, error) { + return []Outcome{}, Data{}, nil +} + +func (o opaEvaluator) Destroy() { + if o.workDir != "" && os.Getenv("EC_DEBUG") == "" { + _ = o.fs.RemoveAll(o.workDir) + } +} + +func (o opaEvaluator) CapabilitiesPath() string { + return path.Join(o.workDir, "capabilities.json") +} diff --git a/internal/evaluator/opa_evaluator_test.go b/internal/evaluator/opa_evaluator_test.go new file mode 100644 index 000000000..f939d3dd8 --- /dev/null +++ b/internal/evaluator/opa_evaluator_test.go @@ -0,0 +1,115 @@ +// 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 evaluator + +import ( + "context" + "os" + "testing" + + "github.com/spf13/afero" + "github.com/stretchr/testify/assert" +) + +// TestNewOPAEvaluator tests the constructor NewOPAEvaluator. +func TestNewOPAEvaluator(t *testing.T) { + evaluator, err := NewOPAEvaluator() + assert.NoError(t, err, "Expected no error from NewOPAEvaluator") + assert.Equal(t, evaluator, opaEvaluator{}) +} + +func TestEvaluate(t *testing.T) { + opaEval := opaEvaluator{} + + outcomes, data, err := opaEval.Evaluate(context.Background(), EvaluationTarget{}) + assert.NoError(t, err, "Expected no error from Evaluate") + assert.Equal(t, []Outcome{}, outcomes) + assert.Equal(t, data, Data{}) +} + +// Test Destroy method of opaEvaluator. +func TestDestroy(t *testing.T) { + // Setup an in-memory filesystem + fs := afero.NewMemMapFs() + workDir := "/tmp/workdir" + + // Define test cases + testCases := []struct { + name string + workDir string + EC_DEBUG bool + expectRemove bool + }{ + { + name: "Empty workDir, EC_DEBUG not set", + workDir: "", + EC_DEBUG: false, + expectRemove: false, + }, + { + name: "Non-empty workDir, EC_DEBUG not set", + workDir: workDir, + EC_DEBUG: false, + expectRemove: true, + }, + { + name: "Non-empty workDir, EC_DEBUG set", + workDir: workDir, + EC_DEBUG: true, + expectRemove: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Set up the environment + if tc.workDir != "" { + err := fs.MkdirAll(tc.workDir, 0755) + assert.NoError(t, err, "Failed to create workDir in in-memory filesystem") + } + + if tc.EC_DEBUG { + os.Setenv("EC_DEBUG", "true") + } else { + os.Unsetenv("EC_DEBUG") + } + + // Initialize the evaluator + opaEval := opaEvaluator{ + workDir: tc.workDir, + fs: fs, + } + + // Call Destroy + opaEval.Destroy() + + // Verify the result + exists, err := afero.DirExists(fs, tc.workDir) + assert.NoError(t, err, "Error checking if workDir exists after Destroy") + + if tc.expectRemove { + assert.False(t, exists, "workDir should be removed") + } else { + assert.True(t, exists, "workDir should not be removed") + } + + // Clean up for next test + _ = fs.RemoveAll(tc.workDir) + os.Unsetenv("EC_DEBUG") + }) + } +} diff --git a/internal/utils/helpers.go b/internal/utils/helpers.go index d6cc5f46c..5d778f831 100644 --- a/internal/utils/helpers.go +++ b/internal/utils/helpers.go @@ -98,6 +98,10 @@ func Experimental() bool { return os.Getenv("EC_EXPERIMENTAL") == "1" } +func IsOpaEnabled() bool { + return os.Getenv("EC_USE_OPA") == "1" +} + // detect if the string is json func IsJson(data string) bool { var jsMsg json.RawMessage