Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions acceptance/examples/embedded_rego_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
sources:
- policy:
- "git::https://${GITHOST}/git/embedded-rego-policy.git"
16 changes: 16 additions & 0 deletions acceptance/examples/embedded_rego_test.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package main

import data.ec_lib

# METADATA
# custom:
# short_name: embedded_test
deny contains result if {
# Always deny
true

# We're expecting this to be defined in the "embedded" rego
msg := ec_lib.hello_world("testy mctest")

result := {"code": "main.embedded_test", "msg": msg}
}
20 changes: 20 additions & 0 deletions features/__snapshots__/embedded_rego.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

[policy using embedded rego functions:stdout - 1]
Success: false
Result: FAILURE
Violations: 1, Warnings: 0, Successes: 0
Input File: pipeline_definition.json

Results:
✕ [Violation] main.embedded_test
FilePath: pipeline_definition.json
Reason: Hello, testy mctest! (from embedded rego)

For more information about policy issues, see the policy documentation: https://conforma.dev/docs/policy/

---

[policy using embedded rego functions:stderr - 1]
Error: success criteria not met

---
22 changes: 22 additions & 0 deletions features/embedded_rego.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Feature: embedded rego functionality
The ec command should be able to use embedded rego functions in policy evaluation

Background:
# Todo: We can use file paths so we don't really need git to test this
Given stub git daemon running

Scenario: policy using embedded rego functions
Given a git repository named "embedded-rego-config" with
| policy.yaml | examples/embedded_rego_config.yaml |
Given a git repository named "embedded-rego-policy" with
| main.rego | examples/embedded_rego_test.rego |

# The rego in embedded_rego_test ignores the input
Given a pipeline definition file named "pipeline_definition.json" containing
"""
{}
"""

When ec command is run with "validate input --file pipeline_definition.json --policy git::https://${GITHOST}/git/embedded-rego-config.git --show-successes"
Then the exit status should be 1
Then the output should match the snapshot
4 changes: 4 additions & 0 deletions internal/embedded/rego/ec_lib/hello_world.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package ec_lib

# Simple POC to demonstrate the idea of embedding rego in the cli
hello_world(name) := sprintf("Hello, %s! (from embedded rego)", [name])
26 changes: 26 additions & 0 deletions internal/embedded/rego/embed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright The Conforma 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 rego

import "embed"

// EmbeddedRego contains rego files embedded in the CLI binary.
// These files are available without needing to fetch them from external
// sources, solving the shared dependency problem for common rego utilities.
//
//go:embed ec_lib/*.rego
var EmbeddedRego embed.FS
38 changes: 38 additions & 0 deletions internal/embedded/rego/embed_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright The Conforma 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 rego

