From 3589382378b89f6868623efdeb33da338195526e Mon Sep 17 00:00:00 2001 From: Jason Hall Date: Thu, 10 Feb 2022 12:12:23 -0500 Subject: [PATCH] Correctly handle platforms that include CPU variants (#1929) * Correctly handle platforms that include CPU variants Prior to this change, Kaniko would only select the platform-specific image from a multi-platform image using the OS and architecture to select the image. This leads to problems when there are two platforms that are indistinguishable using only this information (e.g., linux/arm/v5 and linux/arm/v7). This change more explicitly selects the right image from a multi-platform image, taking CPU variant into account (v5 vs v7), using containerd's CPU variant detection logic. This also moves platform defaulting up as early as possible in execution as it can go, so that malformed platform values are detected as soon as possible. * set platform in unit test --- cmd/executor/cmd/root.go | 10 ++++++++++ cmd/warmer/cmd/root.go | 11 +++++++++++ pkg/executor/build_test.go | 2 ++ pkg/image/remote/remote.go | 30 ++++++------------------------ 4 files changed, 29 insertions(+), 24 deletions(-) diff --git a/cmd/executor/cmd/root.go b/cmd/executor/cmd/root.go index fc0aa16f5..bc0231a2e 100644 --- a/cmd/executor/cmd/root.go +++ b/cmd/executor/cmd/root.go @@ -32,7 +32,9 @@ import ( "github.com/GoogleContainerTools/kaniko/pkg/logging" "github.com/GoogleContainerTools/kaniko/pkg/timing" "github.com/GoogleContainerTools/kaniko/pkg/util" + "github.com/containerd/containerd/platforms" "github.com/genuinetools/bpfd/proc" + v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -223,6 +225,14 @@ func addKanikoOptionsFlags() { if val, ok := os.LookupEnv("KANIKO_REGISTRY_MIRROR"); ok { opts.RegistryMirrors.Set(val) } + + // Default the custom platform flag to our current platform, and validate it. + if opts.CustomPlatform == "" { + opts.CustomPlatform = platforms.DefaultString() + } + if _, err := v1.ParsePlatform(opts.CustomPlatform); err != nil { + logrus.Fatalf("Invalid platform %q: %v", opts.CustomPlatform, err) + } } // addHiddenFlags marks certain flags as hidden from the executor help text diff --git a/cmd/warmer/cmd/root.go b/cmd/warmer/cmd/root.go index e60caa5f9..4e50ff657 100644 --- a/cmd/warmer/cmd/root.go +++ b/cmd/warmer/cmd/root.go @@ -24,7 +24,10 @@ import ( "github.com/GoogleContainerTools/kaniko/pkg/cache" "github.com/GoogleContainerTools/kaniko/pkg/config" "github.com/GoogleContainerTools/kaniko/pkg/logging" + "github.com/containerd/containerd/platforms" + v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -84,6 +87,14 @@ func addKanikoOptionsFlags() { RootCmd.PersistentFlags().VarP(&opts.RegistriesCertificates, "registry-certificate", "", "Use the provided certificate for TLS communication with the given registry. Expected format is 'my.registry.url=/path/to/the/server/certificate'.") RootCmd.PersistentFlags().VarP(&opts.RegistryMirrors, "registry-mirror", "", "Registry mirror to use as pull-through cache instead of docker.io. Set it repeatedly for multiple mirrors.") RootCmd.PersistentFlags().StringVarP(&opts.CustomPlatform, "customPlatform", "", "", "Specify the build platform if different from the current host") + + // Default the custom platform flag to our current platform, and validate it. + if opts.CustomPlatform == "" { + opts.CustomPlatform = platforms.DefaultString() + } + if _, err := v1.ParsePlatform(opts.CustomPlatform); err != nil { + logrus.Fatalf("Invalid platform %q: %v", opts.CustomPlatform, err) + } } // addHiddenFlags marks certain flags as hidden from the executor help text diff --git a/pkg/executor/build_test.go b/pkg/executor/build_test.go index 4796fc7ac..1c383f3f9 100644 --- a/pkg/executor/build_test.go +++ b/pkg/executor/build_test.go @@ -33,6 +33,7 @@ import ( "github.com/GoogleContainerTools/kaniko/pkg/dockerfile" "github.com/GoogleContainerTools/kaniko/pkg/util" "github.com/GoogleContainerTools/kaniko/testutil" + "github.com/containerd/containerd/platforms" "github.com/google/go-cmp/cmp" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/empty" @@ -371,6 +372,7 @@ COPY --from=second /bar /bat ioutil.WriteFile(f.Name(), []byte(tt.args.dockerfile), 0755) opts := &config.KanikoOptions{ DockerfilePath: f.Name(), + CustomPlatform: platforms.DefaultString(), } testStages, metaArgs, err := dockerfile.ParseStages(opts) if err != nil { diff --git a/pkg/image/remote/remote.go b/pkg/image/remote/remote.go index 990a7a1db..d02638684 100644 --- a/pkg/image/remote/remote.go +++ b/pkg/image/remote/remote.go @@ -17,7 +17,6 @@ limitations under the License. package remote import ( - "runtime" "strings" "github.com/GoogleContainerTools/kaniko/pkg/config" @@ -129,28 +128,11 @@ func setNewRegistry(ref name.Reference, newReg name.Registry) name.Reference { func remoteOptions(registryName string, opts config.RegistryOptions, customPlatform string) []remote.Option { tr := util.MakeTransport(opts, registryName) - // on which v1.Platform is this currently running? - platform := currentPlatform(customPlatform) - - return []remote.Option{remote.WithTransport(tr), remote.WithAuthFromKeychain(creds.GetKeychain()), remote.WithPlatform(platform)} -} - -// CurrentPlatform returns the v1.Platform on which the code runs -func currentPlatform(customPlatform string) v1.Platform { - if customPlatform != "" { - customPlatformArray := strings.Split(customPlatform, "/") - imagePlatform := v1.Platform{} - imagePlatform.OS = customPlatformArray[0] - if len(customPlatformArray) > 1 { - imagePlatform.Architecture = customPlatformArray[1] - if len(customPlatformArray) > 2 { - imagePlatform.Variant = customPlatformArray[2] - } - } - return imagePlatform - } - return v1.Platform{ - OS: runtime.GOOS, - Architecture: runtime.GOARCH, + // The platform value has previously been validated. + platform, err := v1.ParsePlatform(customPlatform) + if err != nil { + logrus.Fatalf("Invalid platform %q: %v", customPlatform, err) } + + return []remote.Option{remote.WithTransport(tr), remote.WithAuthFromKeychain(creds.GetKeychain()), remote.WithPlatform(*platform)} }