diff --git a/commands/policy/eval.go b/commands/policy/eval.go index 9cd484cdd..7454a61df 100644 --- a/commands/policy/eval.go +++ b/commands/policy/eval.go @@ -33,6 +33,7 @@ type evalOpts struct { filename string printOutput bool fields []string + platform string builder *string } @@ -52,6 +53,7 @@ func evalCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command { cmd.Flags().StringVar(&opts.filename, "filename", "Dockerfile", "Policy filename to evaluate") cmd.Flags().BoolVar(&opts.printOutput, "print", false, "Print policy output") cmd.Flags().StringSliceVar(&opts.fields, "fields", nil, "Fields to evaluate") + cmd.Flags().StringVar(&opts.platform, "platform", "", "Target platform for policy evaluation") return cmd } @@ -81,29 +83,29 @@ func runEval(ctx context.Context, dockerCli command.Cli, source string, opts eva return err } - workers, err := c.ListWorkers(ctx) - if err != nil { - return err - } + var p ocispecs.Platform + if opts.platform != "" { + parsedPlatform, err := parsePlatform(opts.platform) + if err != nil { + return err + } + p = *parsedPlatform + } else { + workers, err := c.ListWorkers(ctx) + if err != nil { + return err + } - if len(workers) == 0 { - return errors.New("no workers available in the builder") - } + if len(workers) == 0 { + return errors.New("no workers available in the builder") + } - defaultPlatform := workers[0].Platforms[0] - p := ocispecs.Platform{ - Architecture: defaultPlatform.Architecture, - OS: defaultPlatform.OS, - Variant: defaultPlatform.Variant, + p = workers[0].Platforms[0] } metaResolver := sourcemeta.NewResolver(c) defer metaResolver.Close() - platform := &pb.Platform{ - Architecture: p.Architecture, - OS: p.OS, - Variant: p.Variant, - } + platform := toPBPlatform(p) verifier := policy.SignatureVerifier(confutil.NewConfig(dockerCli)) if opts.printOutput { diff --git a/commands/policy/eval_test.go b/commands/policy/eval_test.go index 77522a5b3..e1cc6f46b 100644 --- a/commands/policy/eval_test.go +++ b/commands/policy/eval_test.go @@ -6,10 +6,39 @@ import ( policytypes "github.com/docker/buildx/policy" "github.com/docker/buildx/util/sourcemeta" gwpb "github.com/moby/buildkit/frontend/gateway/pb" + "github.com/moby/buildkit/solver/pb" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/stretchr/testify/require" ) +func TestParsePlatform(t *testing.T) { + t.Run("normalize", func(t *testing.T) { + platform, err := parsePlatform("linux/arm/v7") + require.NoError(t, err) + require.Equal(t, &ocispecs.Platform{ + OS: "linux", + Architecture: "arm", + Variant: "v7", + }, platform) + }) + + t.Run("invalid", func(t *testing.T) { + platform, err := parsePlatform("not-a-platform") + require.Nil(t, platform) + require.Error(t, err) + require.ErrorContains(t, err, "invalid platform \"not-a-platform\"") + require.ErrorContains(t, err, "unknown operating system or architecture") + }) +} + +func TestToPBPlatform(t *testing.T) { + platform := ocispecs.Platform{OS: "linux", Architecture: "amd64"} + require.Equal(t, &pb.Platform{ + OS: "linux", + Architecture: "amd64", + }, toPBPlatform(platform)) +} + func TestSourceResolverOptIncludesResolveAttestations(t *testing.T) { req := &gwpb.ResolveSourceMetaRequest{ ResolveMode: "default", diff --git a/commands/policy/platform.go b/commands/policy/platform.go new file mode 100644 index 000000000..cca1be0d8 --- /dev/null +++ b/commands/policy/platform.go @@ -0,0 +1,25 @@ +package policy + +import ( + "github.com/containerd/platforms" + "github.com/moby/buildkit/solver/pb" + ocispecs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +func parsePlatform(platform string) (*ocispecs.Platform, error) { + p, err := platforms.Parse(platform) + if err != nil { + return nil, errors.Wrapf(err, "invalid platform %q", platform) + } + p = platforms.Normalize(p) + return &p, nil +} + +func toPBPlatform(platform ocispecs.Platform) *pb.Platform { + return &pb.Platform{ + Architecture: platform.Architecture, + OS: platform.OS, + Variant: platform.Variant, + } +}