Skip to content

Commit

Permalink
Merge pull request #2252 from joejstuart/opa-eval
Browse files Browse the repository at this point in the history
Allow choosing OPA evaluator
  • Loading branch information
joejstuart authored Jan 10, 2025
2 parents 9d4f0a8 + 9eb4a60 commit 7f35564
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 1 deletion.
10 changes: 9 additions & 1 deletion cmd/validate/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
49 changes: 49 additions & 0 deletions internal/evaluator/opa_evaluator.go
Original file line number Diff line number Diff line change
@@ -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")
}
115 changes: 115 additions & 0 deletions internal/evaluator/opa_evaluator_test.go
Original file line number Diff line number Diff line change
@@ -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")
})
}
}
4 changes: 4 additions & 0 deletions internal/utils/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 7f35564

Please sign in to comment.