From d67e282f68611056b8311a255142e75e89714978 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 af5140f13e25a304cba8d3ac8646342440fc2ad6 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 --- README.md | 2 +- cmd/root.go | 2 +- go.mod | 1 + go.sum | 1 + pkg/container/docker_run.go | 40 +++++++++++++++++++++++++++++-------- 5 files changed, 36 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a039fa5..8374e15 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ It will save that information to `~/.actrc`, please refer to [Configuration](#co ```none -a, --actor string user that triggered the event (default "nektos/act") -b, --bind bind working directory to container, rather than copy - --container-architecture string Architecture which should be used to run containers, e.g.: linux/amd64. Defaults to linux/ [linux/amd64] + --container-architecture string Architecture which should be used to run containers, e.g.: linux/amd64. Defaults to linux/ [linux/amd64]. Requires Docker server API Version 1.41+. Ignored on earlier Docker server platforms. --defaultbranch string the name of the main branch --detect-event Use first event type from workflow as event that triggered the workflow -C, --directory string working directory (default ".") diff --git a/cmd/root.go b/cmd/root.go index 8169146..0468b82 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 { diff --git a/go.mod b/go.mod index 5a73f97..a2ffe39 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.14 require ( github.com/AlecAivazis/survey/v2 v2.2.7 + github.com/Masterminds/semver v1.5.0 github.com/MichaelTJones/walk v0.0.0-20161122175330-4748e29d5718 // indirect github.com/andreaskoch/go-fswatch v1.0.0 github.com/containerd/containerd v1.4.1 // indirect diff --git a/go.sum b/go.sum index b2da1d9..c3c0321 100644 --- a/go.sum +++ b/go.sum @@ -93,6 +93,7 @@ github.com/Djarvur/go-err113 v0.0.0-20200410182137-af658d038157/go.mod h1:4UJr5H github.com/Djarvur/go-err113 v0.1.0/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo= github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= diff --git a/pkg/container/docker_run.go b/pkg/container/docker_run.go index 68340ef..0bbe42d 100644 --- a/pkg/container/docker_run.go +++ b/pkg/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) }