import (
"io/fs"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestHelloWorldRegoContent(t *testing.T) {
// Test that the hello_world.rego file can be read and has expected content
content, err := fs.ReadFile(EmbeddedRego, "ec_lib/hello_world.rego")
require.NoError(t, err, "Should be able to read hello_world.rego")

contentStr := string(content)
assert.Contains(t, contentStr, "package ec_lib")
assert.Contains(t, contentStr, "hello_world(name)")
assert.Contains(t, contentStr, "from embedded rego")
}
6 changes: 6 additions & 0 deletions internal/evaluator/conftest_evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,12 @@ func NewConftestEvaluatorWithNamespaceAndFilterType(
c.policyDir = filepath.Join(c.workDir, "policy")
c.dataDir = filepath.Join(c.workDir, "data")

// Write embedded rego files to policy directory
if err := utils.WriteEmbeddedRego(ctx, fs, c.policyDir); err != nil {
log.Warnf("Failed to write embedded rego files: %v", err)
// Continue without embedded files - graceful degradation
}

if err := c.createDataDirectory(ctx); err != nil {
return nil, err
}
Expand Down
156 changes: 156 additions & 0 deletions internal/utils/embedded_rego_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Copyright The Conforma 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 utils

import (
"context"
"path/filepath"
"testing"

"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestWriteEmbeddedRego(t *testing.T) {
// Test that embedded rego files are written correctly to filesystem
ctx := context.Background()
fs := afero.NewMemMapFs()

// Create a temporary policy directory
policyDir := "/tmp/policy"
err := fs.MkdirAll(policyDir, 0o755)
require.NoError(t, err)

// Call WriteEmbeddedRego
err = WriteEmbeddedRego(ctx, fs, policyDir)
require.NoError(t, err, "WriteEmbeddedRego should succeed")

// Verify embedded directory was created
embeddedDir := filepath.Join(policyDir, "embedded")
exists, err := afero.DirExists(fs, embeddedDir)
require.NoError(t, err)
assert.True(t, exists, "embedded directory should be created")

// Verify hello_world.rego file was written
helloWorldPath := filepath.Join(embeddedDir, "ec_lib", "hello_world.rego")
exists, err = afero.Exists(fs, helloWorldPath)
require.NoError(t, err)
assert.True(t, exists, "hello_world.rego should be written")

// Verify file content is correct
content, err := afero.ReadFile(fs, helloWorldPath)
require.NoError(t, err)

contentStr := string(content)
assert.Contains(t, contentStr, "package ec_lib")
assert.Contains(t, contentStr, "hello_world(name)")
assert.Contains(t, contentStr, "from embedded rego")
}

func TestWriteEmbeddedRegoMaintainsDirectoryStructure(t *testing.T) {
// Test that directory structure from embedded files is maintained
ctx := context.Background()
fs := afero.NewMemMapFs()

policyDir := "/tmp/policy"
err := fs.MkdirAll(policyDir, 0o755)
require.NoError(t, err)

err = WriteEmbeddedRego(ctx, fs, policyDir)
require.NoError(t, err)

// Verify the full directory structure: policy/embedded/ec_lib/hello_world.rego
expectedPath := filepath.Join(policyDir, "embedded", "ec_lib", "hello_world.rego")
exists, err := afero.Exists(fs, expectedPath)
require.NoError(t, err)
assert.True(t, exists, "Full directory structure should be maintained")

// Verify ec_lib directory exists
ecLibDir := filepath.Join(policyDir, "embedded", "ec_lib")
exists, err = afero.DirExists(fs, ecLibDir)
require.NoError(t, err)
assert.True(t, exists, "ec_lib subdirectory should be created")
}

func TestWriteEmbeddedRegoFilePermissions(t *testing.T) {
// Test that files are written with correct permissions
ctx := context.Background()
fs := afero.NewMemMapFs()

policyDir := "/tmp/policy"
err := fs.MkdirAll(policyDir, 0o755)
require.NoError(t, err)

err = WriteEmbeddedRego(ctx, fs, policyDir)
require.NoError(t, err)

// Check file permissions
helloWorldPath := filepath.Join(policyDir, "embedded", "ec_lib", "hello_world.rego")
info, err := fs.Stat(helloWorldPath)
require.NoError(t, err)

// Should be readable and writable by owner, readable by group and others
assert.Equal(t, "-rw-r--r--", info.Mode().String())
}

func TestWriteEmbeddedRegoNonExistentPolicyDir(t *testing.T) {
// Test behavior when policy directory doesn't exist
ctx := context.Background()
fs := afero.NewMemMapFs()

// Don't create the policy directory
policyDir := "/tmp/nonexistent/policy"

// WriteEmbeddedRego should create necessary directories
err := WriteEmbeddedRego(ctx, fs, policyDir)
require.NoError(t, err, "Should create necessary directories")

// Verify the embedded directory was created
embeddedDir := filepath.Join(policyDir, "embedded")
exists, err := afero.DirExists(fs, embeddedDir)
require.NoError(t, err)
assert.True(t, exists, "Should create embedded directory and parents")
}

func TestWriteEmbeddedRegoIdempotent(t *testing.T) {
// Test that calling WriteEmbeddedRego multiple times is safe
ctx := context.Background()
fs := afero.NewMemMapFs()

policyDir := "/tmp/policy"
err := fs.MkdirAll(policyDir, 0o755)
require.NoError(t, err)

// Call WriteEmbeddedRego twice
err = WriteEmbeddedRego(ctx, fs, policyDir)
require.NoError(t, err, "First call should succeed")

err = WriteEmbeddedRego(ctx, fs, policyDir)
require.NoError(t, err, "Second call should succeed (idempotent)")

// Verify file still exists and has correct content
helloWorldPath := filepath.Join(policyDir, "embedded", "ec_lib", "hello_world.rego")
content, err := afero.ReadFile(fs, helloWorldPath)
require.NoError(t, err)

contentStr := string(content)
assert.Contains(t, contentStr, "package ec_lib")
assert.Contains(t, contentStr, "hello_world(name)")
}
55 changes: 55 additions & 0 deletions internal/utils/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"encoding/json"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
Expand All @@ -28,6 +29,8 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/afero"
"sigs.k8s.io/yaml"

embeddedRego "github.com/conforma/cli/internal/embedded/rego"
)

// CreateWorkDir creates the working directory in tmp and some subdirectories
Expand Down Expand Up @@ -162,3 +165,55 @@ func anyEnvSet(varNames []string) bool {
}
return false
}

// WriteEmbeddedRego writes embedded rego files to the specified policy directory.
// This follows the same pattern as external policy sources, where each source
// gets its own subdirectory. Embedded rego files are placed in an "embedded"
// subdirectory to match the uniqueDestination pattern used for external sources.
func WriteEmbeddedRego(ctx context.Context, afs afero.Fs, policyDir string) error {
// Create the embedded subdirectory (follows pattern where each source gets its own subdir)
embeddedDir := filepath.Join(policyDir, "embedded")
if err := afs.MkdirAll(embeddedDir, 0o755); err != nil {
return fmt.Errorf("failed to create embedded rego directory: %w", err)
}

// Walk through all embedded rego files and write them to the filesystem
return fs.WalkDir(embeddedRego.EmbeddedRego, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return fmt.Errorf("failed to walk embedded rego file %s: %w", path, err)
}

// Skip directories
if d.IsDir() {
return nil
}

// Only process .rego files
if !strings.HasSuffix(path, ".rego") {
return nil
}

// Read the embedded file content
content, err := fs.ReadFile(embeddedRego.EmbeddedRego, path)
if err != nil {
return fmt.Errorf("failed to read embedded rego file %s: %w", path, err)
}

// Write to the target location, maintaining directory structure
targetPath := filepath.Join(embeddedDir, path)
targetDir := filepath.Dir(targetPath)

// Ensure target directory exists
if err := afs.MkdirAll(targetDir, 0o755); err != nil {
return fmt.Errorf("failed to create directory %s: %w", targetDir, err)
}

// Write the file
if err := afero.WriteFile(afs, targetPath, content, 0o644); err != nil {
return fmt.Errorf("failed to write embedded rego file to %s: %w", targetPath, err)
}

log.Debugf("Wrote embedded rego file: %s", targetPath)
return nil
})
}
Loading