diff --git a/Dockerfile b/Dockerfile index fcb6b802d..4479c81d5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ ARG ALPINE_VERSION=3.23 ARG XX_VERSION=1.7.0 # for testing -ARG DOCKER_VERSION=29.0.0 +ARG DOCKER_VERSION=29.1 ARG DOCKER_VERSION_ALT_28=28.5 ARG DOCKER_VERSION_ALT_27=27.5.1 ARG DOCKER_CLI_VERSION=${DOCKER_VERSION} diff --git a/tests/integration_test.go b/tests/integration_test.go index b19d6f573..72b2dd29e 100644 --- a/tests/integration_test.go +++ b/tests/integration_test.go @@ -24,6 +24,7 @@ func TestIntegration(t *testing.T) { tests = append(tests, commonTests...) tests = append(tests, buildTests...) tests = append(tests, policyBuildTests...) + tests = append(tests, policyEvalTests...) tests = append(tests, bakeTests...) tests = append(tests, historyTests...) tests = append(tests, inspectTests...) diff --git a/tests/policy_eval.go b/tests/policy_eval.go new file mode 100644 index 000000000..d6ce00f01 --- /dev/null +++ b/tests/policy_eval.go @@ -0,0 +1,212 @@ +package tests + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "testing" + + "github.com/containerd/continuity/fs/fstest" + "github.com/docker/buildx/policy" + "github.com/moby/buildkit/identity" + "github.com/moby/buildkit/util/testutil/integration" + "github.com/stretchr/testify/require" +) + +var policyEvalTests = []func(t *testing.T, sb integration.Sandbox){ + testPolicyEvalAllow, + testPolicyEvalDeny, + testPolicyEvalPrint, + testPolicyEvalFields, + testPolicyEvalLabel, +} + +func testPolicyEvalAllow(t *testing.T, sb integration.Sandbox) { + policyFile := []byte(` +package docker + +default allow = false + +allow if not input.image + +allow if input.image.repo == "busybox" + +decision := {"allow": allow} +`) + dir := tmpdir( + t, + fstest.CreateFile("policy.rego", policyFile, 0600), + ) + + cmd := buildxCmd(sb, withDir(dir), withArgs( + "policy", + "eval", + "--filename", + "policy", + "docker-image://busybox:latest", + )) + out, err := cmd.CombinedOutput() + require.NoError(t, err, string(out)) +} + +func testPolicyEvalDeny(t *testing.T, sb integration.Sandbox) { + policyFile := []byte(` +package docker + +default allow = false + +allow if not input.image + +allow if input.image.repo == "alpine" + +decision := {"allow": allow} +`) + dir := tmpdir( + t, + fstest.CreateFile("policy.rego", policyFile, 0600), + ) + + cmd := buildxCmd(sb, withDir(dir), withArgs( + "policy", + "eval", + "--filename", + "policy", + "docker-image://busybox:latest", + )) + out, err := cmd.CombinedOutput() + require.Error(t, err, string(out)) + require.Contains(t, string(out), "policy denied") +} + +func testPolicyEvalPrint(t *testing.T, sb integration.Sandbox) { + cmd := buildxCmd(sb, withArgs( + "policy", + "eval", + "--print", + "docker-image://busybox:latest", + )) + var stderr bytes.Buffer + cmd.Stderr = &stderr + out, err := cmd.Output() + require.NoError(t, err, stderr.String()) + + var input policy.Input + err = json.Unmarshal(out, &input) + require.NoError(t, err, string(out)) + require.NotNil(t, input.Image) + require.Equal(t, "busybox", input.Image.Repo) +} + +func testPolicyEvalFields(t *testing.T, sb integration.Sandbox) { + registry, err := sb.NewRegistry() + if errors.Is(err, integration.ErrRequirements) { + t.Skip(err.Error()) + } + require.NoError(t, err) + unlabeledRef := registry + "/buildx/policy-eval-fields:" + identity.NewID() + labeledRef := registry + "/buildx/policy-eval-fields:" + identity.NewID() + + dir := tmpdir( + t, + fstest.CreateFile("Dockerfile", []byte("FROM busybox:latest\n"), 0600), + ) + buildCmd := buildxCmd(sb, withDir(dir), withArgs( + "build", + "--progress=plain", + "--output=type=image,name="+unlabeledRef+",push=true", + dir, + )) + buildOut, err := buildCmd.CombinedOutput() + require.NoError(t, err, string(buildOut)) + + labeledDir := tmpdir( + t, + fstest.CreateFile("Dockerfile", []byte("FROM busybox:latest\nLABEL com.example.policy=label\n"), 0600), + ) + labeledCmd := buildxCmd(sb, withDir(labeledDir), withArgs( + "build", + "--progress=plain", + "--output=type=image,name="+labeledRef+",push=true", + labeledDir, + )) + labeledOut, err := labeledCmd.CombinedOutput() + require.NoError(t, err, string(labeledOut)) + + cmd := buildxCmd(sb, withArgs( + "policy", + "eval", + "--print", + "--fields", + "image.labels", + fmt.Sprintf("docker-image://%s", unlabeledRef), + )) + var stderr bytes.Buffer + cmd.Stderr = &stderr + out, err := cmd.Output() + require.NoError(t, err, stderr.String()) + + var input policy.Input + err = json.Unmarshal(out, &input) + require.NoError(t, err, string(out)) + require.NotNil(t, input.Image) + require.Empty(t, input.Image.Labels) + + cmd = buildxCmd(sb, withArgs( + "policy", + "eval", + "--print", + "--fields", + "image.labels", + fmt.Sprintf("docker-image://%s", labeledRef), + )) + stderr.Reset() + out, err = cmd.Output() + require.NoError(t, err, stderr.String()) + + err = json.Unmarshal(out, &input) + require.NoError(t, err, string(out)) + require.NotNil(t, input.Image) + require.Equal(t, "label", input.Image.Labels["com.example.policy"]) +} + +func testPolicyEvalLabel(t *testing.T, sb integration.Sandbox) { + registry, err := sb.NewRegistry() + if errors.Is(err, integration.ErrRequirements) { + t.Skip(err.Error()) + } + require.NoError(t, err) + imageRef := registry + "/buildx/policy-eval-label:" + identity.NewID() + + dir := tmpdir( + t, + fstest.CreateFile("Dockerfile", []byte("FROM busybox:latest\nLABEL com.example.policy=label\n"), 0600), + fstest.CreateFile("policy.rego", []byte(` +package docker + +default allow = false + +allow if input.image.labels["com.example.policy"] == "label" + +decision := {"allow": allow} +`), 0600), + ) + buildCmd := buildxCmd(sb, withDir(dir), withArgs( + "build", + "--progress=plain", + "--output=type=image,name="+imageRef+",push=true", + dir, + )) + buildOut, err := buildCmd.CombinedOutput() + require.NoError(t, err, string(buildOut)) + + cmd := buildxCmd(sb, withDir(dir), withArgs( + "policy", + "eval", + "--filename", + "policy", + fmt.Sprintf("docker-image://%s", imageRef), + )) + out, err := cmd.CombinedOutput() + require.NoError(t, err, string(out)) +}