From dd35ad8be74f52e29e1b843860a0cce9affc3532 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Mon, 22 Apr 2019 12:00:00 -0700 Subject: [PATCH 01/17] wip --- integration/examples/bazel/build.sh | 6 +++++ integration/examples/bazel/skaffold.yaml | 9 ++++++-- pkg/skaffold/build/local/custom.go | 28 ++++++++++++++++++++++++ pkg/skaffold/build/local/local.go | 2 ++ pkg/skaffold/schema/latest/config.go | 16 ++++++++++++++ 5 files changed, 59 insertions(+), 2 deletions(-) create mode 100755 integration/examples/bazel/build.sh create mode 100644 pkg/skaffold/build/local/custom.go diff --git a/integration/examples/bazel/build.sh b/integration/examples/bazel/build.sh new file mode 100755 index 00000000000..eac220e21de --- /dev/null +++ b/integration/examples/bazel/build.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +bazel build //:skaffold_example.tar +docker load -i /private/var/tmp/_bazel_priyawadhwa/84207b180d3c99eceb0884a6401eb421/execroot/skaffold/bazel-out/darwin-fastbuild/bin/skaffold_example.tar +docker tag bazel:skaffold_example $IMAGE_NAME +docker push $IMAGE_NAME diff --git a/integration/examples/bazel/skaffold.yaml b/integration/examples/bazel/skaffold.yaml index 31aacc5d583..98004690694 100644 --- a/integration/examples/bazel/skaffold.yaml +++ b/integration/examples/bazel/skaffold.yaml @@ -3,5 +3,10 @@ kind: Config build: artifacts: - image: gcr.io/k8s-skaffold/skaffold-bazel - bazel: - target: //:skaffold_example.tar + custom: + buildCommand: ./build.sh + dependencies: + paths: + - . + # bazel: + # target: //:skaffold_example.tar diff --git a/pkg/skaffold/build/local/custom.go b/pkg/skaffold/build/local/custom.go new file mode 100644 index 00000000000..508166b9f0e --- /dev/null +++ b/pkg/skaffold/build/local/custom.go @@ -0,0 +1,28 @@ +/* +Copyright 2019 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 local + +import ( + "context" + "io" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" +) + +func (b *Builder) buildCustom(ctx context.Context, out io.Writer, a *latest.Artifact, tag string) (string, error) { + return "soemthing", nil +} diff --git a/pkg/skaffold/build/local/local.go b/pkg/skaffold/build/local/local.go index 36e42082413..f41c015a9d1 100644 --- a/pkg/skaffold/build/local/local.go +++ b/pkg/skaffold/build/local/local.go @@ -96,6 +96,8 @@ func (b *Builder) runBuildForArtifact(ctx context.Context, out io.Writer, artifa case artifact.JibGradleArtifact != nil: return b.buildJibGradle(ctx, out, artifact.Workspace, artifact.JibGradleArtifact, tag) + case artifact.CustomArtifact != nil: + return b.buildCustom(ctx, out, artifact, tag) default: return "", fmt.Errorf("undefined artifact type: %+v", artifact.ArtifactType) } diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 6a300d92332..3e71b4b0a3e 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -576,6 +576,22 @@ type ArtifactType struct { // KanikoArtifact *alpha* builds images using [kaniko](https://github.com/GoogleContainerTools/kaniko). KanikoArtifact *KanikoArtifact `yaml:"kaniko,omitempty" yamltags:"oneOf=artifact"` + + // CustomArtifact *alpha* builds images using a custom build script written by the user + CustomArtifact *CustomArtifact `yaml:"custom,omitempty" yamltags:"oneOf=artifact"` +} + +// CustomArtifact *alpha* describes an artifact built from a custom build script +// written by the user. It can be used to build images with builders that aren't directly integrated with skaffold. +type CustomArtifact struct { + BuildCommand string `yaml:"buildCommand,omitempty"` + Dependencies *CustomDependencies `yaml:"dependencies,omitempty"` +} + +// CustomDependencies is used to specify dependencies for an artifact built by a custom build script. +type CustomDependencies struct { + Dockerfile string `yaml:"dockerfile,omitempty" yamltags:"oneOf=dependency"` + Paths []string `yaml:"paths,omitempty" yamltags:"oneOf=dependency"` } // KanikoArtifact *alpha* describes an artifact built from a Dockerfile, From 257181e9be4cfd6c6bf7a953960d25474fe4e367 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Mon, 22 Apr 2019 14:50:56 -0700 Subject: [PATCH 02/17] prototype for custom builders works --- integration/examples/bazel/build.sh | 6 --- integration/examples/bazel/skaffold.yaml | 9 +--- integration/examples/custom/BUILD | 9 ++++ integration/examples/custom/WORKSPACE | 30 +++++++++++++ integration/examples/custom/build.sh | 6 +++ integration/examples/custom/k8s/k8s-pod.yaml | 8 ++++ integration/examples/custom/main.go | 13 ++++++ integration/examples/custom/skaffold.yaml | 13 ++++++ pkg/skaffold/build/custom/dependencies.go | 46 ++++++++++++++++++++ pkg/skaffold/build/local/custom.go | 29 +++++++++++- pkg/skaffold/build/local/local.go | 4 ++ pkg/skaffold/constants/constants.go | 5 +++ pkg/skaffold/docker/parse.go | 44 +++++++++++-------- pkg/skaffold/schema/latest/config.go | 3 ++ 14 files changed, 193 insertions(+), 32 deletions(-) delete mode 100755 integration/examples/bazel/build.sh create mode 100644 integration/examples/custom/BUILD create mode 100644 integration/examples/custom/WORKSPACE create mode 100755 integration/examples/custom/build.sh create mode 100644 integration/examples/custom/k8s/k8s-pod.yaml create mode 100644 integration/examples/custom/main.go create mode 100644 integration/examples/custom/skaffold.yaml create mode 100644 pkg/skaffold/build/custom/dependencies.go diff --git a/integration/examples/bazel/build.sh b/integration/examples/bazel/build.sh deleted file mode 100755 index eac220e21de..00000000000 --- a/integration/examples/bazel/build.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -bazel build //:skaffold_example.tar -docker load -i /private/var/tmp/_bazel_priyawadhwa/84207b180d3c99eceb0884a6401eb421/execroot/skaffold/bazel-out/darwin-fastbuild/bin/skaffold_example.tar -docker tag bazel:skaffold_example $IMAGE_NAME -docker push $IMAGE_NAME diff --git a/integration/examples/bazel/skaffold.yaml b/integration/examples/bazel/skaffold.yaml index 98004690694..31aacc5d583 100644 --- a/integration/examples/bazel/skaffold.yaml +++ b/integration/examples/bazel/skaffold.yaml @@ -3,10 +3,5 @@ kind: Config build: artifacts: - image: gcr.io/k8s-skaffold/skaffold-bazel - custom: - buildCommand: ./build.sh - dependencies: - paths: - - . - # bazel: - # target: //:skaffold_example.tar + bazel: + target: //:skaffold_example.tar diff --git a/integration/examples/custom/BUILD b/integration/examples/custom/BUILD new file mode 100644 index 00000000000..ba00afe13d3 --- /dev/null +++ b/integration/examples/custom/BUILD @@ -0,0 +1,9 @@ +load("@io_bazel_rules_docker//go:image.bzl", "go_image") + +go_image( + name = "skaffold_example", + srcs = ["main.go"], + goos = "linux", + goarch = "amd64", + static = "on", +) diff --git a/integration/examples/custom/WORKSPACE b/integration/examples/custom/WORKSPACE new file mode 100644 index 00000000000..6f90ba50199 --- /dev/null +++ b/integration/examples/custom/WORKSPACE @@ -0,0 +1,30 @@ +workspace(name = "skaffold") + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "io_bazel_rules_docker", + strip_prefix = "rules_docker-0.7.0", + urls = ["https://github.com/bazelbuild/rules_docker/archive/v0.7.0.tar.gz"], + sha256 = "aed1c249d4ec8f703edddf35cbe9dfaca0b5f5ea6e4cd9e83e99f3b0d1136c3d", +) + +http_archive( + name = "io_bazel_rules_go", + urls = ["https://github.com/bazelbuild/rules_go/releases/download/0.16.5/rules_go-0.16.5.tar.gz"], + sha256 = "7be7dc01f1e0afdba6c8eb2b43d2fa01c743be1b9273ab1eaf6c233df078d705", +) + +load("@io_bazel_rules_go//go:def.bzl", "go_rules_dependencies", "go_register_toolchains") + +go_rules_dependencies() +go_register_toolchains( + go_version = "1.10.1", +) + +load( + "@io_bazel_rules_docker//go:image.bzl", + _go_image_repos = "repositories", +) + +_go_image_repos() \ No newline at end of file diff --git a/integration/examples/custom/build.sh b/integration/examples/custom/build.sh new file mode 100755 index 00000000000..e2f348cbd92 --- /dev/null +++ b/integration/examples/custom/build.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +bazel build //:skaffold_example.tar +docker load -i /usr/local/google/home/priyawadhwa/.cache/bazel/_bazel_priyawadhwa/70bf527ef4c26d952e28ad531f67ba5f/execroot/skaffold/bazel-out/k8-fastbuild/bin/skaffold_example.tar +docker tag bazel:skaffold_example $IMAGE_NAME +docker push $IMAGE_NAME diff --git a/integration/examples/custom/k8s/k8s-pod.yaml b/integration/examples/custom/k8s/k8s-pod.yaml new file mode 100644 index 00000000000..2ecd153fb4f --- /dev/null +++ b/integration/examples/custom/k8s/k8s-pod.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Pod +metadata: + name: bazel +spec: + containers: + - name: bazel + image: gcr.io/k8s-skaffold/skaffold-bazel diff --git a/integration/examples/custom/main.go b/integration/examples/custom/main.go new file mode 100644 index 00000000000..340af68a97f --- /dev/null +++ b/integration/examples/custom/main.go @@ -0,0 +1,13 @@ +package main + +import ( + "fmt" + "time" +) + +func main() { + for { + fmt.Println("Hello bazel!S!") + time.Sleep(time.Second * 1) + } +} diff --git a/integration/examples/custom/skaffold.yaml b/integration/examples/custom/skaffold.yaml new file mode 100644 index 00000000000..fcdf2287e8b --- /dev/null +++ b/integration/examples/custom/skaffold.yaml @@ -0,0 +1,13 @@ +apiVersion: skaffold/v1beta9 +kind: Config +build: + artifacts: + - image: gcr.io/k8s-skaffold/skaffold-bazel + custom: + buildCommand: ./build.sh + dependencies: + paths: + - . + ignore: + - bazel* + - README* diff --git a/pkg/skaffold/build/custom/dependencies.go b/pkg/skaffold/build/custom/dependencies.go new file mode 100644 index 00000000000..f9ab6bdf012 --- /dev/null +++ b/pkg/skaffold/build/custom/dependencies.go @@ -0,0 +1,46 @@ +/* +Copyright 2019 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 custom + +import ( + "context" + "sort" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/pkg/errors" +) + +// GetDependencies returns dependencies listed for a custom artifact +func GetDependencies(ctx context.Context, workspace string, a *latest.CustomArtifact) ([]string, error) { + switch { + case a.Dependencies.Dockerfile != "": + return docker.GetDependencies(ctx, workspace, a.Dependencies.Dockerfile, nil, nil) + + default: + files, err := docker.WalkWorkspace(workspace, a.Dependencies.Ignore, a.Dependencies.Paths) + if err != nil { + return nil, errors.Wrapf(err, "walking workspace %s", workspace) + } + var dependencies []string + for file := range files { + dependencies = append(dependencies, file) + } + sort.Strings(dependencies) + return dependencies, nil + } +} diff --git a/pkg/skaffold/build/local/custom.go b/pkg/skaffold/build/local/custom.go index 508166b9f0e..030b5e347a8 100644 --- a/pkg/skaffold/build/local/custom.go +++ b/pkg/skaffold/build/local/custom.go @@ -18,11 +18,38 @@ package local import ( "context" + "fmt" "io" + "os" + "os/exec" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/pkg/errors" ) func (b *Builder) buildCustom(ctx context.Context, out io.Writer, a *latest.Artifact, tag string) (string, error) { - return "soemthing", nil + artifact := a.CustomArtifact + cmd := exec.Command(artifact.BuildCommand) + cmd.Env = retrieveEnv(tag) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return "", errors.Wrapf(err, "building image with command %s", cmd.Args) + } + + if b.pushImages { + return docker.RemoteDigest(tag, b.insecureRegistries) + } + + return b.localDocker.ImageID(ctx, tag) +} + +// TODO: priyawadhwa@ to write unit tests for this +func retrieveEnv(tag string) []string { + tags := []string{ + fmt.Sprintf("%s=%s", constants.ImageName, tag), + } + return append(tags, os.Environ()...) } diff --git a/pkg/skaffold/build/local/local.go b/pkg/skaffold/build/local/local.go index 850e9955935..7644f96749c 100644 --- a/pkg/skaffold/build/local/local.go +++ b/pkg/skaffold/build/local/local.go @@ -24,6 +24,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/bazel" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/custom" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/tag" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/color" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" @@ -122,6 +123,9 @@ func (b *Builder) DependenciesForArtifact(ctx context.Context, a *latest.Artifac case a.JibGradleArtifact != nil: paths, err = jib.GetDependenciesGradle(ctx, a.Workspace, a.JibGradleArtifact) + case a.CustomArtifact != nil: + paths, err = custom.GetDependencies(ctx, a.Workspace, a.CustomArtifact) + default: return nil, fmt.Errorf("undefined artifact type: %+v", a.ArtifactType) } diff --git a/pkg/skaffold/constants/constants.go b/pkg/skaffold/constants/constants.go index bcff0e2eca8..ba0876a2e3a 100644 --- a/pkg/skaffold/constants/constants.go +++ b/pkg/skaffold/constants/constants.go @@ -81,6 +81,11 @@ var ( Local latest.ExecEnvironment = "local" ) +var ( + // ImageName is the env variable that will pass the tagged image name to custom builders + ImageName = "IMAGE_NAME" +) + var DefaultKubectlManifests = []string{"k8s/*.yaml"} var LatestDownloadURL = fmt.Sprintf("https://storage.googleapis.com/skaffold/releases/latest/skaffold-%s-%s", runtime.GOOS, runtime.GOARCH) diff --git a/pkg/skaffold/docker/parse.go b/pkg/skaffold/docker/parse.go index 4c3178ff168..87cf24c2ffe 100644 --- a/pkg/skaffold/docker/parse.go +++ b/pkg/skaffold/docker/parse.go @@ -283,6 +283,31 @@ func GetDependencies(ctx context.Context, workspace string, dockerfilePath strin } } + files, err := WalkWorkspace(workspace, excludes, deps) + if err != nil { + return nil, errors.Wrap(err, "walking workspace") + } + + // Always add dockerfile even if it's .dockerignored. The daemon will need it anyways. + if !filepath.IsAbs(dockerfilePath) { + files[dockerfilePath] = true + } else { + files[absDockerfilePath] = true + } + + // Ignore .dockerignore + delete(files, ".dockerignore") + + var dependencies []string + for file := range files { + dependencies = append(dependencies, file) + } + sort.Strings(dependencies) + + return dependencies, nil +} + +func WalkWorkspace(workspace string, excludes, deps []string) (map[string]bool, error) { pExclude, err := fileutils.NewPatternMatcher(excludes) if err != nil { return nil, errors.Wrap(err, "invalid exclude patterns") @@ -342,24 +367,7 @@ func GetDependencies(ctx context.Context, workspace string, dockerfilePath strin } } } - - // Always add dockerfile even if it's .dockerignored. The daemon will need it anyways. - if !filepath.IsAbs(dockerfilePath) { - files[dockerfilePath] = true - } else { - files[absDockerfilePath] = true - } - - // Ignore .dockerignore - delete(files, ".dockerignore") - - var dependencies []string - for file := range files { - dependencies = append(dependencies, file) - } - sort.Strings(dependencies) - - return dependencies, nil + return files, nil } func retrieveImage(image string, insecureRegistries map[string]bool) (*v1.ConfigFile, error) { diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index d976e190e95..62fcd8bbd74 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -596,6 +596,9 @@ type CustomDependencies struct { Dockerfile string `yaml:"dockerfile,omitempty" yamltags:"oneOf=dependency"` // Paths should be set to the dependencies for this artifact if no Dockerfile exists. Paths []string `yaml:"paths,omitempty" yamltags:"oneOf=dependency"` + // Ignore specifies the paths that should be ignored by the watcher. If a file exists in `paths` and in `ignore`, it will be ignored. + // Will only work in conjunction with `paths`. + Ignore []string `yaml:"ignore,omitempty"` } // KanikoArtifact *alpha* describes an artifact built from a Dockerfile, From 0c51b3b55afb8fb2596b6adc7b7294e4af22d449 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Mon, 22 Apr 2019 15:09:11 -0700 Subject: [PATCH 03/17] Make build script more general, add bazel directories to gitignore --- .gitignore | 1 + integration/examples/custom/build.sh | 3 ++- integration/examples/custom/main.go | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 3515a1def5b..937bc8545f7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ out/ examples/bazel/bazel-* integration/examples/bazel/bazel-* +integration/examples/custom/bazel-* integration/testdata/plugin/local/bazel/bazel-* *.new *.iml diff --git a/integration/examples/custom/build.sh b/integration/examples/custom/build.sh index e2f348cbd92..c88bead95d7 100755 --- a/integration/examples/custom/build.sh +++ b/integration/examples/custom/build.sh @@ -1,6 +1,7 @@ #!/bin/bash bazel build //:skaffold_example.tar -docker load -i /usr/local/google/home/priyawadhwa/.cache/bazel/_bazel_priyawadhwa/70bf527ef4c26d952e28ad531f67ba5f/execroot/skaffold/bazel-out/k8-fastbuild/bin/skaffold_example.tar +TAR_PATH=$(bazel info bazel-bin) +docker load -i $TAR_PATH/skaffold_example.tar docker tag bazel:skaffold_example $IMAGE_NAME docker push $IMAGE_NAME diff --git a/integration/examples/custom/main.go b/integration/examples/custom/main.go index 340af68a97f..9d01ff7dfbb 100644 --- a/integration/examples/custom/main.go +++ b/integration/examples/custom/main.go @@ -7,7 +7,7 @@ import ( func main() { for { - fmt.Println("Hello bazel!S!") + fmt.Println("Hello bazel!") time.Sleep(time.Second * 1) } } From 8b90ffb252f9bab363f8f311f3fe8cb8289b799f Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Wed, 24 Apr 2019 11:42:27 -0700 Subject: [PATCH 04/17] remove dockerfile dependency for now --- pkg/skaffold/build/custom/dependencies.go | 24 +++++++++-------------- pkg/skaffold/schema/latest/config.go | 2 -- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/pkg/skaffold/build/custom/dependencies.go b/pkg/skaffold/build/custom/dependencies.go index f9ab6bdf012..56fdf65e4c2 100644 --- a/pkg/skaffold/build/custom/dependencies.go +++ b/pkg/skaffold/build/custom/dependencies.go @@ -27,20 +27,14 @@ import ( // GetDependencies returns dependencies listed for a custom artifact func GetDependencies(ctx context.Context, workspace string, a *latest.CustomArtifact) ([]string, error) { - switch { - case a.Dependencies.Dockerfile != "": - return docker.GetDependencies(ctx, workspace, a.Dependencies.Dockerfile, nil, nil) - - default: - files, err := docker.WalkWorkspace(workspace, a.Dependencies.Ignore, a.Dependencies.Paths) - if err != nil { - return nil, errors.Wrapf(err, "walking workspace %s", workspace) - } - var dependencies []string - for file := range files { - dependencies = append(dependencies, file) - } - sort.Strings(dependencies) - return dependencies, nil + files, err := docker.WalkWorkspace(workspace, a.Dependencies.Ignore, a.Dependencies.Paths) + if err != nil { + return nil, errors.Wrapf(err, "walking workspace %s", workspace) + } + var dependencies []string + for file := range files { + dependencies = append(dependencies, file) } + sort.Strings(dependencies) + return dependencies, nil } diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 62fcd8bbd74..59e95d351f7 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -592,8 +592,6 @@ type CustomArtifact struct { // CustomDependencies is used to specify dependencies for an artifact built by a custom build script. type CustomDependencies struct { - // Dockerfile can be set if the custom builder uses a Dockerfile to build images. - Dockerfile string `yaml:"dockerfile,omitempty" yamltags:"oneOf=dependency"` // Paths should be set to the dependencies for this artifact if no Dockerfile exists. Paths []string `yaml:"paths,omitempty" yamltags:"oneOf=dependency"` // Ignore specifies the paths that should be ignored by the watcher. If a file exists in `paths` and in `ignore`, it will be ignored. From 7b2ec3bf42818d03e71976ea8c34467100f4fb0c Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Wed, 24 Apr 2019 11:51:20 -0700 Subject: [PATCH 05/17] add unit test for retrieving environ --- docs/content/en/schemas/v1beta9.json | 16 ++++--- pkg/skaffold/build/local/custom.go | 8 +++- pkg/skaffold/build/local/custom_test.go | 59 +++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 pkg/skaffold/build/local/custom_test.go diff --git a/docs/content/en/schemas/v1beta9.json b/docs/content/en/schemas/v1beta9.json index 84ae7b82197..093a9ffd7ab 100755 --- a/docs/content/en/schemas/v1beta9.json +++ b/docs/content/en/schemas/v1beta9.json @@ -684,10 +684,14 @@ }, "CustomDependencies": { "properties": { - "dockerfile": { - "type": "string", - "description": "can be set if the custom builder uses a Dockerfile to build images.", - "x-intellij-html-description": "can be set if the custom builder uses a Dockerfile to build images." + "ignore": { + "items": { + "type": "string" + }, + "type": "array", + "description": "specifies the paths that should be ignored by the watcher. If a file exists in `paths` and in `ignore`, it will be ignored. Will only work in conjunction with `paths`.", + "x-intellij-html-description": "specifies the paths that should be ignored by the watcher. If a file exists in paths and in ignore, it will be ignored. Will only work in conjunction with paths.", + "default": "[]" }, "paths": { "items": { @@ -700,8 +704,8 @@ } }, "preferredOrder": [ - "dockerfile", - "paths" + "paths", + "ignore" ], "additionalProperties": false, "description": "used to specify dependencies for an artifact built by a custom build script.", diff --git a/pkg/skaffold/build/local/custom.go b/pkg/skaffold/build/local/custom.go index 030b5e347a8..b465bbca969 100644 --- a/pkg/skaffold/build/local/custom.go +++ b/pkg/skaffold/build/local/custom.go @@ -29,6 +29,11 @@ import ( "github.com/pkg/errors" ) +var ( + // For testing + environ = os.Environ +) + func (b *Builder) buildCustom(ctx context.Context, out io.Writer, a *latest.Artifact, tag string) (string, error) { artifact := a.CustomArtifact cmd := exec.Command(artifact.BuildCommand) @@ -46,10 +51,9 @@ func (b *Builder) buildCustom(ctx context.Context, out io.Writer, a *latest.Arti return b.localDocker.ImageID(ctx, tag) } -// TODO: priyawadhwa@ to write unit tests for this func retrieveEnv(tag string) []string { tags := []string{ fmt.Sprintf("%s=%s", constants.ImageName, tag), } - return append(tags, os.Environ()...) + return append(tags, environ()...) } diff --git a/pkg/skaffold/build/local/custom_test.go b/pkg/skaffold/build/local/custom_test.go new file mode 100644 index 00000000000..ffcd563fee1 --- /dev/null +++ b/pkg/skaffold/build/local/custom_test.go @@ -0,0 +1,59 @@ +/* +Copyright 2019 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 local + +import ( + "testing" + + "github.com/GoogleContainerTools/skaffold/testutil" +) + +func TestRetrieveEnv(t *testing.T) { + tests := []struct { + description string + tag string + environ []string + expected []string + }{ + + { + description: "make sure tags are correct", + tag: "gcr.io/image/tag:mytag", + environ: nil, + expected: []string{"IMAGE_NAME=gcr.io/image/tag:mytag"}, + }, { + description: "make sure environ is correctly applied", + tag: "gcr.io/image/tag:anothertag", + environ: []string{"PATH=/path", "HOME=/root"}, + expected: []string{"IMAGE_NAME=gcr.io/image/tag:anothertag", "PATH=/path", "HOME=/root"}, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + initial := environ + defer func() { + environ = initial + }() + environ = func() []string { + return test.environ + } + actual := retrieveEnv(test.tag) + testutil.CheckErrorAndDeepEqual(t, false, nil, test.expected, actual) + }) + } +} From 6b3cd5421d81cb070c4a2fb91a14cf1845b34f68 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Wed, 24 Apr 2019 15:20:48 -0700 Subject: [PATCH 06/17] get started making sure it works with minikube --- integration/examples/custom/build.sh | 6 +++++- pkg/skaffold/build/local/custom.go | 5 +++-- pkg/skaffold/constants/constants.go | 3 +++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/integration/examples/custom/build.sh b/integration/examples/custom/build.sh index c88bead95d7..0bebf2f968a 100755 --- a/integration/examples/custom/build.sh +++ b/integration/examples/custom/build.sh @@ -4,4 +4,8 @@ bazel build //:skaffold_example.tar TAR_PATH=$(bazel info bazel-bin) docker load -i $TAR_PATH/skaffold_example.tar docker tag bazel:skaffold_example $IMAGE_NAME -docker push $IMAGE_NAME + +if [[ $PUSH_IMAGE = "true" ]] +then + docker push $IMAGE_NAME +fi diff --git a/pkg/skaffold/build/local/custom.go b/pkg/skaffold/build/local/custom.go index b465bbca969..69b927938b4 100644 --- a/pkg/skaffold/build/local/custom.go +++ b/pkg/skaffold/build/local/custom.go @@ -37,7 +37,7 @@ var ( func (b *Builder) buildCustom(ctx context.Context, out io.Writer, a *latest.Artifact, tag string) (string, error) { artifact := a.CustomArtifact cmd := exec.Command(artifact.BuildCommand) - cmd.Env = retrieveEnv(tag) + cmd.Env = retrieveEnv(tag, b.pushImages) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { @@ -51,9 +51,10 @@ func (b *Builder) buildCustom(ctx context.Context, out io.Writer, a *latest.Arti return b.localDocker.ImageID(ctx, tag) } -func retrieveEnv(tag string) []string { +func retrieveEnv(tag string, pushImage bool) []string { tags := []string{ fmt.Sprintf("%s=%s", constants.ImageName, tag), + fmt.Sprintf("%s=%s", constants.PushImage, pushImage), } return append(tags, environ()...) } diff --git a/pkg/skaffold/constants/constants.go b/pkg/skaffold/constants/constants.go index ba0876a2e3a..58b4687e7cf 100644 --- a/pkg/skaffold/constants/constants.go +++ b/pkg/skaffold/constants/constants.go @@ -84,6 +84,9 @@ var ( var ( // ImageName is the env variable that will pass the tagged image name to custom builders ImageName = "IMAGE_NAME" + + // PushImage lets the custom builder know if the image is expected to be pushed to a remote registry + PushImage = "PUSH_IMAGE" ) var DefaultKubectlManifests = []string{"k8s/*.yaml"} From 95babe2bec5e6016d3b7b7698bae3827008f6ac9 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Thu, 25 Apr 2019 11:59:54 -0700 Subject: [PATCH 07/17] Make sure custom artifact works w/ minikube Pass in docker env variables so that built images are built directly into minikube. Also add to the unit test and update the build script to only push images if necessary. --- integration/examples/custom/build.sh | 3 ++- integration/examples/custom/k8s/k8s-pod.yaml | 4 ++-- pkg/skaffold/build/local/custom.go | 7 ++++--- pkg/skaffold/build/local/custom_test.go | 18 +++++++++++++++--- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/integration/examples/custom/build.sh b/integration/examples/custom/build.sh index 0bebf2f968a..92e2ffd2acb 100755 --- a/integration/examples/custom/build.sh +++ b/integration/examples/custom/build.sh @@ -5,7 +5,8 @@ TAR_PATH=$(bazel info bazel-bin) docker load -i $TAR_PATH/skaffold_example.tar docker tag bazel:skaffold_example $IMAGE_NAME -if [[ $PUSH_IMAGE = "true" ]] + +if $PUSH_IMAGE then docker push $IMAGE_NAME fi diff --git a/integration/examples/custom/k8s/k8s-pod.yaml b/integration/examples/custom/k8s/k8s-pod.yaml index 2ecd153fb4f..c4805ad358b 100644 --- a/integration/examples/custom/k8s/k8s-pod.yaml +++ b/integration/examples/custom/k8s/k8s-pod.yaml @@ -4,5 +4,5 @@ metadata: name: bazel spec: containers: - - name: bazel - image: gcr.io/k8s-skaffold/skaffold-bazel + - name: bazel + image: gcr.io/k8s-skaffold/skaffold-bazel diff --git a/pkg/skaffold/build/local/custom.go b/pkg/skaffold/build/local/custom.go index 69b927938b4..9d24e95bb1f 100644 --- a/pkg/skaffold/build/local/custom.go +++ b/pkg/skaffold/build/local/custom.go @@ -37,7 +37,7 @@ var ( func (b *Builder) buildCustom(ctx context.Context, out io.Writer, a *latest.Artifact, tag string) (string, error) { artifact := a.CustomArtifact cmd := exec.Command(artifact.BuildCommand) - cmd.Env = retrieveEnv(tag, b.pushImages) + cmd.Env = retrieveEnv(tag, b.pushImages, b.localDocker.ExtraEnv()) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { @@ -51,10 +51,11 @@ func (b *Builder) buildCustom(ctx context.Context, out io.Writer, a *latest.Arti return b.localDocker.ImageID(ctx, tag) } -func retrieveEnv(tag string, pushImage bool) []string { +func retrieveEnv(tag string, pushImage bool, localDockerEnv []string) []string { tags := []string{ fmt.Sprintf("%s=%s", constants.ImageName, tag), - fmt.Sprintf("%s=%s", constants.PushImage, pushImage), + fmt.Sprintf("%s=%t", constants.PushImage, pushImage), } + tags = append(tags, localDockerEnv...) return append(tags, environ()...) } diff --git a/pkg/skaffold/build/local/custom_test.go b/pkg/skaffold/build/local/custom_test.go index ffcd563fee1..996d2b0dc31 100644 --- a/pkg/skaffold/build/local/custom_test.go +++ b/pkg/skaffold/build/local/custom_test.go @@ -26,7 +26,9 @@ func TestRetrieveEnv(t *testing.T) { tests := []struct { description string tag string + pushImage bool environ []string + dockerEnv []string expected []string }{ @@ -34,12 +36,22 @@ func TestRetrieveEnv(t *testing.T) { description: "make sure tags are correct", tag: "gcr.io/image/tag:mytag", environ: nil, - expected: []string{"IMAGE_NAME=gcr.io/image/tag:mytag"}, + expected: []string{"IMAGE_NAME=gcr.io/image/tag:mytag", "PUSH_IMAGE=false"}, }, { description: "make sure environ is correctly applied", tag: "gcr.io/image/tag:anothertag", environ: []string{"PATH=/path", "HOME=/root"}, - expected: []string{"IMAGE_NAME=gcr.io/image/tag:anothertag", "PATH=/path", "HOME=/root"}, + expected: []string{"IMAGE_NAME=gcr.io/image/tag:anothertag", "PUSH_IMAGE=false", "PATH=/path", "HOME=/root"}, + }, { + description: "make sure docker env is correctly applied", + tag: "gcr.io/image/docker:tag", + dockerEnv: []string{"DOCKER_API_VERSION=1.3", "DOCKER_CERT_PATH=/home/.minikube/certs"}, + expected: []string{"IMAGE_NAME=gcr.io/image/docker:tag", "PUSH_IMAGE=false", "DOCKER_API_VERSION=1.3", "DOCKER_CERT_PATH=/home/.minikube/certs"}, + }, { + description: "push image is true", + tag: "gcr.io/image/push:tag", + pushImage: true, + expected: []string{"IMAGE_NAME=gcr.io/image/push:tag", "PUSH_IMAGE=true"}, }, } @@ -52,7 +64,7 @@ func TestRetrieveEnv(t *testing.T) { environ = func() []string { return test.environ } - actual := retrieveEnv(test.tag) + actual := retrieveEnv(test.tag, test.pushImage, test.dockerEnv) testutil.CheckErrorAndDeepEqual(t, false, nil, test.expected, actual) }) } From 3ed255cb694f8f7b5f6572a1ec454b998f3385a1 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Thu, 25 Apr 2019 12:44:23 -0700 Subject: [PATCH 08/17] add custom builder integration test --- integration/run_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/integration/run_test.go b/integration/run_test.go index eb1cd5432d9..8d616af8186 100644 --- a/integration/run_test.go +++ b/integration/run_test.go @@ -104,6 +104,10 @@ func TestRun(t *testing.T) { args: []string{"-p", "gcb"}, deployments: []string{"web"}, remoteOnly: true, + }, { + description: "custom builder", + dir: "examples/custom", + pods: []string{"bazel"}, }, } From a03ca29e86aee86feb0fca34f991f9ef153182ab Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Fri, 26 Apr 2019 11:41:17 -0700 Subject: [PATCH 09/17] update schema to v1beta10, since we are adding in fields for custom builders --- docs/config.toml | 2 +- docs/content/en/schemas/v1beta10.json | 119 +++- docs/content/en/schemas/v1beta9.json | 98 --- integration/examples/bazel/skaffold.yaml | 2 +- integration/examples/custom/skaffold.yaml | 2 +- .../examples/getting-started/skaffold.yaml | 2 +- .../examples/helm-deployment/skaffold.yaml | 2 +- integration/examples/hot-reload/skaffold.yaml | 2 +- .../examples/jib-multimodule/skaffold.yaml | 2 +- integration/examples/jib/skaffold.yaml | 2 +- .../examples/kaniko-local/skaffold.yaml | 2 +- integration/examples/kaniko/skaffold.yaml | 2 +- integration/examples/kustomize/skaffold.yaml | 2 +- .../examples/microservices/skaffold.yaml | 2 +- integration/examples/nodejs/skaffold.yaml | 2 +- .../examples/react-reload/skaffold.yaml | 2 +- .../examples/structure-tests/skaffold.yaml | 2 +- .../skaffold.yaml | 2 +- integration/testdata/build/skaffold.yaml | 2 +- integration/testdata/dev/skaffold.yaml | 2 +- integration/testdata/file-sync/skaffold.yaml | 2 +- integration/testdata/jib/skaffold.yaml | 2 +- .../kaniko-microservices/skaffold.yaml | 2 +- .../testdata/kaniko-sub-folder/skaffold.yaml | 2 +- integration/testdata/tagPolicy/skaffold.yaml | 2 +- pkg/skaffold/schema/latest/config.go | 2 +- pkg/skaffold/schema/v1beta8/upgrade.go | 26 +- pkg/skaffold/schema/v1beta8/upgrade_test.go | 4 +- pkg/skaffold/schema/v1beta9/config.go | 640 ++++++++++++++++++ pkg/skaffold/schema/v1beta9/upgrade.go | 68 ++ pkg/skaffold/schema/v1beta9/upgrade_test.go | 111 +++ pkg/skaffold/schema/versions.go | 2 + 32 files changed, 971 insertions(+), 145 deletions(-) create mode 100644 pkg/skaffold/schema/v1beta9/config.go create mode 100644 pkg/skaffold/schema/v1beta9/upgrade.go create mode 100644 pkg/skaffold/schema/v1beta9/upgrade_test.go diff --git a/docs/config.toml b/docs/config.toml index 9eef3c6abec..cde2dcec16e 100644 --- a/docs/config.toml +++ b/docs/config.toml @@ -70,7 +70,7 @@ weight = 1 #copyright = "Skaffold" #privacy_policy = "https://policies.google.com/privacy" github_repo = "https://github.com/GoogleContainerTools/skaffold" -skaffold_version = "skaffold/v1beta9" +skaffold_version = "skaffold/v1beta10" # Google Custom Search Engine ID. Remove or comment out to disable search. gcs_engine_id = "013756393218025596041:3nojel67sum" diff --git a/docs/content/en/schemas/v1beta10.json b/docs/content/en/schemas/v1beta10.json index 188a9a96b48..bb391666f4c 100755 --- a/docs/content/en/schemas/v1beta10.json +++ b/docs/content/en/schemas/v1beta10.json @@ -293,6 +293,48 @@ "kaniko" ], "additionalProperties": false + }, + { + "properties": { + "context": { + "type": "string", + "description": "directory containing the artifact's sources.", + "x-intellij-html-description": "directory containing the artifact's sources.", + "default": "." + }, + "custom": { + "$ref": "#/definitions/CustomArtifact", + "description": "*alpha* builds images using a custom build script written by the user.", + "x-intellij-html-description": "alpha builds images using a custom build script written by the user." + }, + "image": { + "type": "string", + "description": "name of the image to be built.", + "x-intellij-html-description": "name of the image to be built.", + "examples": [ + "gcr.io/k8s-skaffold/example" + ] + }, + "sync": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "description": "*alpha* local files synced to pods instead of triggering an image build when modified. This is a mapping of local files to sync to remote folders.", + "x-intellij-html-description": "alpha local files synced to pods instead of triggering an image build when modified. This is a mapping of local files to sync to remote folders.", + "default": "{}", + "examples": [ + "{\"*.py\": \".\", \"css/**/*.css\": \"app/css\"}" + ] + } + }, + "preferredOrder": [ + "image", + "context", + "sync", + "custom" + ], + "additionalProperties": false } ], "description": "items that need to be built, along with the context in which they should be built.", @@ -355,8 +397,8 @@ }, "tagPolicy": { "$ref": "#/definitions/TagPolicy", - "description": "*beta* determines how images are tagged. A few strategies are provided here, although you most likely won't need to care! If not specified, it defaults to `gitCommit: {}`.", - "x-intellij-html-description": "beta determines how images are tagged. A few strategies are provided here, although you most likely won't need to care! If not specified, it defaults to gitCommit: {}." + "description": "*beta* determines how images are tagged. A few strategies are provided here, although you most likely won't need to care! If not specified, it defaults to `gitCommit: {variant: Tags}`.", + "x-intellij-html-description": "beta determines how images are tagged. A few strategies are provided here, although you most likely won't need to care! If not specified, it defaults to gitCommit: {variant: Tags}." } }, "preferredOrder": [ @@ -392,8 +434,8 @@ }, "tagPolicy": { "$ref": "#/definitions/TagPolicy", - "description": "*beta* determines how images are tagged. A few strategies are provided here, although you most likely won't need to care! If not specified, it defaults to `gitCommit: {}`.", - "x-intellij-html-description": "beta determines how images are tagged. A few strategies are provided here, although you most likely won't need to care! If not specified, it defaults to gitCommit: {}." + "description": "*beta* determines how images are tagged. A few strategies are provided here, although you most likely won't need to care! If not specified, it defaults to `gitCommit: {variant: Tags}`.", + "x-intellij-html-description": "beta determines how images are tagged. A few strategies are provided here, although you most likely won't need to care! If not specified, it defaults to gitCommit: {variant: Tags}." } }, "preferredOrder": [ @@ -430,8 +472,8 @@ }, "tagPolicy": { "$ref": "#/definitions/TagPolicy", - "description": "*beta* determines how images are tagged. A few strategies are provided here, although you most likely won't need to care! If not specified, it defaults to `gitCommit: {}`.", - "x-intellij-html-description": "beta determines how images are tagged. A few strategies are provided here, although you most likely won't need to care! If not specified, it defaults to gitCommit: {}." + "description": "*beta* determines how images are tagged. A few strategies are provided here, although you most likely won't need to care! If not specified, it defaults to `gitCommit: {variant: Tags}`.", + "x-intellij-html-description": "beta determines how images are tagged. A few strategies are provided here, although you most likely won't need to care! If not specified, it defaults to gitCommit: {variant: Tags}." } }, "preferredOrder": [ @@ -468,8 +510,8 @@ }, "tagPolicy": { "$ref": "#/definitions/TagPolicy", - "description": "*beta* determines how images are tagged. A few strategies are provided here, although you most likely won't need to care! If not specified, it defaults to `gitCommit: {}`.", - "x-intellij-html-description": "beta determines how images are tagged. A few strategies are provided here, although you most likely won't need to care! If not specified, it defaults to gitCommit: {}." + "description": "*beta* determines how images are tagged. A few strategies are provided here, although you most likely won't need to care! If not specified, it defaults to `gitCommit: {variant: Tags}`.", + "x-intellij-html-description": "beta determines how images are tagged. A few strategies are provided here, although you most likely won't need to care! If not specified, it defaults to gitCommit: {variant: Tags}." } }, "preferredOrder": [ @@ -530,6 +572,56 @@ "description": "*beta* describes how to do an on-cluster build.", "x-intellij-html-description": "beta describes how to do an on-cluster build." }, + "CustomArtifact": { + "properties": { + "buildCommand": { + "type": "string", + "description": "command executed to build the image.", + "x-intellij-html-description": "command executed to build the image." + }, + "dependencies": { + "$ref": "#/definitions/CustomDependencies", + "description": "file dependencies that skaffold should watch for this artifact.", + "x-intellij-html-description": "file dependencies that skaffold should watch for this artifact." + } + }, + "preferredOrder": [ + "buildCommand", + "dependencies" + ], + "additionalProperties": false, + "description": "*alpha* describes an artifact built from a custom build script written by the user. It can be used to build images with builders that aren't directly integrated with skaffold.", + "x-intellij-html-description": "alpha describes an artifact built from a custom build script written by the user. It can be used to build images with builders that aren't directly integrated with skaffold." + }, + "CustomDependencies": { + "properties": { + "ignore": { + "items": { + "type": "string" + }, + "type": "array", + "description": "specifies the paths that should be ignored by the watcher. If a file exists in `paths` and in `ignore`, it will be ignored. Will only work in conjunction with `paths`.", + "x-intellij-html-description": "specifies the paths that should be ignored by the watcher. If a file exists in paths and in ignore, it will be ignored. Will only work in conjunction with paths.", + "default": "[]" + }, + "paths": { + "items": { + "type": "string" + }, + "type": "array", + "description": "should be set to the dependencies for this artifact if no Dockerfile exists.", + "x-intellij-html-description": "should be set to the dependencies for this artifact if no Dockerfile exists.", + "default": "[]" + } + }, + "preferredOrder": [ + "paths", + "ignore" + ], + "additionalProperties": false, + "description": "used to specify dependencies for an artifact built by a custom build script.", + "x-intellij-html-description": "used to specify dependencies for an artifact built by a custom build script." + }, "DateTimeTagger": { "properties": { "format": { @@ -691,6 +783,17 @@ "x-intellij-html-description": "beta tags images with a configurable template string." }, "GitTagger": { + "properties": { + "variant": { + "type": "string", + "description": "determines the behavior of the git tagger. Valid variants are `Tags` (default): use git tags or fall back to abbreviated commit hash. `CommitSha`: use the full git commit sha. `AbbrevCommitSha`: use the abbreviated git commit sha.", + "x-intellij-html-description": "determines the behavior of the git tagger. Valid variants are Tags (default): use git tags or fall back to abbreviated commit hash. CommitSha: use the full git commit sha. AbbrevCommitSha: use the abbreviated git commit sha." + } + }, + "preferredOrder": [ + "variant" + ], + "additionalProperties": false, "description": "*beta* tags images with the git tag or commit of the artifact's workspace.", "x-intellij-html-description": "beta tags images with the git tag or commit of the artifact's workspace." }, diff --git a/docs/content/en/schemas/v1beta9.json b/docs/content/en/schemas/v1beta9.json index bf3ac3533f0..9c8c1876c1d 100755 --- a/docs/content/en/schemas/v1beta9.json +++ b/docs/content/en/schemas/v1beta9.json @@ -293,54 +293,6 @@ "kaniko" ], "additionalProperties": false - }, - { - "properties": { - "context": { - "type": "string", - "description": "directory containing the artifact's sources.", - "x-intellij-html-description": "directory containing the artifact's sources.", - "default": "." - }, - "custom": { - "$ref": "#/definitions/CustomArtifact", - "description": "*alpha* builds images using a custom build script written by the user.", - "x-intellij-html-description": "alpha builds images using a custom build script written by the user." - }, - "image": { - "type": "string", - "description": "name of the image to be built.", - "x-intellij-html-description": "name of the image to be built.", - "examples": [ - "gcr.io/k8s-skaffold/example" - ] - }, - "plugin": { - "$ref": "#/definitions/BuilderPlugin", - "description": "plugin used to build this artifact.", - "x-intellij-html-description": "plugin used to build this artifact." - }, - "sync": { - "additionalProperties": { - "type": "string" - }, - "type": "object", - "description": "*alpha* local files synced to pods instead of triggering an image build when modified. This is a mapping of local files to sync to remote folders.", - "x-intellij-html-description": "alpha local files synced to pods instead of triggering an image build when modified. This is a mapping of local files to sync to remote folders.", - "default": "{}", - "examples": [ - "{\"*.py\": \".\", \"css/**/*.css\": \"app/css\"}" - ] - } - }, - "preferredOrder": [ - "image", - "context", - "sync", - "plugin", - "custom" - ], - "additionalProperties": false } ], "description": "items that need to be built, along with the context in which they should be built.", @@ -578,56 +530,6 @@ "description": "*beta* describes how to do an on-cluster build.", "x-intellij-html-description": "beta describes how to do an on-cluster build." }, - "CustomArtifact": { - "properties": { - "buildCommand": { - "type": "string", - "description": "command executed to build the image.", - "x-intellij-html-description": "command executed to build the image." - }, - "dependencies": { - "$ref": "#/definitions/CustomDependencies", - "description": "file dependencies that skaffold should watch for this artifact.", - "x-intellij-html-description": "file dependencies that skaffold should watch for this artifact." - } - }, - "preferredOrder": [ - "buildCommand", - "dependencies" - ], - "additionalProperties": false, - "description": "*alpha* describes an artifact built from a custom build script written by the user. It can be used to build images with builders that aren't directly integrated with skaffold.", - "x-intellij-html-description": "alpha describes an artifact built from a custom build script written by the user. It can be used to build images with builders that aren't directly integrated with skaffold." - }, - "CustomDependencies": { - "properties": { - "ignore": { - "items": { - "type": "string" - }, - "type": "array", - "description": "specifies the paths that should be ignored by the watcher. If a file exists in `paths` and in `ignore`, it will be ignored. Will only work in conjunction with `paths`.", - "x-intellij-html-description": "specifies the paths that should be ignored by the watcher. If a file exists in paths and in ignore, it will be ignored. Will only work in conjunction with paths.", - "default": "[]" - }, - "paths": { - "items": { - "type": "string" - }, - "type": "array", - "description": "should be set to the dependencies for this artifact if no Dockerfile exists.", - "x-intellij-html-description": "should be set to the dependencies for this artifact if no Dockerfile exists.", - "default": "[]" - } - }, - "preferredOrder": [ - "paths", - "ignore" - ], - "additionalProperties": false, - "description": "used to specify dependencies for an artifact built by a custom build script.", - "x-intellij-html-description": "used to specify dependencies for an artifact built by a custom build script." - }, "DateTimeTagger": { "properties": { "format": { diff --git a/integration/examples/bazel/skaffold.yaml b/integration/examples/bazel/skaffold.yaml index 31aacc5d583..ab1f0a85340 100644 --- a/integration/examples/bazel/skaffold.yaml +++ b/integration/examples/bazel/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: artifacts: diff --git a/integration/examples/custom/skaffold.yaml b/integration/examples/custom/skaffold.yaml index fcdf2287e8b..eb53408506a 100644 --- a/integration/examples/custom/skaffold.yaml +++ b/integration/examples/custom/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: artifacts: diff --git a/integration/examples/getting-started/skaffold.yaml b/integration/examples/getting-started/skaffold.yaml index 4ba1741eae5..3eedc1153d0 100644 --- a/integration/examples/getting-started/skaffold.yaml +++ b/integration/examples/getting-started/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: artifacts: diff --git a/integration/examples/helm-deployment/skaffold.yaml b/integration/examples/helm-deployment/skaffold.yaml index 2ec83f1a6bb..62371e20026 100644 --- a/integration/examples/helm-deployment/skaffold.yaml +++ b/integration/examples/helm-deployment/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: tagPolicy: diff --git a/integration/examples/hot-reload/skaffold.yaml b/integration/examples/hot-reload/skaffold.yaml index 87b0d0ff420..0545763e6cf 100644 --- a/integration/examples/hot-reload/skaffold.yaml +++ b/integration/examples/hot-reload/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: artifacts: diff --git a/integration/examples/jib-multimodule/skaffold.yaml b/integration/examples/jib-multimodule/skaffold.yaml index 6067b71be16..bc693cf6470 100644 --- a/integration/examples/jib-multimodule/skaffold.yaml +++ b/integration/examples/jib-multimodule/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: artifacts: diff --git a/integration/examples/jib/skaffold.yaml b/integration/examples/jib/skaffold.yaml index b362c9bf3dc..affe4a915c6 100644 --- a/integration/examples/jib/skaffold.yaml +++ b/integration/examples/jib/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: artifacts: diff --git a/integration/examples/kaniko-local/skaffold.yaml b/integration/examples/kaniko-local/skaffold.yaml index 8ba0e0dd9f0..8abe6cfc04b 100644 --- a/integration/examples/kaniko-local/skaffold.yaml +++ b/integration/examples/kaniko-local/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: artifacts: diff --git a/integration/examples/kaniko/skaffold.yaml b/integration/examples/kaniko/skaffold.yaml index 56a150f65ac..f7aee47420f 100644 --- a/integration/examples/kaniko/skaffold.yaml +++ b/integration/examples/kaniko/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: artifacts: diff --git a/integration/examples/kustomize/skaffold.yaml b/integration/examples/kustomize/skaffold.yaml index cf0538f419b..dc01c6930db 100644 --- a/integration/examples/kustomize/skaffold.yaml +++ b/integration/examples/kustomize/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config deploy: kustomize: {} diff --git a/integration/examples/microservices/skaffold.yaml b/integration/examples/microservices/skaffold.yaml index 6c8072da52c..10a758fc9f5 100644 --- a/integration/examples/microservices/skaffold.yaml +++ b/integration/examples/microservices/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: artifacts: diff --git a/integration/examples/nodejs/skaffold.yaml b/integration/examples/nodejs/skaffold.yaml index 775797721ef..c46056008a5 100644 --- a/integration/examples/nodejs/skaffold.yaml +++ b/integration/examples/nodejs/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: artifacts: diff --git a/integration/examples/react-reload/skaffold.yaml b/integration/examples/react-reload/skaffold.yaml index f44b5685d56..2adfa800c88 100644 --- a/integration/examples/react-reload/skaffold.yaml +++ b/integration/examples/react-reload/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: artifacts: diff --git a/integration/examples/structure-tests/skaffold.yaml b/integration/examples/structure-tests/skaffold.yaml index c8d27422cf4..a6e566129ab 100644 --- a/integration/examples/structure-tests/skaffold.yaml +++ b/integration/examples/structure-tests/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: artifacts: diff --git a/integration/examples/tagging-with-environment-variables/skaffold.yaml b/integration/examples/tagging-with-environment-variables/skaffold.yaml index 70073d970b1..23242d5b626 100644 --- a/integration/examples/tagging-with-environment-variables/skaffold.yaml +++ b/integration/examples/tagging-with-environment-variables/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: artifacts: diff --git a/integration/testdata/build/skaffold.yaml b/integration/testdata/build/skaffold.yaml index 7a25db9d16b..a0560effb4f 100644 --- a/integration/testdata/build/skaffold.yaml +++ b/integration/testdata/build/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: local: diff --git a/integration/testdata/dev/skaffold.yaml b/integration/testdata/dev/skaffold.yaml index 04be81ac93f..80fa3a836ca 100644 --- a/integration/testdata/dev/skaffold.yaml +++ b/integration/testdata/dev/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: artifacts: diff --git a/integration/testdata/file-sync/skaffold.yaml b/integration/testdata/file-sync/skaffold.yaml index 481a6b947fe..05fa015766c 100644 --- a/integration/testdata/file-sync/skaffold.yaml +++ b/integration/testdata/file-sync/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: tagPolicy: diff --git a/integration/testdata/jib/skaffold.yaml b/integration/testdata/jib/skaffold.yaml index 66d8296cbe2..c7381a348a7 100644 --- a/integration/testdata/jib/skaffold.yaml +++ b/integration/testdata/jib/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: artifacts: diff --git a/integration/testdata/kaniko-microservices/skaffold.yaml b/integration/testdata/kaniko-microservices/skaffold.yaml index 9c8e49da650..062a6fb4748 100644 --- a/integration/testdata/kaniko-microservices/skaffold.yaml +++ b/integration/testdata/kaniko-microservices/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: artifacts: diff --git a/integration/testdata/kaniko-sub-folder/skaffold.yaml b/integration/testdata/kaniko-sub-folder/skaffold.yaml index dc3a8265edd..2536b602ab0 100644 --- a/integration/testdata/kaniko-sub-folder/skaffold.yaml +++ b/integration/testdata/kaniko-sub-folder/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: artifacts: diff --git a/integration/testdata/tagPolicy/skaffold.yaml b/integration/testdata/tagPolicy/skaffold.yaml index 35cd39e4297..4891d665061 100644 --- a/integration/testdata/tagPolicy/skaffold.yaml +++ b/integration/testdata/tagPolicy/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v1beta9 +apiVersion: skaffold/v1beta10 kind: Config build: artifacts: diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index dfb7dc262d8..db05fae9d48 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -20,7 +20,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util" ) -const Version string = "skaffold/v1beta9" +const Version string = "skaffold/v1beta10" // NewSkaffoldConfig creates a SkaffoldConfig func NewSkaffoldConfig() util.VersionedConfig { diff --git a/pkg/skaffold/schema/v1beta8/upgrade.go b/pkg/skaffold/schema/v1beta8/upgrade.go index 002e44b8910..7c699a5508b 100644 --- a/pkg/skaffold/schema/v1beta8/upgrade.go +++ b/pkg/skaffold/schema/v1beta8/upgrade.go @@ -17,8 +17,8 @@ limitations under the License. package v1beta8 import ( - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util" + next "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1beta9" pkgutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/pkg/errors" "gopkg.in/yaml.v2" @@ -32,13 +32,13 @@ import ( // 3. No updates func (config *SkaffoldConfig) Upgrade() (util.VersionedConfig, error) { // convert Deploy (should be the same) - var newDeploy latest.DeployConfig + var newDeploy next.DeployConfig if err := pkgutil.CloneThroughJSON(config.Deploy, &newDeploy); err != nil { return nil, errors.Wrap(err, "converting deploy config") } // convert Profiles (should be the same) - var newProfiles []latest.Profile + var newProfiles []next.Profile if config.Profiles != nil { if err := pkgutil.CloneThroughJSON(config.Profiles, &newProfiles); err != nil { return nil, errors.Wrap(err, "converting new profile") @@ -52,7 +52,7 @@ func (config *SkaffoldConfig) Upgrade() (util.VersionedConfig, error) { } // convert Build (should be same) - var newBuild latest.BuildConfig + var newBuild next.BuildConfig if err := pkgutil.CloneThroughJSON(config.Build, &newBuild); err != nil { return nil, errors.Wrap(err, "converting new build") } @@ -62,15 +62,15 @@ func (config *SkaffoldConfig) Upgrade() (util.VersionedConfig, error) { } // convert Test (should be the same) - var newTest []*latest.TestCase + var newTest []*next.TestCase if err := pkgutil.CloneThroughJSON(config.Test, &newTest); err != nil { return nil, errors.Wrap(err, "converting new test") } - return &latest.SkaffoldConfig{ - APIVersion: latest.Version, + return &next.SkaffoldConfig{ + APIVersion: next.Version, Kind: config.Kind, - Pipeline: latest.Pipeline{ + Pipeline: next.Pipeline{ Build: newBuild, Test: newTest, Deploy: newDeploy, @@ -79,13 +79,13 @@ func (config *SkaffoldConfig) Upgrade() (util.VersionedConfig, error) { }, nil } -func updateBuild(config *BuildConfig, newBuild *latest.BuildConfig) error { +func updateBuild(config *BuildConfig, newBuild *next.BuildConfig) error { for i, a := range config.Artifacts { if a.BuilderPlugin == nil { continue } if a.BuilderPlugin.Name == "bazel" { - var ba *latest.BazelArtifact + var ba *next.BazelArtifact contents, err := yaml.Marshal(a.BuilderPlugin.Properties) if err != nil { return errors.Wrap(err, "unmarshalling properties") @@ -97,7 +97,7 @@ func updateBuild(config *BuildConfig, newBuild *latest.BuildConfig) error { } if a.BuilderPlugin.Name == "docker" { - var da *latest.DockerArtifact + var da *next.DockerArtifact contents, err := yaml.Marshal(a.BuilderPlugin.Properties) if err != nil { return errors.Wrap(err, "unmarshalling properties") @@ -111,7 +111,7 @@ func updateBuild(config *BuildConfig, newBuild *latest.BuildConfig) error { if c := config.ExecutionEnvironment; c != nil { if c.Name == "googleCloudBuild" { - var gcb *latest.GoogleCloudBuild + var gcb *next.GoogleCloudBuild contents, err := yaml.Marshal(c.Properties) if err != nil { return errors.Wrap(err, "unmarshalling properties") @@ -122,7 +122,7 @@ func updateBuild(config *BuildConfig, newBuild *latest.BuildConfig) error { newBuild.GoogleCloudBuild = gcb } if c.Name == "local" { - var local *latest.LocalBuild + var local *next.LocalBuild contents, err := yaml.Marshal(c.Properties) if err != nil { return errors.Wrap(err, "unmarshalling properties") diff --git a/pkg/skaffold/schema/v1beta8/upgrade_test.go b/pkg/skaffold/schema/v1beta8/upgrade_test.go index b4b2c4e5529..e4f3fba2e31 100644 --- a/pkg/skaffold/schema/v1beta8/upgrade_test.go +++ b/pkg/skaffold/schema/v1beta8/upgrade_test.go @@ -19,7 +19,7 @@ package v1beta8 import ( "testing" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + next "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1beta9" "github.com/GoogleContainerTools/skaffold/testutil" yaml "gopkg.in/yaml.v2" ) @@ -152,7 +152,7 @@ func verifyUpgrade(t *testing.T, input, output string) { upgraded, err := config.Upgrade() testutil.CheckError(t, false, err) - expected := latest.NewSkaffoldConfig() + expected := next.NewSkaffoldConfig() err = yaml.UnmarshalStrict([]byte(output), expected) testutil.CheckErrorAndDeepEqual(t, false, err, expected, upgraded) diff --git a/pkg/skaffold/schema/v1beta9/config.go b/pkg/skaffold/schema/v1beta9/config.go new file mode 100644 index 00000000000..6209eba003c --- /dev/null +++ b/pkg/skaffold/schema/v1beta9/config.go @@ -0,0 +1,640 @@ +/* +Copyright 2019 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 v1beta9 + +import ( + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util" +) + +const Version string = "skaffold/v1beta9" + +// NewSkaffoldConfig creates a SkaffoldConfig +func NewSkaffoldConfig() util.VersionedConfig { + return new(SkaffoldConfig) +} + +// SkaffoldConfig holds the fields parsed from the Skaffold configuration file (skaffold.yaml). +type SkaffoldConfig struct { + // APIVersion is the version of the configuration. + APIVersion string `yaml:"apiVersion" yamltags:"required"` + + // Kind is always `Config`. Defaults to `Config`. + Kind string `yaml:"kind" yamltags:"required"` + + // Pipeline defines the Build/Test/Deploy phases. + Pipeline `yaml:",inline"` + + // Profiles *beta* can override be used to `build`, `test` or `deploy` configuration. + Profiles []Profile `yaml:"profiles,omitempty"` +} + +// Pipeline describes a Skaffold pipeline. +type Pipeline struct { + // Build describes how images are built. + Build BuildConfig `yaml:"build,omitempty"` + + // Test describes how images are tested. + Test []*TestCase `yaml:"test,omitempty"` + + // Deploy describes how images are deployed. + Deploy DeployConfig `yaml:"deploy,omitempty"` +} + +func (c *SkaffoldConfig) GetVersion() string { + return c.APIVersion +} + +// BuildConfig contains all the configuration for the build steps. +type BuildConfig struct { + // Artifacts lists the images you're going to be building. + Artifacts []*Artifact `yaml:"artifacts,omitempty"` + + // InsecureRegistries is a list of registries declared by the user to be insecure. + // These registries will be connected to via HTTP instead of HTTPS. + InsecureRegistries []string `yaml:"insecureRegistries,omitempty"` + + // TagPolicy *beta* determines how images are tagged. + // A few strategies are provided here, although you most likely won't need to care! + // If not specified, it defaults to `gitCommit: {variant: Tags}`. + TagPolicy TagPolicy `yaml:"tagPolicy,omitempty"` + + BuildType `yaml:",inline"` +} + +// TagPolicy contains all the configuration for the tagging step. +type TagPolicy struct { + // GitTagger *beta* tags images with the git tag or commit of the artifact's workspace. + GitTagger *GitTagger `yaml:"gitCommit,omitempty" yamltags:"oneOf=tag"` + + // ShaTagger *beta* tags images with their sha256 digest. + ShaTagger *ShaTagger `yaml:"sha256,omitempty" yamltags:"oneOf=tag"` + + // EnvTemplateTagger *beta* tags images with a configurable template string. + EnvTemplateTagger *EnvTemplateTagger `yaml:"envTemplate,omitempty" yamltags:"oneOf=tag"` + + // DateTimeTagger *beta* tags images with the build timestamp. + DateTimeTagger *DateTimeTagger `yaml:"dateTime,omitempty" yamltags:"oneOf=tag"` +} + +// ShaTagger *beta* tags images with their sha256 digest. +type ShaTagger struct{} + +// GitTagger *beta* tags images with the git tag or commit of the artifact's workspace. +type GitTagger struct { + // Variant determines the behavior of the git tagger. Valid variants are + // `Tags` (default): use git tags or fall back to abbreviated commit hash. + // `CommitSha`: use the full git commit sha. + // `AbbrevCommitSha`: use the abbreviated git commit sha. + Variant string `yaml:"variant,omitempty"` +} + +// EnvTemplateTagger *beta* tags images with a configurable template string. +type EnvTemplateTagger struct { + // Template used to produce the image name and tag. + // See golang [text/template](https://golang.org/pkg/text/template/). + // The template is executed against the current environment, + // with those variables injected: + // IMAGE_NAME | Name of the image being built, as supplied in the artifacts section. + // For example: `{{.RELEASE}}-{{.IMAGE_NAME}}`. + Template string `yaml:"template,omitempty" yamltags:"required"` +} + +// DateTimeTagger *beta* tags images with the build timestamp. +type DateTimeTagger struct { + // Format formats the date and time. + // See [#Time.Format](https://golang.org/pkg/time/#Time.Format). + // Defaults to `2006-01-02_15-04-05.999_MST`. + Format string `yaml:"format,omitempty"` + + // TimeZone sets the timezone for the date and time. + // See [Time.LoadLocation](https://golang.org/pkg/time/#Time.LoadLocation). + // Defaults to the local timezone. + TimeZone string `yaml:"timezone,omitempty"` +} + +// BuildType contains the specific implementation and parameters needed +// for the build step. Only one field should be populated. +type BuildType struct { + // LocalBuild *beta* describes how to do a build on the local docker daemon + // and optionally push to a repository. + LocalBuild *LocalBuild `yaml:"local,omitempty" yamltags:"oneOf=build"` + + // GoogleCloudBuild *beta* describes how to do a remote build on + // [Google Cloud Build](https://cloud.google.com/cloud-build/). + GoogleCloudBuild *GoogleCloudBuild `yaml:"googleCloudBuild,omitempty" yamltags:"oneOf=build"` + + // Cluster *beta* describes how to do an on-cluster build. + Cluster *ClusterDetails `yaml:"cluster,omitempty" yamltags:"oneOf=build"` +} + +// LocalBuild *beta* describes how to do a build on the local docker daemon +// and optionally push to a repository. +type LocalBuild struct { + // Push should images be pushed to a registry. + // If not specified, images are pushed only if the current Kubernetes context + // connects to a remote cluster. + Push *bool `yaml:"push,omitempty"` + + // UseDockerCLI use `docker` command-line interface instead of Docker Engine APIs. + UseDockerCLI bool `yaml:"useDockerCLI,omitempty"` + + // UseBuildkit use BuildKit to build Docker images. + UseBuildkit bool `yaml:"useBuildkit,omitempty"` +} + +// GoogleCloudBuild *beta* describes how to do a remote build on +// [Google Cloud Build](https://cloud.google.com/cloud-build/docs/). +// Docker and Jib artifacts can be built on Cloud Build. The `projectId` needs +// to be provided and the currently logged in user should be given permissions to trigger +// new builds. +type GoogleCloudBuild struct { + // ProjectID is the ID of your Cloud Platform Project. + // If it is not provided, Skaffold will guess it from the image name. + // For example, given the artifact image name `gcr.io/myproject/image`, Skaffold + // will use the `myproject` GCP project. + ProjectID string `yaml:"projectId,omitempty"` + + // DiskSizeGb is the disk size of the VM that runs the build. + // See [Cloud Build Reference](https://cloud.google.com/cloud-build/docs/api/reference/rest/v1/projects.builds#buildoptions). + DiskSizeGb int64 `yaml:"diskSizeGb,omitempty"` + + // MachineType is the type of the VM that runs the build. + // See [Cloud Build Reference](https://cloud.google.com/cloud-build/docs/api/reference/rest/v1/projects.builds#buildoptions). + MachineType string `yaml:"machineType,omitempty"` + + // Timeout is the amount of time (in seconds) that this build should be allowed to run. + // See [Cloud Build Reference](https://cloud.google.com/cloud-build/docs/api/reference/rest/v1/projects.builds#resource-build). + Timeout string `yaml:"timeout,omitempty"` + + // DockerImage is the image that runs a Docker build. + // See [Cloud Builders](https://cloud.google.com/cloud-build/docs/cloud-builders). + // Defaults to `gcr.io/cloud-builders/docker`. + DockerImage string `yaml:"dockerImage,omitempty"` + + // MavenImage is the image that runs a Maven build. + // See [Cloud Builders](https://cloud.google.com/cloud-build/docs/cloud-builders). + // Defaults to `gcr.io/cloud-builders/mvn`. + MavenImage string `yaml:"mavenImage,omitempty"` + + // GradleImage is the image that runs a Gradle build. + // See [Cloud Builders](https://cloud.google.com/cloud-build/docs/cloud-builders). + // Defaults to `gcr.io/cloud-builders/gradle`. + GradleImage string `yaml:"gradleImage,omitempty"` +} + +// LocalDir configures how Kaniko mounts sources directly via an `emptyDir` volume. +type LocalDir struct { + // InitImage is the image used to run init container which mounts kaniko context. + InitImage string `yaml:"initImage,omitempty"` +} + +// KanikoBuildContext contains the different fields available to specify +// a Kaniko build context. +type KanikoBuildContext struct { + // GCSBucket is the CGS bucket to which sources are uploaded. + // Kaniko will need access to that bucket to download the sources. + GCSBucket string `yaml:"gcsBucket,omitempty" yamltags:"oneOf=buildContext"` + + // LocalDir configures how Kaniko mounts sources directly via an `emptyDir` volume. + LocalDir *LocalDir `yaml:"localDir,omitempty" yamltags:"oneOf=buildContext"` +} + +// KanikoCache configures Kaniko caching. If a cache is specified, Kaniko will +// use a remote cache which will speed up builds. +type KanikoCache struct { + // Repo is a remote repository to store cached layers. If none is specified, one will be + // inferred from the image name. See [Kaniko Caching](https://github.com/GoogleContainerTools/kaniko#caching). + Repo string `yaml:"repo,omitempty"` +} + +// ClusterDetails *beta* describes how to do an on-cluster build. +type ClusterDetails struct { + // PullSecret is the path to the secret key file. + PullSecret string `yaml:"pullSecret,omitempty"` + + // PullSecretName is the name of the Kubernetes secret for pulling the files + // from the build context and pushing the final image. + // Defaults to `kaniko-secret`. + PullSecretName string `yaml:"pullSecretName,omitempty"` + + // Namespace is the Kubernetes namespace. + // Defaults to current namespace in Kubernetes configuration. + Namespace string `yaml:"namespace,omitempty"` + + // Timeout is the amount of time (in seconds) that this build is allowed to run. + // Defaults to 20 minutes (`20m`). + Timeout string `yaml:"timeout,omitempty"` + + // DockerConfig describes how to mount the local Docker configuration into a pod. + DockerConfig *DockerConfig `yaml:"dockerConfig,omitempty"` + + // Resources define the resource requirements for the kaniko pod. + Resources *ResourceRequirements `yaml:"resources,omitempty"` +} + +// DockerConfig contains information about the docker `config.json` to mount. +type DockerConfig struct { + // Path is the path to the docker `config.json`. + Path string `yaml:"path,omitempty"` + + // SecretName is the Kubernetes secret that will hold the Docker configuration. + SecretName string `yaml:"secretName,omitempty"` +} + +// ResourceRequirements describes the resource requirements for the kaniko pod. +type ResourceRequirements struct { + // Requests [resource requests](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container) for the Kaniko pod. + Requests *ResourceRequirement `yaml:"requests,omitempty"` + + // Limits [resource limits](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container) for the Kaniko pod. + Limits *ResourceRequirement `yaml:"limits,omitempty"` +} + +// ResourceRequirement stores the CPU/Memory requirements for the pod. +type ResourceRequirement struct { + // CPU the number cores to be used. + // For example: `2`, `2.0` or `200m`. + CPU string `yaml:"cpu,omitempty"` + + // Memory the amount of memory to allocate to the pod. + // For example: `1Gi` or `1000Mi`. + Memory string `yaml:"memory,omitempty"` +} + +// TestCase is a list of structure tests to run on images that Skaffold builds. +type TestCase struct { + // ImageName is the artifact on which to run those tests. + // For example: `gcr.io/k8s-skaffold/example`. + ImageName string `yaml:"image" yamltags:"required"` + + // StructureTests lists the [Container Structure Tests](https://github.com/GoogleContainerTools/container-structure-test) + // to run on that artifact. + // For example: `["./test/*"]`. + StructureTests []string `yaml:"structureTests,omitempty"` +} + +// DeployConfig contains all the configuration needed by the deploy steps. +type DeployConfig struct { + DeployType `yaml:",inline"` +} + +// DeployType contains the specific implementation and parameters needed +// for the deploy step. Only one field should be populated. +type DeployType struct { + // HelmDeploy *beta* uses the `helm` CLI to apply the charts to the cluster. + HelmDeploy *HelmDeploy `yaml:"helm,omitempty" yamltags:"oneOf=deploy"` + + // KubectlDeploy *beta* uses a client side `kubectl apply` to deploy manifests. + // You'll need a `kubectl` CLI version installed that's compatible with your cluster. + KubectlDeploy *KubectlDeploy `yaml:"kubectl,omitempty" yamltags:"oneOf=deploy"` + + // KustomizeDeploy *beta* uses the `kustomize` CLI to "patch" a deployment for a target environment. + KustomizeDeploy *KustomizeDeploy `yaml:"kustomize,omitempty" yamltags:"oneOf=deploy"` +} + +// KubectlDeploy *beta* uses a client side `kubectl apply` to deploy manifests. +// You'll need a `kubectl` CLI version installed that's compatible with your cluster. +type KubectlDeploy struct { + // Manifests lists the Kubernetes yaml or json manifests. + // Defaults to `["k8s/*.yaml"]`. + Manifests []string `yaml:"manifests,omitempty"` + + // RemoteManifests lists Kubernetes manifests in remote clusters. + RemoteManifests []string `yaml:"remoteManifests,omitempty"` + + // Flags are additional flags passed to `kubectl`. + Flags KubectlFlags `yaml:"flags,omitempty"` +} + +// KubectlFlags are additional flags passed on the command +// line to kubectl either on every command (Global), on creations (Apply) +// or deletions (Delete). +type KubectlFlags struct { + // Global are additional flags passed on every command. + Global []string `yaml:"global,omitempty"` + + // Apply are additional flags passed on creations (`kubectl apply`). + Apply []string `yaml:"apply,omitempty"` + + // Delete are additional flags passed on deletions (`kubectl delete`). + Delete []string `yaml:"delete,omitempty"` +} + +// HelmDeploy *beta* uses the `helm` CLI to apply the charts to the cluster. +type HelmDeploy struct { + // Releases is a list of Helm releases. + Releases []HelmRelease `yaml:"releases,omitempty" yamltags:"required"` + + // Flags are additional option flags that are passed on the command + // line to `helm`. + Flags HelmDeployFlags `yaml:"flags,omitempty"` +} + +// HelmDeployFlags are additional option flags that are passed on the command +// line to `helm`. +type HelmDeployFlags struct { + // Global are additional flags passed on every command. + Global []string `yaml:"global,omitempty"` + + // Install are additional flags passed to (`helm install`). + Install []string `yaml:"install,omitempty"` + + // Upgrade are additional flags passed to (`helm upgrade`). + Upgrade []string `yaml:"upgrade,omitempty"` +} + +// KustomizeDeploy *beta* uses the `kustomize` CLI to "patch" a deployment for a target environment. +type KustomizeDeploy struct { + // KustomizePath is the path to Kustomization files. + // Defaults to `.`. + KustomizePath string `yaml:"path,omitempty"` + + // Flags are additional flags passed to `kubectl`. + Flags KubectlFlags `yaml:"flags,omitempty"` +} + +// HelmRelease describes a helm release to be deployed. +type HelmRelease struct { + // Name is the name of the Helm release. + Name string `yaml:"name,omitempty" yamltags:"required"` + + // ChartPath is the path to the Helm chart. + ChartPath string `yaml:"chartPath,omitempty" yamltags:"required"` + + // ValuesFiles are the paths to the Helm `values` files. + ValuesFiles []string `yaml:"valuesFiles,omitempty"` + + // Values are key-value pairs supplementing the Helm `values` file. + Values map[string]string `yaml:"values,omitempty,omitempty"` + + // Namespace is the Kubernetes namespace. + Namespace string `yaml:"namespace,omitempty"` + + // Version is the version of the chart. + Version string `yaml:"version,omitempty"` + + // SetValues are key-value pairs. + // If present, Skaffold will send `--set` flag to Helm CLI and append all pairs after the flag. + SetValues map[string]string `yaml:"setValues,omitempty"` + + // SetValueTemplates are key-value pairs. + // If present, Skaffold will try to parse the value part of each key-value pair using + // environment variables in the system, then send `--set` flag to Helm CLI and append + // all parsed pairs after the flag. + SetValueTemplates map[string]string `yaml:"setValueTemplates,omitempty"` + + // Wait if `true`, Skaffold will send `--wait` flag to Helm CLI. + // Defaults to `false`. + Wait bool `yaml:"wait,omitempty"` + + // RecreatePods if `true`, Skaffold will send `--recreate-pods` flag to Helm CLI. + // Defaults to `false`. + RecreatePods bool `yaml:"recreatePods,omitempty"` + + // SkipBuildDependencies should build dependencies be skipped. + SkipBuildDependencies bool `yaml:"skipBuildDependencies,omitempty"` + + // UseHelmSecrets instructs skaffold to use secrets plugin on deployment. + UseHelmSecrets bool `yaml:"useHelmSecrets,omitempty"` + + // Overrides are key-value pairs. + // If present, Skaffold will build a Helm `values` file that overrides + // the original and use it to call Helm CLI (`--f` flag). + Overrides util.HelmOverrides `yaml:"overrides,omitempty"` + + // Packaged parameters for packaging helm chart (`helm package`). + Packaged *HelmPackaged `yaml:"packaged,omitempty"` + + // ImageStrategy adds image configurations to the Helm `values` file. + ImageStrategy HelmImageStrategy `yaml:"imageStrategy,omitempty"` +} + +// HelmPackaged parameters for packaging helm chart (`helm package`). +type HelmPackaged struct { + // Version sets the `version` on the chart to this semver version. + Version string `yaml:"version,omitempty"` + + // AppVersion sets the `appVersion` on the chart to this version. + AppVersion string `yaml:"appVersion,omitempty"` +} + +// HelmImageStrategy adds image configurations to the Helm `values` file. +type HelmImageStrategy struct { + HelmImageConfig `yaml:",inline"` +} + +// HelmImageConfig describes an image configuration. +type HelmImageConfig struct { + // HelmFQNConfig is the image configuration uses the syntax `IMAGE-NAME=IMAGE-REPOSITORY:IMAGE-TAG`. + HelmFQNConfig *HelmFQNConfig `yaml:"fqn,omitempty" yamltags:"oneOf=helmImageStrategy"` + + // HelmConventionConfig is the image configuration uses the syntax `IMAGE-NAME.repository=IMAGE-REPOSITORY, IMAGE-NAME.tag=IMAGE-TAG`. + HelmConventionConfig *HelmConventionConfig `yaml:"helm,omitempty" yamltags:"oneOf=helmImageStrategy"` +} + +// HelmFQNConfig is the image config to use the FullyQualifiedImageName as param to set. +type HelmFQNConfig struct { + // Property defines the image config. + Property string `yaml:"property,omitempty"` +} + +// HelmConventionConfig is the image config in the syntax of image.repository and image.tag. +type HelmConventionConfig struct { +} + +// Artifact are the items that need to be built, along with the context in which +// they should be built. +type Artifact struct { + // ImageName is the name of the image to be built. + // For example: `gcr.io/k8s-skaffold/example`. + ImageName string `yaml:"image,omitempty" yamltags:"required"` + + // Workspace is the directory containing the artifact's sources. + // Defaults to `.`. + Workspace string `yaml:"context,omitempty"` + + // Sync *alpha* lists local files synced to pods instead + // of triggering an image build when modified. + // This is a mapping of local files to sync to remote folders. + // For example: `{"*.py": ".", "css/**/*.css": "app/css"}`. + Sync map[string]string `yaml:"sync,omitempty"` + + // ArtifactType describes how to build an artifact. + ArtifactType `yaml:",inline"` + + WorkspaceHash string `yaml:"-,omitempty"` +} + +// Profile *beta* profiles are used to override any `build`, `test` or `deploy` configuration. +type Profile struct { + // Name is a unique profile name. + // For example: `profile-prod`. + Name string `yaml:"name,omitempty" yamltags:"required"` + + // Pipeline contains the definitions to replace the default skaffold pipeline. + Pipeline `yaml:",inline"` + + // Patches lists patches applied to the configuration. + // Patches use the JSON patch notation. + Patches []JSONPatch `yaml:"patches,omitempty"` + + // Activation criteria by which a profile can be auto-activated. + // The profile is auto-activated if any one of the activations are triggered. + // An activation is triggered if all of the criteria (env, kubeContext, command) are triggered. + Activation []Activation `yaml:"activation,omitempty"` +} + +// JSONPatch patch to be applied by a profile. +type JSONPatch struct { + // Op is the operation carried by the patch: `add`, `remove`, `replace`, `move`, `copy` or `test`. + // Defaults to `replace`. + Op string `yaml:"op,omitempty"` + + // Path is the position in the yaml where the operation takes place. + // For example, this targets the `dockerfile` of the first artifact built. + // For example: `/build/artifacts/0/docker/dockerfile`. + Path string `yaml:"path,omitempty" yamltags:"required"` + + // From is the source position in the yaml, used for `copy` or `move` operations. + From string `yaml:"from,omitempty"` + + // Value is the value to apply. Can be any portion of yaml. + Value *util.YamlpatchNode `yaml:"value,omitempty"` +} + +// Activation criteria by which a profile is auto-activated. +type Activation struct { + // Env is a `key=value` pair. The profile is auto-activated if an Environment + // Variable `key` has value `value`. + // For example: `ENV=production`. + Env string `yaml:"env,omitempty"` + + // KubeContext is a Kubernetes context for which the profile is auto-activated. + // For example: `minikube`. + KubeContext string `yaml:"kubeContext,omitempty"` + + // Command is a Skaffold command for which the profile is auto-activated. + // For example: `dev`. + Command string `yaml:"command,omitempty"` +} + +// ArtifactType describes how to build an artifact. +type ArtifactType struct { + // DockerArtifact *beta* describes an artifact built from a Dockerfile. + DockerArtifact *DockerArtifact `yaml:"docker,omitempty" yamltags:"oneOf=artifact"` + + // BazelArtifact *beta* requires bazel CLI to be installed and the sources to + // contain [Bazel](https://bazel.build/) configuration files. + BazelArtifact *BazelArtifact `yaml:"bazel,omitempty" yamltags:"oneOf=artifact"` + + // JibMavenArtifact *alpha* builds images using the + // [Jib plugin for Maven](https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin). + JibMavenArtifact *JibMavenArtifact `yaml:"jibMaven,omitempty" yamltags:"oneOf=artifact"` + + // JibGradleArtifact *alpha* builds images using the + // [Jib plugin for Gradle](https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin). + JibGradleArtifact *JibGradleArtifact `yaml:"jibGradle,omitempty" yamltags:"oneOf=artifact"` + + // KanikoArtifact *alpha* builds images using [kaniko](https://github.com/GoogleContainerTools/kaniko). + KanikoArtifact *KanikoArtifact `yaml:"kaniko,omitempty" yamltags:"oneOf=artifact"` +} + +// KanikoArtifact *alpha* describes an artifact built from a Dockerfile, +// with kaniko. +type KanikoArtifact struct { + // AdditionalFlags are additional flags to be passed to Kaniko command line. + // See [Kaniko Additional Flags](https://github.com/GoogleContainerTools/kaniko#additional-flags). + // Deprecated - instead the named, unique fields should be used, e.g. `buildArgs`, `cache`, `target`. + AdditionalFlags []string `yaml:"flags,omitempty"` + + // DockerfilePath locates the Dockerfile relative to workspace. + // Defaults to `Dockerfile`. + DockerfilePath string `yaml:"dockerfile,omitempty"` + + // Target is the Dockerfile target name to build. + Target string `yaml:"target,omitempty"` + + // BuildArgs are arguments passed to the docker build. + // For example: `{"key1": "value1", "key2": "value2"}`. + BuildArgs map[string]*string `yaml:"buildArgs,omitempty"` + + // BuildContext is where the build context for this artifact resides. + BuildContext *KanikoBuildContext `yaml:"buildContext,omitempty"` + + // Image is the Docker image used by the Kaniko pod. + // Defaults to the latest released version of `gcr.io/kaniko-project/executor`. + Image string `yaml:"image,omitempty"` + + // Cache configures Kaniko caching. If a cache is specified, Kaniko will + // use a remote cache which will speed up builds. + Cache *KanikoCache `yaml:"cache,omitempty"` +} + +// DockerArtifact *beta* describes an artifact built from a Dockerfile, +// usually using `docker build`. +type DockerArtifact struct { + // DockerfilePath locates the Dockerfile relative to workspace. + // Defaults to `Dockerfile`. + DockerfilePath string `yaml:"dockerfile,omitempty"` + + // Target is the Dockerfile target name to build. + Target string `yaml:"target,omitempty"` + + // BuildArgs are arguments passed to the docker build. + // For example: `{"key1": "value1", "key2": "value2"}`. + BuildArgs map[string]*string `yaml:"buildArgs,omitempty"` + + // CacheFrom lists the Docker images used as cache sources. + // For example: `["golang:1.10.1-alpine3.7", "alpine:3.7"]`. + CacheFrom []string `yaml:"cacheFrom,omitempty"` +} + +// BazelArtifact *beta* describes an artifact built with [Bazel](https://bazel.build/). +type BazelArtifact struct { + // BuildTarget is the `bazel build` target to run. + // For example: `//:skaffold_example.tar`. + BuildTarget string `yaml:"target,omitempty" yamltags:"required"` + + // BuildArgs are additional args to pass to `bazel build`. + // For example: `["-flag", "--otherflag"]`. + BuildArgs []string `yaml:"args,omitempty"` +} + +// JibMavenArtifact *alpha* builds images using the +// [Jib plugin for Maven](https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin). +type JibMavenArtifact struct { + // Module selects which Maven module to build, for a multi module project. + Module string `yaml:"module"` + + // Profile selects which Maven profile to activate. + Profile string `yaml:"profile"` + + // Flags are additional build flags passed to Maven. + // For example: `["-x", "-DskipTests"]`. + Flags []string `yaml:"args,omitempty"` +} + +// JibGradleArtifact *alpha* builds images using the +// [Jib plugin for Gradle](https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin). +type JibGradleArtifact struct { + // Project selects which Gradle project to build. + Project string `yaml:"project"` + + // Flags are additional build flags passed to Gradle. + // For example: `["--no-build-cache"]`. + Flags []string `yaml:"args,omitempty"` +} diff --git a/pkg/skaffold/schema/v1beta9/upgrade.go b/pkg/skaffold/schema/v1beta9/upgrade.go new file mode 100644 index 00000000000..5f296b80afa --- /dev/null +++ b/pkg/skaffold/schema/v1beta9/upgrade.go @@ -0,0 +1,68 @@ +/* +Copyright 2019 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 v1beta9 + +import ( + next "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util" + pkgutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" + "github.com/pkg/errors" +) + +// Upgrade upgrades a configuration to the next version. +// Config changes from v1beta9 to v1beta10 +// 1. No additions +// 2. No removals +// 3. No updates +func (config *SkaffoldConfig) Upgrade() (util.VersionedConfig, error) { + // convert Deploy (should be the same) + var newDeploy next.DeployConfig + if err := pkgutil.CloneThroughJSON(config.Deploy, &newDeploy); err != nil { + return nil, errors.Wrap(err, "converting deploy config") + } + + // convert Profiles (should be the same) + var newProfiles []next.Profile + if config.Profiles != nil { + if err := pkgutil.CloneThroughJSON(config.Profiles, &newProfiles); err != nil { + return nil, errors.Wrap(err, "converting new profile") + } + } + + // convert Build (should be same) + var newBuild next.BuildConfig + if err := pkgutil.CloneThroughJSON(config.Build, &newBuild); err != nil { + return nil, errors.Wrap(err, "converting new build") + } + + // convert Test (should be the same) + var newTest []*next.TestCase + if err := pkgutil.CloneThroughJSON(config.Test, &newTest); err != nil { + return nil, errors.Wrap(err, "converting new test") + } + + return &next.SkaffoldConfig{ + APIVersion: next.Version, + Kind: config.Kind, + Pipeline: next.Pipeline{ + Build: newBuild, + Test: newTest, + Deploy: newDeploy, + }, + Profiles: newProfiles, + }, nil +} diff --git a/pkg/skaffold/schema/v1beta9/upgrade_test.go b/pkg/skaffold/schema/v1beta9/upgrade_test.go new file mode 100644 index 00000000000..d880512c63c --- /dev/null +++ b/pkg/skaffold/schema/v1beta9/upgrade_test.go @@ -0,0 +1,111 @@ +/* +Copyright 2019 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 v1beta9 + +import ( + "testing" + + next "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/GoogleContainerTools/skaffold/testutil" + yaml "gopkg.in/yaml.v2" +) + +func TestUpgrade(t *testing.T) { + yaml := `apiVersion: skaffold/v1beta9 +kind: Config +build: + artifacts: + - image: gcr.io/k8s-skaffold/skaffold-example +test: + - image: gcr.io/k8s-skaffold/skaffold-example + structureTests: + - ./test/* +deploy: + kubectl: + manifests: + - k8s-* +profiles: + - name: test profile + build: + artifacts: + - image: gcr.io/k8s-skaffold/skaffold-example + kaniko: + buildContext: + gcsBucket: skaffold-kaniko + cache: {} + cluster: + pullSecretName: e2esecret + namespace: default + test: + - image: gcr.io/k8s-skaffold/skaffold-example + structureTests: + - ./test/* + deploy: + kubectl: + manifests: + - k8s-* +` + expected := `apiVersion: skaffold/v1beta10 +kind: Config +build: + artifacts: + - image: gcr.io/k8s-skaffold/skaffold-example +test: + - image: gcr.io/k8s-skaffold/skaffold-example + structureTests: + - ./test/* +deploy: + kubectl: + manifests: + - k8s-* +profiles: + - name: test profile + build: + artifacts: + - image: gcr.io/k8s-skaffold/skaffold-example + kaniko: + buildContext: + gcsBucket: skaffold-kaniko + cache: {} + cluster: + pullSecretName: e2esecret + namespace: default + test: + - image: gcr.io/k8s-skaffold/skaffold-example + structureTests: + - ./test/* + deploy: + kubectl: + manifests: + - k8s-* +` + verifyUpgrade(t, yaml, expected) +} + +func verifyUpgrade(t *testing.T, input, output string) { + config := NewSkaffoldConfig() + err := yaml.UnmarshalStrict([]byte(input), config) + testutil.CheckErrorAndDeepEqual(t, false, err, Version, config.GetVersion()) + + upgraded, err := config.Upgrade() + testutil.CheckError(t, false, err) + + expected := next.NewSkaffoldConfig() + err = yaml.UnmarshalStrict([]byte(output), expected) + + testutil.CheckErrorAndDeepEqual(t, false, err, expected, upgraded) +} diff --git a/pkg/skaffold/schema/versions.go b/pkg/skaffold/schema/versions.go index 417a91ff591..d7f13d274c5 100644 --- a/pkg/skaffold/schema/versions.go +++ b/pkg/skaffold/schema/versions.go @@ -35,6 +35,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1beta6" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1beta7" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1beta8" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1beta9" misc "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -59,6 +60,7 @@ var SchemaVersions = Versions{ {v1beta6.Version, v1beta6.NewSkaffoldConfig}, {v1beta7.Version, v1beta7.NewSkaffoldConfig}, {v1beta8.Version, v1beta8.NewSkaffoldConfig}, + {v1beta9.Version, v1beta9.NewSkaffoldConfig}, {latest.Version, latest.NewSkaffoldConfig}, } From 67eeb834fde6ca3c5a291fe6fde8be1c30626a39 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Fri, 26 Apr 2019 11:43:25 -0700 Subject: [PATCH 10/17] move custom to testdata --- integration/run_test.go | 2 +- integration/{examples => testdata}/custom/BUILD | 0 integration/{examples => testdata}/custom/WORKSPACE | 0 integration/{examples => testdata}/custom/build.sh | 0 integration/{examples => testdata}/custom/k8s/k8s-pod.yaml | 0 integration/{examples => testdata}/custom/main.go | 0 integration/{examples => testdata}/custom/skaffold.yaml | 0 7 files changed, 1 insertion(+), 1 deletion(-) rename integration/{examples => testdata}/custom/BUILD (100%) rename integration/{examples => testdata}/custom/WORKSPACE (100%) rename integration/{examples => testdata}/custom/build.sh (100%) rename integration/{examples => testdata}/custom/k8s/k8s-pod.yaml (100%) rename integration/{examples => testdata}/custom/main.go (100%) rename integration/{examples => testdata}/custom/skaffold.yaml (100%) diff --git a/integration/run_test.go b/integration/run_test.go index 8d616af8186..3dae1201c84 100644 --- a/integration/run_test.go +++ b/integration/run_test.go @@ -106,7 +106,7 @@ func TestRun(t *testing.T) { remoteOnly: true, }, { description: "custom builder", - dir: "examples/custom", + dir: "testdata/custom", pods: []string{"bazel"}, }, } diff --git a/integration/examples/custom/BUILD b/integration/testdata/custom/BUILD similarity index 100% rename from integration/examples/custom/BUILD rename to integration/testdata/custom/BUILD diff --git a/integration/examples/custom/WORKSPACE b/integration/testdata/custom/WORKSPACE similarity index 100% rename from integration/examples/custom/WORKSPACE rename to integration/testdata/custom/WORKSPACE diff --git a/integration/examples/custom/build.sh b/integration/testdata/custom/build.sh similarity index 100% rename from integration/examples/custom/build.sh rename to integration/testdata/custom/build.sh diff --git a/integration/examples/custom/k8s/k8s-pod.yaml b/integration/testdata/custom/k8s/k8s-pod.yaml similarity index 100% rename from integration/examples/custom/k8s/k8s-pod.yaml rename to integration/testdata/custom/k8s/k8s-pod.yaml diff --git a/integration/examples/custom/main.go b/integration/testdata/custom/main.go similarity index 100% rename from integration/examples/custom/main.go rename to integration/testdata/custom/main.go diff --git a/integration/examples/custom/skaffold.yaml b/integration/testdata/custom/skaffold.yaml similarity index 100% rename from integration/examples/custom/skaffold.yaml rename to integration/testdata/custom/skaffold.yaml From 156ae7001cb42795191aed74400c450ecbb19013 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Mon, 29 Apr 2019 15:27:45 -0700 Subject: [PATCH 11/17] Create custom artifact builder Created a custom artifact builder which local/cluster builders will call out to for building custom artifacts. Added unit tests. --- pkg/skaffold/build/custom/custom.go | 102 +++++++++++++++++++++++ pkg/skaffold/build/custom/custom_test.go | 90 ++++++++++++++++++++ pkg/skaffold/build/local/custom.go | 61 -------------- pkg/skaffold/build/local/custom_test.go | 71 ---------------- pkg/skaffold/build/local/local.go | 3 +- pkg/skaffold/constants/constants.go | 9 +- 6 files changed, 200 insertions(+), 136 deletions(-) create mode 100644 pkg/skaffold/build/custom/custom.go create mode 100644 pkg/skaffold/build/custom/custom_test.go delete mode 100644 pkg/skaffold/build/local/custom.go delete mode 100644 pkg/skaffold/build/local/custom_test.go diff --git a/pkg/skaffold/build/custom/custom.go b/pkg/skaffold/build/custom/custom.go new file mode 100644 index 00000000000..b1512f68582 --- /dev/null +++ b/pkg/skaffold/build/custom/custom.go @@ -0,0 +1,102 @@ +/* +Copyright 2019 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 custom + +import ( + "context" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "sort" + "strings" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/pkg/errors" +) + +var ( + // For testing + environ = os.Environ + buildContext = retrieveBuildContext +) + +// ArtifactBuilder is a builder for custom artifacts +type ArtifactBuilder struct { + pushImages bool + localDocker docker.LocalDaemon + kubeContext string + insecureRegistries map[string]bool +} + +func NewArtifactBuilder(pushImages bool, kubeContext string, insecureRegistries map[string]bool, localDocker docker.LocalDaemon) *ArtifactBuilder { + return &ArtifactBuilder{ + pushImages: pushImages, + kubeContext: kubeContext, + localDocker: localDocker, + insecureRegistries: insecureRegistries, + } +} + +// Build builds a custom artifact and returns the digest of the built image +func (b *ArtifactBuilder) Build(ctx context.Context, out io.Writer, a *latest.Artifact, tag string) (string, error) { + artifact := a.CustomArtifact + cmd := exec.Command(artifact.BuildCommand) + env, err := b.retrieveEnv(a, tag) + if err != nil { + return "", errors.Wrapf(err, "retrieving env variables for %s", a.ImageName) + } + cmd.Env = env + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return "", errors.Wrapf(err, "building image with command %s", cmd.Args) + } + + if b.pushImages { + return docker.RemoteDigest(tag, b.insecureRegistries) + } + + return b.localDocker.ImageID(ctx, tag) +} + +func (b *ArtifactBuilder) retrieveEnv(a *latest.Artifact, tag string) ([]string, error) { + images := strings.Join([]string{tag}, ",") + buildContext, err := buildContext(a.Workspace) + if err != nil { + return nil, errors.Wrap(err, "getting absolute path for artifact build context") + } + + envs := []string{ + fmt.Sprintf("%s=%s", constants.Images, images), + fmt.Sprintf("%s=%t", constants.PushImage, b.pushImages), + fmt.Sprintf("%s=%s", constants.BuildContext, buildContext), + } + if b.localDocker != nil { + envs = append(envs, b.localDocker.ExtraEnv()...) + } + envs = append(envs, environ()...) + sort.Strings(envs) + return envs, nil +} + +func retrieveBuildContext(workspace string) (string, error) { + return filepath.Abs(workspace) +} diff --git a/pkg/skaffold/build/custom/custom_test.go b/pkg/skaffold/build/custom/custom_test.go new file mode 100644 index 00000000000..1566c4b3d49 --- /dev/null +++ b/pkg/skaffold/build/custom/custom_test.go @@ -0,0 +1,90 @@ +/* +Copyright 2019 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 custom + +import ( + "testing" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/GoogleContainerTools/skaffold/testutil" +) + +func TestRetrieveEnv(t *testing.T) { + tests := []struct { + description string + tag string + pushImages bool + buildContext string + api *testutil.FakeAPIClient + artifact *latest.Artifact + dockerEnv []string + environ []string + expected []string + }{ + + { + description: "make sure tags are correct", + tag: "gcr.io/image/tag:mytag", + environ: nil, + buildContext: "/some/path", + expected: []string{"BUILD_CONTEXT=/some/path", "IMAGES=gcr.io/image/tag:mytag", "PUSH_IMAGE=false"}, + }, { + description: "make sure environ is correctly applied", + tag: "gcr.io/image/tag:anothertag", + environ: []string{"PATH=/path", "HOME=/root"}, + buildContext: "/some/path", + expected: []string{"BUILD_CONTEXT=/some/path", "HOME=/root", "IMAGES=gcr.io/image/tag:anothertag", "PATH=/path", "PUSH_IMAGE=false"}, + }, + { + description: "make sure docker env is correctly applied", + tag: "gcr.io/image/docker:tag", + dockerEnv: []string{"DOCKER_API_VERSION=1.3", "DOCKER_CERT_PATH=/home/.minikube/certs"}, + expected: []string{"BUILD_CONTEXT=", "DOCKER_API_VERSION=1.3", "DOCKER_CERT_PATH=/home/.minikube/certs", "IMAGES=gcr.io/image/docker:tag", "PUSH_IMAGE=false"}, + }, { + description: "push image is true", + tag: "gcr.io/image/push:tag", + pushImages: true, + expected: []string{"BUILD_CONTEXT=", "IMAGES=gcr.io/image/push:tag", "PUSH_IMAGE=true"}, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + initialEnviron := environ + defer func() { + environ = initialEnviron + }() + environ = func() []string { + return test.environ + } + + initialBuildContext := buildContext + defer func() { + buildContext = initialBuildContext + }() + buildContext = func(_ string) (string, error) { + return test.buildContext, nil + } + + client := docker.NewLocalDaemon(test.api, test.dockerEnv, false, nil) + artifactBuilder := NewArtifactBuilder(test.pushImages, "", nil, client) + actual, err := artifactBuilder.retrieveEnv(&latest.Artifact{}, test.tag) + testutil.CheckErrorAndDeepEqual(t, false, err, test.expected, actual) + }) + } +} diff --git a/pkg/skaffold/build/local/custom.go b/pkg/skaffold/build/local/custom.go deleted file mode 100644 index 9d24e95bb1f..00000000000 --- a/pkg/skaffold/build/local/custom.go +++ /dev/null @@ -1,61 +0,0 @@ -/* -Copyright 2019 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 local - -import ( - "context" - "fmt" - "io" - "os" - "os/exec" - - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" - "github.com/pkg/errors" -) - -var ( - // For testing - environ = os.Environ -) - -func (b *Builder) buildCustom(ctx context.Context, out io.Writer, a *latest.Artifact, tag string) (string, error) { - artifact := a.CustomArtifact - cmd := exec.Command(artifact.BuildCommand) - cmd.Env = retrieveEnv(tag, b.pushImages, b.localDocker.ExtraEnv()) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - return "", errors.Wrapf(err, "building image with command %s", cmd.Args) - } - - if b.pushImages { - return docker.RemoteDigest(tag, b.insecureRegistries) - } - - return b.localDocker.ImageID(ctx, tag) -} - -func retrieveEnv(tag string, pushImage bool, localDockerEnv []string) []string { - tags := []string{ - fmt.Sprintf("%s=%s", constants.ImageName, tag), - fmt.Sprintf("%s=%t", constants.PushImage, pushImage), - } - tags = append(tags, localDockerEnv...) - return append(tags, environ()...) -} diff --git a/pkg/skaffold/build/local/custom_test.go b/pkg/skaffold/build/local/custom_test.go deleted file mode 100644 index 996d2b0dc31..00000000000 --- a/pkg/skaffold/build/local/custom_test.go +++ /dev/null @@ -1,71 +0,0 @@ -/* -Copyright 2019 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 local - -import ( - "testing" - - "github.com/GoogleContainerTools/skaffold/testutil" -) - -func TestRetrieveEnv(t *testing.T) { - tests := []struct { - description string - tag string - pushImage bool - environ []string - dockerEnv []string - expected []string - }{ - - { - description: "make sure tags are correct", - tag: "gcr.io/image/tag:mytag", - environ: nil, - expected: []string{"IMAGE_NAME=gcr.io/image/tag:mytag", "PUSH_IMAGE=false"}, - }, { - description: "make sure environ is correctly applied", - tag: "gcr.io/image/tag:anothertag", - environ: []string{"PATH=/path", "HOME=/root"}, - expected: []string{"IMAGE_NAME=gcr.io/image/tag:anothertag", "PUSH_IMAGE=false", "PATH=/path", "HOME=/root"}, - }, { - description: "make sure docker env is correctly applied", - tag: "gcr.io/image/docker:tag", - dockerEnv: []string{"DOCKER_API_VERSION=1.3", "DOCKER_CERT_PATH=/home/.minikube/certs"}, - expected: []string{"IMAGE_NAME=gcr.io/image/docker:tag", "PUSH_IMAGE=false", "DOCKER_API_VERSION=1.3", "DOCKER_CERT_PATH=/home/.minikube/certs"}, - }, { - description: "push image is true", - tag: "gcr.io/image/push:tag", - pushImage: true, - expected: []string{"IMAGE_NAME=gcr.io/image/push:tag", "PUSH_IMAGE=true"}, - }, - } - - for _, test := range tests { - t.Run(test.description, func(t *testing.T) { - initial := environ - defer func() { - environ = initial - }() - environ = func() []string { - return test.environ - } - actual := retrieveEnv(test.tag, test.pushImage, test.dockerEnv) - testutil.CheckErrorAndDeepEqual(t, false, nil, test.expected, actual) - }) - } -} diff --git a/pkg/skaffold/build/local/local.go b/pkg/skaffold/build/local/local.go index 7644f96749c..e1585093f3f 100644 --- a/pkg/skaffold/build/local/local.go +++ b/pkg/skaffold/build/local/local.go @@ -98,7 +98,8 @@ func (b *Builder) runBuildForArtifact(ctx context.Context, out io.Writer, artifa return b.buildJibGradle(ctx, out, artifact.Workspace, artifact.JibGradleArtifact, tag) case artifact.CustomArtifact != nil: - return b.buildCustom(ctx, out, artifact, tag) + artifactBuilder := custom.NewArtifactBuilder(b.pushImages, b.kubeContext, b.insecureRegistries, b.localDocker) + return artifactBuilder.Build(ctx, out, artifact, tag) default: return "", fmt.Errorf("undefined artifact type: %+v", artifact.ArtifactType) } diff --git a/pkg/skaffold/constants/constants.go b/pkg/skaffold/constants/constants.go index 44208af156c..7954c6a2873 100644 --- a/pkg/skaffold/constants/constants.go +++ b/pkg/skaffold/constants/constants.go @@ -65,11 +65,14 @@ const ( ) var ( - // ImageName is the env variable that will pass the tagged image name to custom builders - ImageName = "IMAGE_NAME" + // Images is an environment variable key, whose value is an array of fully qualified image names passed in to a custom build script. + Images = "IMAGES" - // PushImage lets the custom builder know if the image is expected to be pushed to a remote registry + // PushImage lets the custom build script know if the image is expected to be pushed to a remote registry PushImage = "PUSH_IMAGE" + + // BuildContext is the absolute path to a directory this artifact is meant to be built from for custom artifacts + BuildContext = "BUILD_CONTEXT" ) var DefaultKubectlManifests = []string{"k8s/*.yaml"} From 624b0f731b4698346275bd7238f500a6061820a9 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Mon, 29 Apr 2019 15:49:43 -0700 Subject: [PATCH 12/17] Pass in additional envs to custom artifact builder from local builder --- pkg/skaffold/build/custom/custom.go | 36 ++++++++-------------- pkg/skaffold/build/custom/custom_test.go | 33 +++++++++----------- pkg/skaffold/build/local/custom.go | 39 ++++++++++++++++++++++++ pkg/skaffold/build/local/local.go | 3 +- 4 files changed, 68 insertions(+), 43 deletions(-) create mode 100644 pkg/skaffold/build/local/custom.go diff --git a/pkg/skaffold/build/custom/custom.go b/pkg/skaffold/build/custom/custom.go index b1512f68582..299bfcd33a7 100644 --- a/pkg/skaffold/build/custom/custom.go +++ b/pkg/skaffold/build/custom/custom.go @@ -27,7 +27,6 @@ import ( "strings" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/pkg/errors" ) @@ -40,41 +39,34 @@ var ( // ArtifactBuilder is a builder for custom artifacts type ArtifactBuilder struct { - pushImages bool - localDocker docker.LocalDaemon - kubeContext string - insecureRegistries map[string]bool + pushImages bool + additionalEnv []string } -func NewArtifactBuilder(pushImages bool, kubeContext string, insecureRegistries map[string]bool, localDocker docker.LocalDaemon) *ArtifactBuilder { +// NewArtifactBuilder returns a new custom artifact builder +func NewArtifactBuilder(pushImages bool, additionalEnv []string) *ArtifactBuilder { return &ArtifactBuilder{ - pushImages: pushImages, - kubeContext: kubeContext, - localDocker: localDocker, - insecureRegistries: insecureRegistries, + pushImages: pushImages, + additionalEnv: additionalEnv, } } -// Build builds a custom artifact and returns the digest of the built image -func (b *ArtifactBuilder) Build(ctx context.Context, out io.Writer, a *latest.Artifact, tag string) (string, error) { +// Build builds a custom artifact +// It returns true if the image is expected to exist remotely, or false if it is expected to exist locally +func (b *ArtifactBuilder) Build(ctx context.Context, out io.Writer, a *latest.Artifact, tag string) (bool, error) { artifact := a.CustomArtifact cmd := exec.Command(artifact.BuildCommand) env, err := b.retrieveEnv(a, tag) if err != nil { - return "", errors.Wrapf(err, "retrieving env variables for %s", a.ImageName) + return false, errors.Wrapf(err, "retrieving env variables for %s", a.ImageName) } cmd.Env = env cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return "", errors.Wrapf(err, "building image with command %s", cmd.Args) + return false, errors.Wrapf(err, "building image with command %s", cmd.Args) } - - if b.pushImages { - return docker.RemoteDigest(tag, b.insecureRegistries) - } - - return b.localDocker.ImageID(ctx, tag) + return b.pushImages, nil } func (b *ArtifactBuilder) retrieveEnv(a *latest.Artifact, tag string) ([]string, error) { @@ -89,9 +81,7 @@ func (b *ArtifactBuilder) retrieveEnv(a *latest.Artifact, tag string) ([]string, fmt.Sprintf("%s=%t", constants.PushImage, b.pushImages), fmt.Sprintf("%s=%s", constants.BuildContext, buildContext), } - if b.localDocker != nil { - envs = append(envs, b.localDocker.ExtraEnv()...) - } + envs = append(envs, b.additionalEnv...) envs = append(envs, environ()...) sort.Strings(envs) return envs, nil diff --git a/pkg/skaffold/build/custom/custom_test.go b/pkg/skaffold/build/custom/custom_test.go index 1566c4b3d49..a2ea76cca62 100644 --- a/pkg/skaffold/build/custom/custom_test.go +++ b/pkg/skaffold/build/custom/custom_test.go @@ -19,22 +19,20 @@ package custom import ( "testing" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/testutil" ) func TestRetrieveEnv(t *testing.T) { tests := []struct { - description string - tag string - pushImages bool - buildContext string - api *testutil.FakeAPIClient - artifact *latest.Artifact - dockerEnv []string - environ []string - expected []string + description string + tag string + pushImages bool + buildContext string + artifact *latest.Artifact + additionalEnv []string + environ []string + expected []string }{ { @@ -49,17 +47,17 @@ func TestRetrieveEnv(t *testing.T) { environ: []string{"PATH=/path", "HOME=/root"}, buildContext: "/some/path", expected: []string{"BUILD_CONTEXT=/some/path", "HOME=/root", "IMAGES=gcr.io/image/tag:anothertag", "PATH=/path", "PUSH_IMAGE=false"}, - }, - { - description: "make sure docker env is correctly applied", - tag: "gcr.io/image/docker:tag", - dockerEnv: []string{"DOCKER_API_VERSION=1.3", "DOCKER_CERT_PATH=/home/.minikube/certs"}, - expected: []string{"BUILD_CONTEXT=", "DOCKER_API_VERSION=1.3", "DOCKER_CERT_PATH=/home/.minikube/certs", "IMAGES=gcr.io/image/docker:tag", "PUSH_IMAGE=false"}, }, { description: "push image is true", tag: "gcr.io/image/push:tag", pushImages: true, expected: []string{"BUILD_CONTEXT=", "IMAGES=gcr.io/image/push:tag", "PUSH_IMAGE=true"}, + }, { + description: "add additional env", + tag: "gcr.io/image/push:tag", + pushImages: true, + additionalEnv: []string{"KUBECONTEXT=mycluster"}, + expected: []string{"BUILD_CONTEXT=", "IMAGES=gcr.io/image/push:tag", "KUBECONTEXT=mycluster", "PUSH_IMAGE=true"}, }, } @@ -81,8 +79,7 @@ func TestRetrieveEnv(t *testing.T) { return test.buildContext, nil } - client := docker.NewLocalDaemon(test.api, test.dockerEnv, false, nil) - artifactBuilder := NewArtifactBuilder(test.pushImages, "", nil, client) + artifactBuilder := NewArtifactBuilder(test.pushImages, test.additionalEnv) actual, err := artifactBuilder.retrieveEnv(&latest.Artifact{}, test.tag) testutil.CheckErrorAndDeepEqual(t, false, err, test.expected, actual) }) diff --git a/pkg/skaffold/build/local/custom.go b/pkg/skaffold/build/local/custom.go new file mode 100644 index 00000000000..2365d75785b --- /dev/null +++ b/pkg/skaffold/build/local/custom.go @@ -0,0 +1,39 @@ +/* +Copyright 2019 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 local + +import ( + "context" + "io" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/custom" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/pkg/errors" +) + +func (b *Builder) buildCustom(ctx context.Context, out io.Writer, artifact *latest.Artifact, tag string) (string, error) { + customArtifactBuilder := custom.NewArtifactBuilder(b.pushImages, b.localDocker.ExtraEnv()) + imageExpectedRemotely, err := customArtifactBuilder.Build(ctx, out, artifact, tag) + if err != nil { + return "", errors.Wrap(err, "building custom artifact") + } + if imageExpectedRemotely { + return docker.RemoteDigest(tag, b.insecureRegistries) + } + return b.localDocker.ImageID(ctx, tag) +} diff --git a/pkg/skaffold/build/local/local.go b/pkg/skaffold/build/local/local.go index e1585093f3f..7644f96749c 100644 --- a/pkg/skaffold/build/local/local.go +++ b/pkg/skaffold/build/local/local.go @@ -98,8 +98,7 @@ func (b *Builder) runBuildForArtifact(ctx context.Context, out io.Writer, artifa return b.buildJibGradle(ctx, out, artifact.Workspace, artifact.JibGradleArtifact, tag) case artifact.CustomArtifact != nil: - artifactBuilder := custom.NewArtifactBuilder(b.pushImages, b.kubeContext, b.insecureRegistries, b.localDocker) - return artifactBuilder.Build(ctx, out, artifact, tag) + return b.buildCustom(ctx, out, artifact, tag) default: return "", fmt.Errorf("undefined artifact type: %+v", artifact.ArtifactType) } From e59589b5ac355d27ae112f6bf6f80bc4a7048400 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Mon, 29 Apr 2019 16:01:41 -0700 Subject: [PATCH 13/17] update build.sh to reflect change from IMAGE_NAME to IMAGES --- .gitignore | 2 +- integration/testdata/custom/build.sh | 18 +++++++++++++----- pkg/skaffold/build/custom/custom.go | 2 +- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 285551668da..92329b3b996 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ out/ examples/bazel/bazel-* integration/examples/bazel/bazel-* -integration/examples/custom/bazel-* +integration/testdata/custom/bazel-* integration/testdata/plugin/local/bazel/bazel-* *.new *.iml diff --git a/integration/testdata/custom/build.sh b/integration/testdata/custom/build.sh index 92e2ffd2acb..d4adcde3e00 100755 --- a/integration/testdata/custom/build.sh +++ b/integration/testdata/custom/build.sh @@ -3,10 +3,18 @@ bazel build //:skaffold_example.tar TAR_PATH=$(bazel info bazel-bin) docker load -i $TAR_PATH/skaffold_example.tar -docker tag bazel:skaffold_example $IMAGE_NAME -if $PUSH_IMAGE -then - docker push $IMAGE_NAME -fi +images=$(echo $IMAGES | tr " " "\n") + +for image in $images +do + docker tag bazel:skaffold_example $image + + if $PUSH_IMAGE + then + docker push $image + fi + +done + diff --git a/pkg/skaffold/build/custom/custom.go b/pkg/skaffold/build/custom/custom.go index 299bfcd33a7..2b0222ffbba 100644 --- a/pkg/skaffold/build/custom/custom.go +++ b/pkg/skaffold/build/custom/custom.go @@ -70,7 +70,7 @@ func (b *ArtifactBuilder) Build(ctx context.Context, out io.Writer, a *latest.Ar } func (b *ArtifactBuilder) retrieveEnv(a *latest.Artifact, tag string) ([]string, error) { - images := strings.Join([]string{tag}, ",") + images := strings.Join([]string{tag}, " ") buildContext, err := buildContext(a.Workspace) if err != nil { return nil, errors.Wrap(err, "getting absolute path for artifact build context") From 87aae867ac76b5e08a9f7cea9e928e532223e4eb Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Tue, 30 Apr 2019 10:30:07 -0700 Subject: [PATCH 14/17] fix linting error & simplify custom artifact build signature --- pkg/skaffold/build/custom/custom.go | 10 ++++------ pkg/skaffold/build/custom/custom_test.go | 1 - pkg/skaffold/build/local/custom.go | 8 +++++--- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/pkg/skaffold/build/custom/custom.go b/pkg/skaffold/build/custom/custom.go index 2b0222ffbba..1b3299fc513 100644 --- a/pkg/skaffold/build/custom/custom.go +++ b/pkg/skaffold/build/custom/custom.go @@ -53,20 +53,18 @@ func NewArtifactBuilder(pushImages bool, additionalEnv []string) *ArtifactBuilde // Build builds a custom artifact // It returns true if the image is expected to exist remotely, or false if it is expected to exist locally -func (b *ArtifactBuilder) Build(ctx context.Context, out io.Writer, a *latest.Artifact, tag string) (bool, error) { +func (b *ArtifactBuilder) Build(ctx context.Context, out io.Writer, a *latest.Artifact, tag string) error { artifact := a.CustomArtifact cmd := exec.Command(artifact.BuildCommand) env, err := b.retrieveEnv(a, tag) if err != nil { - return false, errors.Wrapf(err, "retrieving env variables for %s", a.ImageName) + return errors.Wrapf(err, "retrieving env variables for %s", a.ImageName) } cmd.Env = env cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - return false, errors.Wrapf(err, "building image with command %s", cmd.Args) - } - return b.pushImages, nil + + return cmd.Run() } func (b *ArtifactBuilder) retrieveEnv(a *latest.Artifact, tag string) ([]string, error) { diff --git a/pkg/skaffold/build/custom/custom_test.go b/pkg/skaffold/build/custom/custom_test.go index a2ea76cca62..e2992c933bf 100644 --- a/pkg/skaffold/build/custom/custom_test.go +++ b/pkg/skaffold/build/custom/custom_test.go @@ -29,7 +29,6 @@ func TestRetrieveEnv(t *testing.T) { tag string pushImages bool buildContext string - artifact *latest.Artifact additionalEnv []string environ []string expected []string diff --git a/pkg/skaffold/build/local/custom.go b/pkg/skaffold/build/local/custom.go index 2365d75785b..0c94fe7e26f 100644 --- a/pkg/skaffold/build/local/custom.go +++ b/pkg/skaffold/build/local/custom.go @@ -28,12 +28,14 @@ import ( func (b *Builder) buildCustom(ctx context.Context, out io.Writer, artifact *latest.Artifact, tag string) (string, error) { customArtifactBuilder := custom.NewArtifactBuilder(b.pushImages, b.localDocker.ExtraEnv()) - imageExpectedRemotely, err := customArtifactBuilder.Build(ctx, out, artifact, tag) - if err != nil { + + if err := customArtifactBuilder.Build(ctx, out, artifact, tag); err != nil { return "", errors.Wrap(err, "building custom artifact") } - if imageExpectedRemotely { + + if b.pushImages { return docker.RemoteDigest(tag, b.insecureRegistries) } + return b.localDocker.ImageID(ctx, tag) } From f1fae32ee6c482a07209c415f4cd03cd74c85901 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Tue, 30 Apr 2019 10:55:01 -0700 Subject: [PATCH 15/17] add unit test for get dependencies --- .../build/custom/dependencies_test.go | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 pkg/skaffold/build/custom/dependencies_test.go diff --git a/pkg/skaffold/build/custom/dependencies_test.go b/pkg/skaffold/build/custom/dependencies_test.go new file mode 100644 index 00000000000..35f0a4d0043 --- /dev/null +++ b/pkg/skaffold/build/custom/dependencies_test.go @@ -0,0 +1,74 @@ +/* +Copyright 2019 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 custom + +import ( + "context" + "path/filepath" + "testing" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/GoogleContainerTools/skaffold/testutil" +) + +func TestGetDependencies(t *testing.T) { + tmpDir, cleanup := testutil.NewTempDir(t) + defer cleanup() + + // Directory structure: + // foo + // bar + // - baz + // file + tmpDir.Write("foo", "") + tmpDir.Write("bar", "") + tmpDir.Mkdir("baz") + tmpDir.Write("baz/file", "") + + tests := []struct { + description string + ignore []string + paths []string + expected []string + }{ + { + description: "watch everything", + paths: []string{"."}, + expected: []string{"bar", filepath.FromSlash("baz/file"), "foo"}, + }, { + description: "watch nothing", + }, { + description: "ignore some paths", + paths: []string{"."}, + ignore: []string{"b*"}, + expected: []string{"foo"}, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + deps, err := GetDependencies(context.Background(), tmpDir.Root(), &latest.CustomArtifact{ + Dependencies: &latest.CustomDependencies{ + Paths: test.paths, + Ignore: test.ignore, + }, + }) + testutil.CheckErrorAndDeepEqual(t, false, err, test.expected, deps) + }) + } + +} From 05a5ee817ef4991cbeff52012e5f1b83b96f415b Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Tue, 30 Apr 2019 13:26:49 -0700 Subject: [PATCH 16/17] code review comments and added documentation --- .../en/docs/how-tos/builders/_index.md | 67 +++++++++++++++++++ docs/content/en/samples/builders/build.sh | 20 ++++++ docs/content/en/samples/builders/custom.yaml | 10 +++ docs/content/en/schemas/v1beta10.json | 16 ++--- hack/boilerplate/boilerplate.py | 2 +- pkg/skaffold/schema/latest/config.go | 8 +-- pkg/skaffold/schema/samples_test.go | 16 +++-- 7 files changed, 120 insertions(+), 19 deletions(-) create mode 100644 docs/content/en/samples/builders/build.sh create mode 100644 docs/content/en/samples/builders/custom.yaml diff --git a/docs/content/en/docs/how-tos/builders/_index.md b/docs/content/en/docs/how-tos/builders/_index.md index 01aa3a4b50f..6c7be47ae12 100755 --- a/docs/content/en/docs/how-tos/builders/_index.md +++ b/docs/content/en/docs/how-tos/builders/_index.md @@ -15,6 +15,7 @@ Skaffold supports the following tools to build your image: * [Bazel](https://bazel.build/) locally * [Jib](https://github.com/GoogleContainerTools/jib) Maven and Gradle projects locally * [Jib](https://github.com/GoogleContainerTools/jib) remotely with [Google Cloud Build](https://cloud.google.com/cloud-build/docs/) +* Custom build script run locally The `build` section in the Skaffold configuration file, `skaffold.yaml`, controls how artifacts are built. To use a specific tool for building @@ -208,3 +209,69 @@ The following `build` section instructs Skaffold to build a Docker image `gcr.io/k8s-skaffold/example` with Bazel: {{% readfile file="samples/builders/bazel.yaml" %}} + +## Custom Build Script Run Locally + +Custom build scripts allow skaffold users the flexibility to build artifacts with any builder they desire. +Users can write a custom build script which must abide by the following contract for skaffold to work as expected: + +### Contract between Skaffold and Custom Build Script + +Skaffold will pass in the following environment variables to the custom build script: + +| Environment Variable | Description | Expectation | +| ------------- |-------------| -----| +| $IMAGES | An array of fully qualified image names, separated by spaces. | The custom build script is expected to build an image and tag it with each image name in $IMAGES. Each image should also be pushed if `$PUSH_IMAGE=true`. | +| $PUSH_IMAGE | Set to true if each image in `$IMAGES` is expected to exist in a remote registry. Set to false if each image in `$IMAGES` is expected to exist locally. | The custom build script will push each image in `$IMAGES` if `$PUSH_IMAGE=true` | +| $BUILD_CONTEXT | An absolute path to the directory this artifact is meant to be built from. Specified by artifact `context` in the skaffold.yaml. | None. | +| Local environment variables | The current state of the local environment (e.g. `$HOST`, `$PATH)`. Determined by the golang [os.Environ](https://golang.org/pkg/os/#Environ) function.| None. | + +As described above, the custom build script is expected to: + +1. Build and tag each image in `$IMAGES` +2. Push each image in `$IMAGES` if `$PUSH_IMAGE=true` + +Once the build script has finished executing, skaffold will try to obtain the digest of the newly built image from a remote registry (if `$PUSH_IMAGE=true`) or the local daemon (if `$PUSH_IMAGE=false`). +If skaffold fails to obtain the digest, it will error out. + +#### Additional Environment Variables + +Skaffold will pass in the following additional environment variables for the following builders: + +##### Local builder +| Environment Variable | Description | Expectation | +| ------------- |-------------| -----| +| Docker daemon environment variables | Inform the custom builder of which docker daemon endpoint we are using. Allows custom build scripts to work with tools like Minikube.| None. | + + +### Configuration + +To use a custom build script, add a `custom` field to each artifact in the `build` section of the skaffold.yaml. +Currently, this only works with the build type `local`. Supported schema for `custom` includes: + + +{{< schema root="CustomArtifact" >}} + + +`buildCommand` is *required* and points skaffold to the custom build script which will be executed to build the artifact. +`dependencies` tells the skaffold file watcher which files should be watched to trigger rebuilds and file syncs. Supported schema for `dependencies` includes: + + +{{< schema root="CustomDependencies" >}} + +#### Custom Build Scripts and File Sync +Syncable files must be specified in the `paths` section of `dependencies` so that the skaffold file watcher knows to watch them. + +#### Custom Build Scripts and Logging +STDOUT and STDERR from the custom build script will be redirected and displayed within skaffold logs. + + +### Example + +The following `build` section instructs Skaffold to build an image `gcr.io/k8s-skaffold/example` with a custom build script `build.sh`: + +{{% readfile file="samples/builders/custom.yaml" %}} + +A sample `build.sh` file, which builds an image with bazel and docker: + +{{% readfile file="samples/builders/build.sh" %}} diff --git a/docs/content/en/samples/builders/build.sh b/docs/content/en/samples/builders/build.sh new file mode 100644 index 00000000000..d4adcde3e00 --- /dev/null +++ b/docs/content/en/samples/builders/build.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +bazel build //:skaffold_example.tar +TAR_PATH=$(bazel info bazel-bin) +docker load -i $TAR_PATH/skaffold_example.tar + + +images=$(echo $IMAGES | tr " " "\n") + +for image in $images +do + docker tag bazel:skaffold_example $image + + if $PUSH_IMAGE + then + docker push $image + fi + +done + diff --git a/docs/content/en/samples/builders/custom.yaml b/docs/content/en/samples/builders/custom.yaml new file mode 100644 index 00000000000..5fb120432aa --- /dev/null +++ b/docs/content/en/samples/builders/custom.yaml @@ -0,0 +1,10 @@ +build: + artifacts: + - image: gcr.io/k8s-skaffold/example + custom: + buildCommand: ./build.sh + dependencies: + paths: + - . + ignore: + - README* diff --git a/docs/content/en/schemas/v1beta10.json b/docs/content/en/schemas/v1beta10.json index bb391666f4c..660bef2fe36 100755 --- a/docs/content/en/schemas/v1beta10.json +++ b/docs/content/en/schemas/v1beta10.json @@ -581,8 +581,8 @@ }, "dependencies": { "$ref": "#/definitions/CustomDependencies", - "description": "file dependencies that skaffold should watch for this artifact.", - "x-intellij-html-description": "file dependencies that skaffold should watch for this artifact." + "description": "file dependencies that skaffold should watch for both rebuilding and file syncing for this artifact.", + "x-intellij-html-description": "file dependencies that skaffold should watch for both rebuilding and file syncing for this artifact." } }, "preferredOrder": [ @@ -600,8 +600,8 @@ "type": "string" }, "type": "array", - "description": "specifies the paths that should be ignored by the watcher. If a file exists in `paths` and in `ignore`, it will be ignored. Will only work in conjunction with `paths`.", - "x-intellij-html-description": "specifies the paths that should be ignored by the watcher. If a file exists in paths and in ignore, it will be ignored. Will only work in conjunction with paths.", + "description": "specifies the paths that should be ignored by skaffold's file watcher. If a file exists in both `paths` and in `ignore`, it will be ignored, and will be excluded from both rebuilds and file synchronization. Will only work in conjunction with `paths`.", + "x-intellij-html-description": "specifies the paths that should be ignored by skaffold's file watcher. If a file exists in both paths and in ignore, it will be ignored, and will be excluded from both rebuilds and file synchronization. Will only work in conjunction with paths.", "default": "[]" }, "paths": { @@ -609,8 +609,8 @@ "type": "string" }, "type": "array", - "description": "should be set to the dependencies for this artifact if no Dockerfile exists.", - "x-intellij-html-description": "should be set to the dependencies for this artifact if no Dockerfile exists.", + "description": "should be set to the file dependencies for this artifact, so that the skaffold file watcher knows when to rebuild and perform file synchronization.", + "x-intellij-html-description": "should be set to the file dependencies for this artifact, so that the skaffold file watcher knows when to rebuild and perform file synchronization.", "default": "[]" } }, @@ -619,8 +619,8 @@ "ignore" ], "additionalProperties": false, - "description": "used to specify dependencies for an artifact built by a custom build script.", - "x-intellij-html-description": "used to specify dependencies for an artifact built by a custom build script." + "description": "*alpha* used to specify dependencies for an artifact built by a custom build script.", + "x-intellij-html-description": "alpha used to specify dependencies for an artifact built by a custom build script." }, "DateTimeTagger": { "properties": { diff --git a/hack/boilerplate/boilerplate.py b/hack/boilerplate/boilerplate.py index 63a9197ad5e..5444664e463 100644 --- a/hack/boilerplate/boilerplate.py +++ b/hack/boilerplate/boilerplate.py @@ -24,7 +24,7 @@ SKIPPED_DIRS = ["Godeps", "third_party", ".git", "vendor", "examples", "testdata", "node_modules"] -SKIPPED_FILES = ["install_golint.sh", "skaffold.pb.go", "skaffold.pb.gw.go"] +SKIPPED_FILES = ["install_golint.sh", "skaffold.pb.go", "skaffold.pb.gw.go", "build.sh"] parser = argparse.ArgumentParser() parser.add_argument("filenames", help="list of files to check, all files if unspecified", nargs='*') diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index db05fae9d48..fa1b42c0a21 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -561,15 +561,15 @@ type ArtifactType struct { type CustomArtifact struct { // BuildCommand is the command executed to build the image. BuildCommand string `yaml:"buildCommand,omitempty"` - // Dependencies are the file dependencies that skaffold should watch for this artifact. + // Dependencies are the file dependencies that skaffold should watch for both rebuilding and file syncing for this artifact. Dependencies *CustomDependencies `yaml:"dependencies,omitempty"` } -// CustomDependencies is used to specify dependencies for an artifact built by a custom build script. +// CustomDependencies *alpha* is used to specify dependencies for an artifact built by a custom build script. type CustomDependencies struct { - // Paths should be set to the dependencies for this artifact if no Dockerfile exists. + // Paths should be set to the file dependencies for this artifact, so that the skaffold file watcher knows when to rebuild and perform file synchronization. Paths []string `yaml:"paths,omitempty" yamltags:"oneOf=dependency"` - // Ignore specifies the paths that should be ignored by the watcher. If a file exists in `paths` and in `ignore`, it will be ignored. + // Ignore specifies the paths that should be ignored by skaffold's file watcher. If a file exists in both `paths` and in `ignore`, it will be ignored, and will be excluded from both rebuilds and file synchronization. // Will only work in conjunction with `paths`. Ignore []string `yaml:"ignore,omitempty"` } diff --git a/pkg/skaffold/schema/samples_test.go b/pkg/skaffold/schema/samples_test.go index 57b3b156db2..ee4b1ef1e0b 100644 --- a/pkg/skaffold/schema/samples_test.go +++ b/pkg/skaffold/schema/samples_test.go @@ -29,8 +29,11 @@ import ( ) const ( - samplesRoot = "../../../docs/content/en/samples" - ignoredSample = "structureTest.yaml" + samplesRoot = "../../../docs/content/en/samples" +) + +var ( + ignoredSamples = []string{"structureTest.yaml", "build.sh"} ) func TestParseSamples(t *testing.T) { @@ -49,11 +52,12 @@ func TestParseSamples(t *testing.T) { for _, path := range paths { name := filepath.Base(path) - if name == ignoredSample { - continue - } - t.Run(name, func(t *testing.T) { + for _, is := range ignoredSamples { + if name == is { + t.Skip() + } + } buf, err := ioutil.ReadFile(path) testutil.CheckError(t, false, err) From c3339cea9edf28d8de3b199d414dde9a256e528c Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Tue, 30 Apr 2019 16:52:51 -0700 Subject: [PATCH 17/17] Final code review comments for docs --- docs/content/en/docs/how-tos/builders/_index.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/content/en/docs/how-tos/builders/_index.md b/docs/content/en/docs/how-tos/builders/_index.md index 6c7be47ae12..97098cd5754 100755 --- a/docs/content/en/docs/how-tos/builders/_index.md +++ b/docs/content/en/docs/how-tos/builders/_index.md @@ -221,7 +221,7 @@ Skaffold will pass in the following environment variables to the custom build sc | Environment Variable | Description | Expectation | | ------------- |-------------| -----| -| $IMAGES | An array of fully qualified image names, separated by spaces. | The custom build script is expected to build an image and tag it with each image name in $IMAGES. Each image should also be pushed if `$PUSH_IMAGE=true`. | +| $IMAGES | An array of fully qualified image names, separated by spaces. For example, "gcr.io/image1 gcr.io/image2" | The custom build script is expected to build an image and tag it with each image name in $IMAGES. Each image should also be pushed if `$PUSH_IMAGE=true`. | | $PUSH_IMAGE | Set to true if each image in `$IMAGES` is expected to exist in a remote registry. Set to false if each image in `$IMAGES` is expected to exist locally. | The custom build script will push each image in `$IMAGES` if `$PUSH_IMAGE=true` | | $BUILD_CONTEXT | An absolute path to the directory this artifact is meant to be built from. Specified by artifact `context` in the skaffold.yaml. | None. | | Local environment variables | The current state of the local environment (e.g. `$HOST`, `$PATH)`. Determined by the golang [os.Environ](https://golang.org/pkg/os/#Environ) function.| None. | @@ -241,12 +241,12 @@ Skaffold will pass in the following additional environment variables for the fol ##### Local builder | Environment Variable | Description | Expectation | | ------------- |-------------| -----| -| Docker daemon environment variables | Inform the custom builder of which docker daemon endpoint we are using. Allows custom build scripts to work with tools like Minikube.| None. | +| Docker daemon environment variables | Inform the custom builder of which docker daemon endpoint we are using. Allows custom build scripts to work with tools like Minikube. For Minikube, this is the output of `minikube docker-env`.| None. | ### Configuration -To use a custom build script, add a `custom` field to each artifact in the `build` section of the skaffold.yaml. +To use a custom build script, add a `custom` field to each corresponding artifact in the `build` section of the skaffold.yaml. Currently, this only works with the build type `local`. Supported schema for `custom` includes: @@ -260,7 +260,7 @@ Currently, this only works with the build type `local`. Supported schema for `cu {{< schema root="CustomDependencies" >}} #### Custom Build Scripts and File Sync -Syncable files must be specified in the `paths` section of `dependencies` so that the skaffold file watcher knows to watch them. +Syncable files must be included in both the `paths` section of `dependencies`, so that the skaffold file watcher knows to watch them, and the `sync` section, so that skaffold knows to sync them. #### Custom Build Scripts and Logging STDOUT and STDERR from the custom build script will be redirected and displayed within skaffold logs.