diff --git a/docs-v2/content/en/docs/pipeline-stages/builders/_index.md b/docs-v2/content/en/docs/pipeline-stages/builders/_index.md index 9be23b86fc9..76c7f6bf4df 100644 --- a/docs-v2/content/en/docs/pipeline-stages/builders/_index.md +++ b/docs-v2/content/en/docs/pipeline-stages/builders/_index.md @@ -15,7 +15,7 @@ Skaffold supports different tools for building images: | **Jib Maven and Gradle** | [Yes]({{< relref "/docs/pipeline-stages/builders/jib#jib-maven-and-gradle-locally" >}}) | - | [Yes]({{< relref "/docs/pipeline-stages/builders/jib#remotely-with-google-cloud-build" >}}) | | **Cloud Native Buildpacks** | [Yes]({{< relref "/docs/pipeline-stages/builders/buildpacks" >}}) | - | [Yes]({{< relref "/docs/pipeline-stages/builders/buildpacks" >}}) | | **Bazel** | [Yes]({{< relref "/docs/pipeline-stages/builders/bazel" >}}) | - | - | -| **ko** | [Yes]({{< relref "/docs/pipeline-stages/builders/ko" >}}) | - | - | +| **ko** | [Yes]({{< relref "/docs/pipeline-stages/builders/ko" >}}) | - | [Yes]({{< relref "/docs/pipeline-stages/builders/ko#remote-builds" >}}) | | **Custom Script** | [Yes]({{}}) | [Yes]({{}}) | - | **Configuration** @@ -184,13 +184,13 @@ The final list of target platforms need to ultimately be supported by the target | | Local Build | In Cluster Build | Remote on Google Cloud Build | |----|:-----------:|:----------------:|:----------------------------:| -| **Dockerfile** | Cross platform supported | Cross platform supported but platform should match cluster node running the pod. | Cross platform supported | -| **Jib Maven and Gradle** | Cross platform supported | - | Cross platform supported | +| **Dockerfile** | Cross-platform supported | Cross-platform supported but platform should match cluster node running the pod. | Cross-platform supported | +| **Jib Maven and Gradle** | Cross-platform and multi-platform supported | - | Cross-platform and multi-platform supported | | **Cloud Native Buildpacks** | Only supports `linux/amd64` | - | Only supports `linux/amd64` | -| **Bazel** | Cross platform supported but requires explicit platform specific rules. Not yet implemented | - | - | -| **ko** | Cross platform supported | - | - | -| **Custom Script** | Cross platform supported but requires user to implement it in the build script | Cross platform supported but requires user to implement it in the build script | - | +| **Bazel** | Cross-platform supported but requires explicit platform specific rules. Not yet implemented | - | - | +| **ko** | Cross-platform and multi-platform supported | - | Cross-platform and multi-platform supported | +| **Custom Script** | Cross-platform and multi-platform supported but requires user to implement it in the build script | Cross-platform and multi-platform supported but requires user to implement it in the build script | - | {{< alert title="Note" >}} -Multi-arch image build is not yet supported for any builders other than the [jib builder]({{}}), the [ko builder]({{}}) and the [custom builder]({{}}) in Skaffold. -{{< /alert >}} \ No newline at end of file +Skaffold supports multi-platform image builds using the [jib builder]({{}}), the [ko builder]({{}}) and the [custom builder]({{}}). +{{< /alert >}} diff --git a/docs-v2/content/en/docs/pipeline-stages/builders/ko.md b/docs-v2/content/en/docs/pipeline-stages/builders/ko.md index e9ded288e96..af0994be80b 100644 --- a/docs-v2/content/en/docs/pipeline-stages/builders/ko.md +++ b/docs-v2/content/en/docs/pipeline-stages/builders/ko.md @@ -411,9 +411,10 @@ To learn more about how Skaffold debugs Go applications, read the File `sync` is not supported while the ko builder feature is in Alpha. -### Remote builders +### Remote builds -Only `local` builds are supported while the ko builder feature is in Alpha. +The `ko` builder supports remote builds on Google Cloud Build. See the +[example](https://github.com/GoogleContainerTools/skaffold/tree/main/examples/ko). ### Using the `custom` builder diff --git a/docs/content/en/docs/pipeline-stages/builders/_index.md b/docs/content/en/docs/pipeline-stages/builders/_index.md index 9be23b86fc9..76c7f6bf4df 100644 --- a/docs/content/en/docs/pipeline-stages/builders/_index.md +++ b/docs/content/en/docs/pipeline-stages/builders/_index.md @@ -15,7 +15,7 @@ Skaffold supports different tools for building images: | **Jib Maven and Gradle** | [Yes]({{< relref "/docs/pipeline-stages/builders/jib#jib-maven-and-gradle-locally" >}}) | - | [Yes]({{< relref "/docs/pipeline-stages/builders/jib#remotely-with-google-cloud-build" >}}) | | **Cloud Native Buildpacks** | [Yes]({{< relref "/docs/pipeline-stages/builders/buildpacks" >}}) | - | [Yes]({{< relref "/docs/pipeline-stages/builders/buildpacks" >}}) | | **Bazel** | [Yes]({{< relref "/docs/pipeline-stages/builders/bazel" >}}) | - | - | -| **ko** | [Yes]({{< relref "/docs/pipeline-stages/builders/ko" >}}) | - | - | +| **ko** | [Yes]({{< relref "/docs/pipeline-stages/builders/ko" >}}) | - | [Yes]({{< relref "/docs/pipeline-stages/builders/ko#remote-builds" >}}) | | **Custom Script** | [Yes]({{}}) | [Yes]({{}}) | - | **Configuration** @@ -184,13 +184,13 @@ The final list of target platforms need to ultimately be supported by the target | | Local Build | In Cluster Build | Remote on Google Cloud Build | |----|:-----------:|:----------------:|:----------------------------:| -| **Dockerfile** | Cross platform supported | Cross platform supported but platform should match cluster node running the pod. | Cross platform supported | -| **Jib Maven and Gradle** | Cross platform supported | - | Cross platform supported | +| **Dockerfile** | Cross-platform supported | Cross-platform supported but platform should match cluster node running the pod. | Cross-platform supported | +| **Jib Maven and Gradle** | Cross-platform and multi-platform supported | - | Cross-platform and multi-platform supported | | **Cloud Native Buildpacks** | Only supports `linux/amd64` | - | Only supports `linux/amd64` | -| **Bazel** | Cross platform supported but requires explicit platform specific rules. Not yet implemented | - | - | -| **ko** | Cross platform supported | - | - | -| **Custom Script** | Cross platform supported but requires user to implement it in the build script | Cross platform supported but requires user to implement it in the build script | - | +| **Bazel** | Cross-platform supported but requires explicit platform specific rules. Not yet implemented | - | - | +| **ko** | Cross-platform and multi-platform supported | - | Cross-platform and multi-platform supported | +| **Custom Script** | Cross-platform and multi-platform supported but requires user to implement it in the build script | Cross-platform and multi-platform supported but requires user to implement it in the build script | - | {{< alert title="Note" >}} -Multi-arch image build is not yet supported for any builders other than the [jib builder]({{}}), the [ko builder]({{}}) and the [custom builder]({{}}) in Skaffold. -{{< /alert >}} \ No newline at end of file +Skaffold supports multi-platform image builds using the [jib builder]({{}}), the [ko builder]({{}}) and the [custom builder]({{}}). +{{< /alert >}} diff --git a/docs/content/en/docs/pipeline-stages/builders/ko.md b/docs/content/en/docs/pipeline-stages/builders/ko.md index e9ded288e96..af0994be80b 100644 --- a/docs/content/en/docs/pipeline-stages/builders/ko.md +++ b/docs/content/en/docs/pipeline-stages/builders/ko.md @@ -411,9 +411,10 @@ To learn more about how Skaffold debugs Go applications, read the File `sync` is not supported while the ko builder feature is in Alpha. -### Remote builders +### Remote builds -Only `local` builds are supported while the ko builder feature is in Alpha. +The `ko` builder supports remote builds on Google Cloud Build. See the +[example](https://github.com/GoogleContainerTools/skaffold/tree/main/examples/ko). ### Using the `custom` builder diff --git a/integration/examples/ko/skaffold.yaml b/integration/examples/ko/skaffold.yaml index f4e3ac2312d..49c5a8f35ce 100644 --- a/integration/examples/ko/skaffold.yaml +++ b/integration/examples/ko/skaffold.yaml @@ -17,4 +17,13 @@ kind: Config build: artifacts: - image: skaffold-ko - ko: {} + ko: + dependencies: + paths: + - "**/*.go" + - go.* +profiles: +- name: gcb + build: + googleCloudBuild: + koImage: gcr.io/k8s-skaffold/skaffold:v1.37.2-lts diff --git a/pkg/skaffold/build/gcb/ko.go b/pkg/skaffold/build/gcb/ko.go new file mode 100644 index 00000000000..7348d3d03e1 --- /dev/null +++ b/pkg/skaffold/build/gcb/ko.go @@ -0,0 +1,143 @@ +/* +Copyright 2022 The Skaffold Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package gcb + +import ( + "context" + "fmt" + "strings" + + "google.golang.org/api/cloudbuild/v1" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/platform" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + schema "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v2beta28" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/yaml" +) + +// koBuildSpec creates a Cloud Build configuration using the `ko` builder. +// +// Because Skaffold embeds ko as a library, the `skaffold` binary builds the container image on Cloud Build. +// +// The method uses the artifact input argument to generate a Skaffold Config manifest. +func (b *Builder) koBuildSpec(ctx context.Context, artifact *latest.Artifact, tag string, platforms platform.Matcher) (cloudbuild.Build, error) { + imageName, imageTag := splitImageNameAndTag(tag) + insecureRegistries := getKeys(b.cfg.GetInsecureRegistries()) + skaffoldConfig := createSkaffoldConfig(artifact, imageName, platforms.Array(), insecureRegistries) + skaffoldYaml, err := yaml.Marshal(skaffoldConfig) + if err != nil { + return cloudbuild.Build{}, fmt.Errorf("marshalling Skaffold Config YAML for %s: %w", tag, err) + } + log.Entry(ctx).Debugf("Skaffold config for Cloud Build:\n%s\n", skaffoldYaml) + verbosity := log.GetLevel().String() + return cloudBuildConfig(b.KoImage, skaffoldYaml, imageTag, verbosity, artifact.KoArtifact.Env...), nil +} + +func splitImageNameAndTag(imageNameWithTag string) (string, string) { + nameAndTag := strings.Split(imageNameWithTag, ":") + imageName := nameAndTag[0] + imageTag := "latest" + if len(nameAndTag) > 1 { + imageTag = nameAndTag[1] + } + return imageName, imageTag +} + +func getKeys(in map[string]bool) []string { + var keys []string + for v := range in { + keys = append(keys, v) + } + return keys +} + +// createSkaffoldConfig creates a Skaffold Config manifest for use in a build step on Cloud Build. +// +// The manifest uses a specific schema version that is known to be supported by a public Skaffold image on GCR/AR. +// The schema version must be a version supported by a public Skaffold image. +// The latest schema version is not used for two reasons: +// +// 1. There could be a mismatch between the versions of Skaffold used locally and on Cloud Build. +// +// For instance, a team could choose to use an LTS image for builds on Cloud Build. +// +// 2. The local version of Skaffold may not be available as a public image on GCR/AR. +// +// The manifest does not include artifact fields that are irrelevant for a remote build, such as Dependencies, LifecycleHooks, and Sync. +func createSkaffoldConfig(artifact *latest.Artifact, imageName string, platforms []string, insecureRegistries []string) *schema.SkaffoldConfig { + return &schema.SkaffoldConfig{ + APIVersion: schema.Version, + Kind: "Config", + Pipeline: schema.Pipeline{ + Build: schema.BuildConfig{ + Artifacts: []*schema.Artifact{{ + // Replace `ImageName` since we need the fully resolved name (with the Skaffold default repo). + ImageName: imageName, + // Copy values from the `artifact` function argument. + ArtifactType: schema.ArtifactType{ + KoArtifact: &schema.KoArtifact{ + BaseImage: artifact.KoArtifact.BaseImage, + Dir: artifact.KoArtifact.Dir, + Env: artifact.KoArtifact.Env, + Flags: artifact.KoArtifact.Flags, + Labels: artifact.KoArtifact.Labels, + Ldflags: artifact.KoArtifact.Ldflags, + Main: artifact.KoArtifact.Main, + }, + }, + Platforms: artifact.Platforms, // platforms defined for the artifact + Workspace: artifact.Workspace, + }}, + InsecureRegistries: insecureRegistries, + Platforms: platforms, // platforms provided via command-line flag, or envvar + }, + }, + } +} + +// cloudBuildConfig creates a single step build configuraration using the provided image and Skaffold config. +// +// The build step writes out the generated Skaffold Config manifest to a temporary file. +// Skaffold uses this Config to build and push the image. +func cloudBuildConfig(koImage string, skaffoldYaml []byte, imageTag string, verbosity string, env ...string) cloudbuild.Build { + return cloudbuild.Build{ + Steps: []*cloudbuild.BuildStep{{ + Name: koImage, + Entrypoint: "sh", + Args: []string{"-c", strings.Join( + []string{ + "skaffoldConfigFile=$(mktemp)", + // here document with quoted end marker to ensure no subsitution or expansion + fmt.Sprintf("cat << 'EOF' > $skaffoldConfigFile\n%s\nEOF", skaffoldYaml), + fmt.Sprintf("skaffold build --filename $skaffoldConfigFile --tag %s --verbosity %s", imageTag, verbosity), + }, + "\n", + )}, + Env: skaffoldGCBEnv(env...), + }}, + } +} + +func skaffoldGCBEnv(env ...string) []string { + defaultEnv := []string{ + "SKAFFOLD_DETECT_MINIKUBE=false", + "SKAFFOLD_INTERACTIVE=false", + "SKAFFOLD_UPDATE_CHECK=false", + } + return append(defaultEnv, env...) +} diff --git a/pkg/skaffold/build/gcb/ko_test.go b/pkg/skaffold/build/gcb/ko_test.go new file mode 100644 index 00000000000..df3e6254fc8 --- /dev/null +++ b/pkg/skaffold/build/gcb/ko_test.go @@ -0,0 +1,182 @@ +/* +Copyright 2022 The Skaffold Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package gcb + +import ( + "strings" + "testing" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + schema "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v2beta28" + "github.com/GoogleContainerTools/skaffold/testutil" +) + +func TestCloudBuildConfig(t *testing.T) { + tests := []struct { + description string + koImage string + skaffoldYaml string + imageTag string + verbosity string + env []string + }{ + { + description: "ensure Skaffold Config manifest is created in build step", + koImage: "gcr.io/k8s-skaffold/skaffold:v1.37.2-lts@sha256:0bde2b09928ce891f4e1bfb8d957648bbece9987ec6ef3678c6542196e64e71a", + skaffoldYaml: `apiVersion: skaffold/v2beta28 +kind: Config +build: + artifacts: + - image: skaffold-ko + ko: {} +`, + imageTag: "mytag", + verbosity: "info", + env: []string{"GOTRACEBACK=2"}, + }, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + config := cloudBuildConfig(test.koImage, []byte(test.skaffoldYaml), test.imageTag, test.verbosity, test.env...) + t.CheckEmpty(config.Images) + step := config.Steps[0] + t.CheckDeepEqual(test.koImage, step.Name) + t.CheckDeepEqual("sh", step.Entrypoint) + t.CheckContains(test.skaffoldYaml, step.Args[1]) + t.CheckContains("--tag "+test.imageTag, step.Args[1]) + t.CheckContains("--verbosity "+test.verbosity, step.Args[1]) + for _, envvar := range test.env { + t.CheckContains(envvar, strings.Join(step.Env, " ")) + } + }) + } +} + +func TestCreateSkaffoldConfig(t *testing.T) { + tests := []struct { + description string + artifact *latest.Artifact + imageName string + insecureRegistries []string + platforms []string + expectedSkaffoldConfig *schema.SkaffoldConfig + }{ + { + description: "all fields", + artifact: &latest.Artifact{ + ImageName: "myimage", + ArtifactType: latest.ArtifactType{ + KoArtifact: &latest.KoArtifact{ + BaseImage: "baseImage", + Dir: "./dir", + Env: []string{"GOTRACEBACK=2"}, + Flags: []string{"-tags", "netgo"}, + Labels: map[string]string{ + "org.opencontainers.image.source": "https://github.com/GoogleContainerTools/skaffold.git", + }, + Ldflags: []string{"-s", "-w"}, + Main: "./main", + Dependencies: &latest.KoDependencies{ + Paths: []string{"unused-koartifact-paths"}, + Ignore: []string{"unused-koartifact-ingore"}, + }, + }, + }, + Platforms: []string{"linux/amd64"}, + Workspace: "./workspace", + Dependencies: []*latest.ArtifactDependency{{ + ImageName: "unused-dependency-image-name", + Alias: "unused-dependency-alias", + }}, + LifecycleHooks: latest.BuildHooks{ + PreHooks: []latest.HostHook{{ + Command: []string{"unused-lifecycle-hook-command"}, + }}, + }, + Sync: &latest.Sync{ + Infer: []string{"unused-sync-infer"}, + }, + }, + imageName: "gcr.io/project-id/myimage", + insecureRegistries: []string{"insecure.example.com:5000"}, + platforms: []string{"linux/amd64", "linux/arm64"}, + expectedSkaffoldConfig: &schema.SkaffoldConfig{ + APIVersion: schema.Version, + Kind: "Config", + Pipeline: schema.Pipeline{ + Build: schema.BuildConfig{ + Artifacts: []*schema.Artifact{{ + ImageName: "gcr.io/project-id/myimage", + ArtifactType: schema.ArtifactType{ + KoArtifact: &schema.KoArtifact{ + BaseImage: "baseImage", + Dir: "./dir", + Env: []string{"GOTRACEBACK=2"}, + Flags: []string{"-tags", "netgo"}, + Labels: map[string]string{ + "org.opencontainers.image.source": "https://github.com/GoogleContainerTools/skaffold.git", + }, + Ldflags: []string{"-s", "-w"}, + Main: "./main", + }, + }, + Platforms: []string{"linux/amd64"}, + Workspace: "./workspace", + }}, + InsecureRegistries: []string{"insecure.example.com:5000"}, + Platforms: []string{"linux/amd64", "linux/arm64"}, + }, + }, + }, + }, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + skaffoldConfig := createSkaffoldConfig(test.artifact, test.imageName, test.platforms, test.insecureRegistries) + t.CheckDeepEqual(test.expectedSkaffoldConfig, skaffoldConfig) + }) + } +} + +func TestSplitImageNameAndTag(t *testing.T) { + tests := []struct { + description string + input string + expectedName string + expectedTag string + }{ + { + description: "image name with tag", + input: "gcr.io/project-id/myimage:mytag", + expectedName: "gcr.io/project-id/myimage", + expectedTag: "mytag", + }, + { + description: "image name without tag defaults to latest", + input: "gcr.io/project-id/myimage", + expectedName: "gcr.io/project-id/myimage", + expectedTag: "latest", + }, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + name, tag := splitImageNameAndTag(test.input) + t.CheckDeepEqual(test.expectedName, name) + t.CheckDeepEqual(test.expectedTag, tag) + }) + } +} diff --git a/pkg/skaffold/build/gcb/spec.go b/pkg/skaffold/build/gcb/spec.go index cf51f9f9663..b13d292672a 100644 --- a/pkg/skaffold/build/gcb/spec.go +++ b/pkg/skaffold/build/gcb/spec.go @@ -76,6 +76,9 @@ func (b *Builder) buildSpecForArtifact(ctx context.Context, a *latest.Artifact, } return b.buildpackBuildSpec(a.BuildpackArtifact, tag, a.Dependencies) + case a.KoArtifact != nil: + return b.koBuildSpec(ctx, a, tag, platforms) + default: return cloudbuild.Build{}, fmt.Errorf("unexpected type %q for gcb artifact:\n%s", misc.ArtifactType(a), misc.FormatArtifact(a)) } diff --git a/pkg/skaffold/build/misc/artifact_type.go b/pkg/skaffold/build/misc/artifact_type.go index 0bdd701d55b..5ebe69df0ac 100644 --- a/pkg/skaffold/build/misc/artifact_type.go +++ b/pkg/skaffold/build/misc/artifact_type.go @@ -31,6 +31,7 @@ const ( Jib = "jib" Custom = "custom" Buildpack = "buildpack" + Ko = "ko" ) // ArtifactType returns a string representing the type found in an artifact. Used for error messages. @@ -49,6 +50,8 @@ func ArtifactType(a *latest.Artifact) string { return Custom case a.BuildpackArtifact != nil: return Buildpack + case a.KoArtifact != nil: + return Ko default: return "" } diff --git a/pkg/skaffold/schema/defaults/defaults.go b/pkg/skaffold/schema/defaults/defaults.go index 4cbb0e86df3..17b9fff0df4 100644 --- a/pkg/skaffold/schema/defaults/defaults.go +++ b/pkg/skaffold/schema/defaults/defaults.go @@ -38,6 +38,7 @@ const ( defaultCloudBuildGradleImage = "gcr.io/cloud-builders/gradle" defaultCloudBuildKanikoImage = kaniko.DefaultImage defaultCloudBuildPackImage = "gcr.io/k8s-skaffold/pack" + defaultCloudBuildKoImage = "gcr.io/k8s-skaffold/skaffold" ) // Set makes sure default values are set on a SkaffoldConfig. @@ -88,6 +89,7 @@ func Set(c *latest.SkaffoldConfig) error { setDefaultCloudBuildGradleImage, setDefaultCloudBuildKanikoImage, setDefaultCloudBuildPackImage, + setDefaultCloudBuildKoImage, ) if err := withClusterConfig(c, @@ -193,6 +195,10 @@ func setDefaultCloudBuildPackImage(gcb *latest.GoogleCloudBuild) { gcb.PackImage = valueOrDefault(gcb.PackImage, defaultCloudBuildPackImage) } +func setDefaultCloudBuildKoImage(gcb *latest.GoogleCloudBuild) { + gcb.KoImage = valueOrDefault(gcb.KoImage, defaultCloudBuildKoImage) +} + func setDefaultTagger(c *latest.SkaffoldConfig) { if c.Build.TagPolicy != (latest.TagPolicy{}) { return diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 2e91325a310..4f317d3e093 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -384,6 +384,12 @@ type GoogleCloudBuild struct { // Defaults to `gcr.io/k8s-skaffold/pack`. PackImage string `yaml:"packImage,omitempty"` + // KoImage is the image that runs a ko build. + // The image must contain Skaffold, Go, and a shell (runnable as `sh`) that supports here documents. + // See [Cloud Builders](https://cloud.google.com/cloud-build/docs/cloud-builders). + // Defaults to `gcr.io/k8s-skaffold/skaffold`. + KoImage string `yaml:"koImage,omitempty"` + // Concurrency is how many artifacts can be built concurrently. 0 means "no-limit". // Defaults to `0`. Concurrency int `yaml:"concurrency,omitempty"` diff --git a/pkg/skaffold/schema/profiles_test.go b/pkg/skaffold/schema/profiles_test.go index d511cbd884b..16dc9bbf8be 100644 --- a/pkg/skaffold/schema/profiles_test.go +++ b/pkg/skaffold/schema/profiles_test.go @@ -136,6 +136,7 @@ func TestApplyProfiles(t *testing.T) { GradleImage: "gcr.io/cloud-builders/gradle", KanikoImage: kaniko.DefaultImage, PackImage: "gcr.io/k8s-skaffold/pack", + KoImage: "gcr.io/k8s-skaffold/skaffold", }, }, }, diff --git a/pkg/skaffold/schema/validation/validation.go b/pkg/skaffold/schema/validation/validation.go index 25e70ecbd92..57bc0e5acc4 100644 --- a/pkg/skaffold/schema/validation/validation.go +++ b/pkg/skaffold/schema/validation/validation.go @@ -612,7 +612,7 @@ func validateArtifactTypes(cfg *parser.SkaffoldConfigEntry, bc latest.BuildConfi case bc.GoogleCloudBuild != nil: for i, a := range bc.Artifacts { at := misc.ArtifactType(a) - if at != misc.Kaniko && at != misc.Docker && at != misc.Jib && at != misc.Buildpack { + if at != misc.Kaniko && at != misc.Docker && at != misc.Jib && at != misc.Buildpack && at != misc.Ko { cfgErrs = append(cfgErrs, ErrorWithLocation{ Error: fmt.Errorf("found a '%s' artifact, which is incompatible with the 'gcb' builder:\n\n%s\n\nTo use the '%s' builder, remove the 'googleCloudBuild' stanza from the 'build' section of your configuration. For information, see https://skaffold.dev/docs/pipeline-stages/builders/", misc.ArtifactType(a), misc.FormatArtifact(a), misc.ArtifactType(a)), Location: cfg.YAMLInfos.Locate(&cfg.Build.Artifacts[i].ArtifactType), diff --git a/pkg/skaffold/schema/versions_test.go b/pkg/skaffold/schema/versions_test.go index 6a8ef662c40..cd0b991d239 100644 --- a/pkg/skaffold/schema/versions_test.go +++ b/pkg/skaffold/schema/versions_test.go @@ -499,6 +499,7 @@ func withGoogleCloudBuild(id string, ops ...func(*latest.BuildConfig)) func(*lat GradleImage: "gcr.io/cloud-builders/gradle", KanikoImage: kaniko.DefaultImage, PackImage: "gcr.io/k8s-skaffold/pack", + KoImage: "gcr.io/k8s-skaffold/skaffold", }}} for _, op := range ops { op(&b)