diff --git a/act/container/docker_run.go b/act/container/docker_run.go index 68340eff..0bbe42d0 100644 --- a/act/container/docker_run.go +++ b/act/container/docker_run.go @@ -26,6 +26,7 @@ import ( "github.com/docker/docker/pkg/stdcopy" specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/Masterminds/semver" "github.com/pkg/errors" log "github.com/sirupsen/logrus" "golang.org/x/term" @@ -77,6 +78,25 @@ func NewContainer(input *NewContainerInput) Container { return cr } +// supportsContainerImagePlatform returns true if the underlying Docker server +// API version is 1.41 and beyond +func supportsContainerImagePlatform(cli *client.Client) bool { + ctx := context.TODO() + logger := common.Logger(ctx) + ver, err := cli.ServerVersion(ctx) + if err != nil { + logger.Panicf("Failed to get Docker API Version: %s", err) + return false + } + sv, err := semver.NewVersion(ver.APIVersion) + if err != nil { + logger.Panicf("Failed to unmarshal Docker Version: %s", err) + return false + } + constraint, _ := semver.NewConstraint(">= 1.41") + return constraint.Check(sv) +} + func (cr *containerReference) Create() common.Executor { return common. NewDebugExecutor("%sdocker create image=%s platform=%s entrypoint=%+q cmd=%+q", logPrefix, cr.input.Image, cr.input.Platform, cr.input.Entrypoint, cr.input.Cmd). @@ -272,22 +292,26 @@ func (cr *containerReference) create() common.Executor { }) } - desiredPlatform := strings.SplitN(cr.input.Platform, `/`, 2) + var platSpecs *specs.Platform + if supportsContainerImagePlatform(cr.cli) { + desiredPlatform := strings.SplitN(cr.input.Platform, `/`, 2) - if len(desiredPlatform) != 2 { - logger.Panicf("Incorrect container platform option. %s is not a valid platform.", cr.input.Platform) + if len(desiredPlatform) != 2 { + logger.Panicf("Incorrect container platform option. %s is not a valid platform.", cr.input.Platform) + } + + platSpecs = &specs.Platform{ + Architecture: desiredPlatform[1], + OS: desiredPlatform[0], + } } - resp, err := cr.cli.ContainerCreate(ctx, config, &container.HostConfig{ Binds: input.Binds, Mounts: mounts, NetworkMode: container.NetworkMode(input.NetworkMode), Privileged: input.Privileged, UsernsMode: container.UsernsMode(input.UsernsMode), - }, nil, &specs.Platform{ - Architecture: desiredPlatform[1], - OS: desiredPlatform[0], - }, input.Name) + }, nil, platSpecs, input.Name) if err != nil { return errors.WithStack(err) } diff --git a/cmd/root.go b/cmd/root.go index 81691460..0468b827 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -58,7 +58,7 @@ func Execute(ctx context.Context, version string) { rootCmd.PersistentFlags().StringVarP(&input.secretfile, "secret-file", "", ".secrets", "file with list of secrets to read from (e.g. --secret-file .secrets)") rootCmd.PersistentFlags().BoolVarP(&input.insecureSecrets, "insecure-secrets", "", false, "NOT RECOMMENDED! Doesn't hide secrets while printing logs.") rootCmd.PersistentFlags().StringVarP(&input.envfile, "env-file", "", ".env", "environment file to read and use as env in the containers") - rootCmd.PersistentFlags().StringVarP(&input.containerArchitecture, "container-architecture", "", "", "Architecture which should be used to run containers, e.g.: linux/amd64. Defaults to linux/ [linux/"+runtime.GOARCH+"]") + rootCmd.PersistentFlags().StringVarP(&input.containerArchitecture, "container-architecture", "", "", "Architecture which should be used to run containers, e.g.: linux/amd64. Defaults to linux/ [linux/"+runtime.GOARCH+"]. Requires Docker server API Version 1.41+. Ignored on earlier Docker server platforms.") rootCmd.SetArgs(args()) if err := rootCmd.Execute(); err != nil {