From 6d023b011e7b3987e01cfbc721de04029fe582da Mon Sep 17 00:00:00 2001 From: Jay Pipes Date: Tue, 30 Mar 2021 13:10:42 -0400 Subject: [PATCH] use container image platform only on docker 1.41+ (#591) Commit fef399d0578e23479926adb8e545743789b69701 introduced support for specifying a container image platform for cross-platform image building. Unfortunately, attempting to execute a docker command that includes the `--platform` flag against Docker daemons using API Version 1.40 and before results in the following error: ``` "specify container image platform" requires API version 1.41, but the Docker daemon API version is 1.40 ``` To allow `act` to be used on the 19.03 Docker CE and earlier versions, this patch simply checks the Docker daemon API version and only specifies platform specification when the daemon API version is 1.41 or greater. Fixes Issue #586 --- act/container/docker_run.go | 40 +++++++++++++++++++++++++++++-------- cmd/root.go | 2 +- 2 files changed, 33 insertions(+), 9 deletions(-) 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 {