policy: support reading policy from stdin via --file -
Allow passing policy content through stdin by specifying "--file -" in the eval command. This enables piping policy data without requiring a file on disk. Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
+17
-3
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"maps"
|
"maps"
|
||||||
"os"
|
"os"
|
||||||
@@ -191,9 +192,8 @@ func runEval(ctx context.Context, dockerCli command.Cli, source string, opts eva
|
|||||||
if opts.filename == "" {
|
if opts.filename == "" {
|
||||||
return errors.New("filename is required")
|
return errors.New("filename is required")
|
||||||
}
|
}
|
||||||
policyName := opts.filename
|
policyName, policyFile := policyFileNames(opts.filename)
|
||||||
policyFile := policyName + ".rego"
|
policyData, err := readPolicyData(policyFile, os.Stdin)
|
||||||
policyData, err := os.ReadFile(policyFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "failed to read policy file %s", policyFile)
|
return errors.Wrapf(err, "failed to read policy file %s", policyFile)
|
||||||
}
|
}
|
||||||
@@ -267,6 +267,20 @@ func runEval(ctx context.Context, dockerCli command.Cli, source string, opts eva
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func policyFileNames(filename string) (string, string) {
|
||||||
|
if filename == "-" {
|
||||||
|
return "stdin", filename
|
||||||
|
}
|
||||||
|
return filename, filename + ".rego"
|
||||||
|
}
|
||||||
|
|
||||||
|
func readPolicyData(filename string, stdin io.Reader) ([]byte, error) {
|
||||||
|
if filename == "-" {
|
||||||
|
return io.ReadAll(stdin)
|
||||||
|
}
|
||||||
|
return os.ReadFile(filename)
|
||||||
|
}
|
||||||
|
|
||||||
func selectReloadFields(fields []string, unknowns []string) ([]string, []string) {
|
func selectReloadFields(fields []string, unknowns []string) ([]string, []string) {
|
||||||
if len(fields) == 0 {
|
if len(fields) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ Evaluate policy for a source
|
|||||||
| `--builder` | `string` | | Override the configured builder instance |
|
| `--builder` | `string` | | Override the configured builder instance |
|
||||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||||
| `--fields` | `stringSlice` | | Fields to evaluate |
|
| `--fields` | `stringSlice` | | Fields to evaluate |
|
||||||
| `--filename` | `string` | `Dockerfile` | Policy filename to evaluate |
|
| `-f`, `--file` | `string` | `Dockerfile` | Policy filename to evaluate |
|
||||||
|
| `--platform` | `string` | | Target platform for policy evaluation |
|
||||||
| `--print` | `bool` | | Print policy output |
|
| `--print` | `bool` | | Print policy output |
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
var policyEvalTests = []func(t *testing.T, sb integration.Sandbox){
|
var policyEvalTests = []func(t *testing.T, sb integration.Sandbox){
|
||||||
testPolicyEvalAllow,
|
testPolicyEvalAllow,
|
||||||
testPolicyEvalDeny,
|
testPolicyEvalDeny,
|
||||||
|
testPolicyEvalStdinFile,
|
||||||
testPolicyEvalPrint,
|
testPolicyEvalPrint,
|
||||||
testPolicyEvalFields,
|
testPolicyEvalFields,
|
||||||
testPolicyEvalLabel,
|
testPolicyEvalLabel,
|
||||||
@@ -88,6 +89,63 @@ decision := {"allow": allow}
|
|||||||
require.Contains(t, string(out), "policy denied")
|
require.Contains(t, string(out), "policy denied")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testPolicyEvalStdinFile(t *testing.T, sb integration.Sandbox) {
|
||||||
|
skipNoCompatBuildKit(t, sb, ">= 0.26.0-0", "policy input requires BuildKit v0.26.0+")
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
policy string
|
||||||
|
wantErrContains string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "allow",
|
||||||
|
policy: `
|
||||||
|
package docker
|
||||||
|
|
||||||
|
default allow = false
|
||||||
|
|
||||||
|
allow if not input.image
|
||||||
|
|
||||||
|
allow if input.image.repo == "busybox"
|
||||||
|
|
||||||
|
decision := {"allow": allow}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "deny",
|
||||||
|
policy: `
|
||||||
|
package docker
|
||||||
|
|
||||||
|
default allow = false
|
||||||
|
|
||||||
|
allow if input.image.repo == "alpine"
|
||||||
|
|
||||||
|
decision := {"allow": allow}
|
||||||
|
`,
|
||||||
|
wantErrContains: "policy denied",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
cmd := buildxCmd(sb, withArgs(
|
||||||
|
"policy",
|
||||||
|
"eval",
|
||||||
|
"--file",
|
||||||
|
"-",
|
||||||
|
"docker-image://busybox:latest",
|
||||||
|
))
|
||||||
|
cmd.Stdin = strings.NewReader(tc.policy)
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if tc.wantErrContains == "" {
|
||||||
|
require.NoError(t, err, string(out))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.Error(t, err, string(out))
|
||||||
|
require.Contains(t, string(out), tc.wantErrContains)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testPolicyEvalPrint(t *testing.T, sb integration.Sandbox) {
|
func testPolicyEvalPrint(t *testing.T, sb integration.Sandbox) {
|
||||||
skipNoCompatBuildKit(t, sb, ">= 0.26.0-0", "policy input requires BuildKit v0.26.0+")
|
skipNoCompatBuildKit(t, sb, ">= 0.26.0-0", "policy input requires BuildKit v0.26.0+")
|
||||||
cmd := buildxCmd(sb, withArgs(
|
cmd := buildxCmd(sb, withArgs(
|
||||||
|
|||||||
Reference in New Issue
Block a user