From 57082cecf099cbee9a9191b531c776c8ea1dff69 Mon Sep 17 00:00:00 2001 From: Felix Date: Fri, 24 Jul 2020 15:39:06 -0400 Subject: [PATCH 001/138] tagTemplate tagger code logic --- pkg/skaffold/build/tag/git_commit_test.go | 55 +++++++++++++ pkg/skaffold/build/tag/tag_template.go | 60 ++++++++++++++ pkg/skaffold/build/tag/tag_template_test.go | 88 +++++++++++++++++++++ 3 files changed, 203 insertions(+) diff --git a/pkg/skaffold/build/tag/git_commit_test.go b/pkg/skaffold/build/tag/git_commit_test.go index 170fd5a2897..a076e8548b7 100644 --- a/pkg/skaffold/build/tag/git_commit_test.go +++ b/pkg/skaffold/build/tag/git_commit_test.go @@ -401,6 +401,61 @@ func TestGitCommit_GenerateFullyQualifiedImageName(t *testing.T) { } } +func TestGitCommit_TagTemplate(t *testing.T) { + gitCommitExample, _ := NewGitCommit("", "CommitSha") + tests := []struct { + description string + template string + customMap map[string]Tagger + expected string + createGitRepo func(string) + subDir string + }{ + { + description: "gitCommit component", + template: "{{.FOO}}", + customMap: map[string]Tagger{"FOO": gitCommitExample}, + expected: "eefe1b9c44eb0aa87199c9a079f2d48d8eb8baed", + createGitRepo: func(dir string) { + gitInit(t, dir). + write("source.go", "code"). + add("source.go"). + commit("initial") + }, + }, + { + description: "gitCommit default component", + template: "{{.GIT}}", + expected: "eefe1b9", + createGitRepo: func(dir string) { + gitInit(t, dir). + write("source.go", "code"). + add("source.go"). + commit("initial") + }, + }, + } + for _, test := range tests { + test := test + testutil.Run(t, test.description, func(t *testutil.T) { + t.Parallel() + + tmpDir := t.NewTempDir() + test.createGitRepo(tmpDir.Root()) + workspace := tmpDir.Path(test.subDir) + + c, err := NewTagTemplateTagger(test.template, test.customMap) + + t.CheckNoError(err) + + tag, err := c.GenerateTag(workspace, "test") + + t.CheckNoError(err) + t.CheckDeepEqual(test.expected, tag) + }) + } +} + func TestGitCommitSubDirectory(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { tmpDir := t.NewTempDir() diff --git a/pkg/skaffold/build/tag/tag_template.go b/pkg/skaffold/build/tag/tag_template.go index 0720af917a8..d60d81d2b53 100644 --- a/pkg/skaffold/build/tag/tag_template.go +++ b/pkg/skaffold/build/tag/tag_template.go @@ -18,12 +18,72 @@ package tag import ( "bytes" + "errors" "fmt" "text/template" "github.com/sirupsen/logrus" ) +// tagTemplateTagger implements Tagger +type tagTemplateTagger struct { + Template *template.Template + Components map[string]Tagger +} + +// NewTagTemplateTagger creates a new tagTemplateTagger +func NewTagTemplateTagger(t string, components map[string]Tagger) (Tagger, error) { + tmpl, err := ParseTagTemplate(t) + if err != nil { + return nil, fmt.Errorf("parsing template: %w", err) + } + + return &tagTemplateTagger{ + Template: tmpl, + Components: components, + }, nil +} + +// GenerateTag generates a tag from a template referencing tagging strategies. +func (t *tagTemplateTagger) GenerateTag(workingDir, imageName string) (string, error) { + customMap, err := t.EvaluateComponents(workingDir, imageName) + if err != nil { + return "", err + } + + tag, err := ExecuteTagTemplate(t.Template.Option("missingkey=error"), customMap) + if err != nil { + return "", err + } + + return tag, nil +} + +// EvaluateComponents creates a custom mapping of component names to their tagger string representation. +func (t *tagTemplateTagger) EvaluateComponents(workingDir, imageName string) (map[string]string, error) { + customMap := map[string]string{} + + gitTagger, _ := NewGitCommit("", "") + dateTimeTagger := NewDateTimeTagger("", "") + + for k, v := range map[string]Tagger{"GIT": gitTagger, "DATE": dateTimeTagger, "SHA": &ChecksumTagger{}} { + tag, _ := v.GenerateTag(workingDir, imageName) + customMap[k] = tag + } + + for k, v := range t.Components { + if _, ok := v.(*tagTemplateTagger); ok { + return nil, errors.New("invalid component tagTemplate") + } + tag, err := v.GenerateTag(workingDir, imageName) + if err != nil { + return nil, fmt.Errorf("evaluating component: %w", err) + } + customMap[k] = tag + } + return customMap, nil +} + // ParseTagTemplate is a simple wrapper to parse an tag template. func ParseTagTemplate(t string) (*template.Template, error) { return template.New("tagTemplate").Parse(t) diff --git a/pkg/skaffold/build/tag/tag_template_test.go b/pkg/skaffold/build/tag/tag_template_test.go index f11434c17e3..a718368a7e6 100644 --- a/pkg/skaffold/build/tag/tag_template_test.go +++ b/pkg/skaffold/build/tag/tag_template_test.go @@ -18,10 +18,98 @@ package tag import ( "testing" + "time" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/testutil" ) +func TestTagTemplate_GenerateTag(t *testing.T) { + aLocalTimeStamp := time.Date(2015, 03, 07, 11, 06, 39, 123456789, time.Local) + + dateTimeExample := &dateTimeTagger{ + Format: "2006-01-02", + TimeZone: "UTC", + timeFn: func() time.Time { return aLocalTimeStamp }, + } + + envTemplateExample, _ := NewEnvTemplateTagger("{{.FOO}}") + invalidEnvTemplate, _ := NewEnvTemplateTagger("{{.BAR}}") + env := []string{"FOO=BAR"} + + tagTemplateExample, _ := NewTagTemplateTagger("", nil) + + tests := []struct { + description string + template string + customMap map[string]Tagger + expected string + shouldErr bool + }{ + { + description: "empty template", + }, + { + description: "only text", + template: "foo-bar", + expected: "foo-bar", + }, + { + description: "only component (dateTime) in template, providing more components than necessary", + template: "{{.FOO}}", + customMap: map[string]Tagger{"FOO": dateTimeExample, "BAR": envTemplateExample}, + expected: "2015-03-07", + }, + { + description: "envTemplate and sha256 as components", + template: "foo-{{.FOO}}-{{.BAR}}", + customMap: map[string]Tagger{"FOO": envTemplateExample, "BAR": &ChecksumTagger{}}, + expected: "foo-BAR-latest", + }, + { + description: "using tagTemplate as a component", + template: "{{.FOO}}", + customMap: map[string]Tagger{"FOO": tagTemplateExample}, + shouldErr: true, + }, + { + description: "faulty component, envTemplate has undefined references", + template: "{{.FOO}}", + customMap: map[string]Tagger{"FOO": invalidEnvTemplate}, + shouldErr: true, + }, + { + description: "missing required components", + template: "{{.FOO}}", + shouldErr: true, + }, + { + description: "default component name SHA", + template: "{{.SHA}}", + expected: "latest", + }, + { + description: "override default components", + template: "{{.GIT}}-{{.DATE}}-{{.SHA}}", + customMap: map[string]Tagger{"GIT": dateTimeExample, "DATE": envTemplateExample, "SHA": dateTimeExample}, + expected: "2015-03-07-BAR-2015-03-07", + }, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + t.Override(&util.OSEnviron, func() []string { return env }) + + c, err := NewTagTemplateTagger(test.template, test.customMap) + + t.CheckNoError(err) + + tag, err := c.GenerateTag(".", "test") + + t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expected, tag) + }) + } +} + func TestTagTemplate_ExecuteTagTemplate(t *testing.T) { tests := []struct { description string From f2d1d6bfe1e1ec3983ed60e9cc71ed2e5f89f9ce Mon Sep 17 00:00:00 2001 From: Felix Date: Sat, 25 Jul 2020 00:27:56 -0400 Subject: [PATCH 002/138] Remove unnecessary use of t.Parallel() --- pkg/skaffold/build/tag/git_commit_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/skaffold/build/tag/git_commit_test.go b/pkg/skaffold/build/tag/git_commit_test.go index a076e8548b7..4b6e2ff4f07 100644 --- a/pkg/skaffold/build/tag/git_commit_test.go +++ b/pkg/skaffold/build/tag/git_commit_test.go @@ -377,8 +377,6 @@ func TestGitCommit_GenerateFullyQualifiedImageName(t *testing.T) { for _, test := range tests { test := test testutil.Run(t, test.description, func(t *testutil.T) { - t.Parallel() - tmpDir := t.NewTempDir() test.createGitRepo(tmpDir.Root()) workspace := tmpDir.Path(test.subDir) @@ -438,7 +436,6 @@ func TestGitCommit_TagTemplate(t *testing.T) { for _, test := range tests { test := test testutil.Run(t, test.description, func(t *testutil.T) { - t.Parallel() tmpDir := t.NewTempDir() test.createGitRepo(tmpDir.Root()) From efd14c10affa160f67e25bbd246a974086adfb40 Mon Sep 17 00:00:00 2001 From: Felix Date: Sat, 25 Jul 2020 00:29:02 -0400 Subject: [PATCH 003/138] fix linter --- pkg/skaffold/build/tag/git_commit_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/skaffold/build/tag/git_commit_test.go b/pkg/skaffold/build/tag/git_commit_test.go index 4b6e2ff4f07..2d2f78d73b4 100644 --- a/pkg/skaffold/build/tag/git_commit_test.go +++ b/pkg/skaffold/build/tag/git_commit_test.go @@ -436,7 +436,6 @@ func TestGitCommit_TagTemplate(t *testing.T) { for _, test := range tests { test := test testutil.Run(t, test.description, func(t *testutil.T) { - tmpDir := t.NewTempDir() test.createGitRepo(tmpDir.Root()) workspace := tmpDir.Path(test.subDir) From ae156144610989e1cc6f97f3e69e2d6451513aba Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Tue, 28 Jul 2020 16:39:39 -0400 Subject: [PATCH 004/138] Update pkg/skaffold/build/tag/tag_template.go Co-authored-by: Nick Kubala --- pkg/skaffold/build/tag/tag_template.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/skaffold/build/tag/tag_template.go b/pkg/skaffold/build/tag/tag_template.go index d60d81d2b53..911efecc8eb 100644 --- a/pkg/skaffold/build/tag/tag_template.go +++ b/pkg/skaffold/build/tag/tag_template.go @@ -73,7 +73,7 @@ func (t *tagTemplateTagger) EvaluateComponents(workingDir, imageName string) (ma for k, v := range t.Components { if _, ok := v.(*tagTemplateTagger); ok { - return nil, errors.New("invalid component tagTemplate") + return nil, fmt.Errorf("invalid component specified in tag template: %v", v) } tag, err := v.GenerateTag(workingDir, imageName) if err != nil { From 6d2bc5df93173075a49254ac6053c89e77bf08ca Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Tue, 28 Jul 2020 16:39:56 -0400 Subject: [PATCH 005/138] Update pkg/skaffold/build/tag/tag_template.go Co-authored-by: Nick Kubala --- pkg/skaffold/build/tag/tag_template.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/skaffold/build/tag/tag_template.go b/pkg/skaffold/build/tag/tag_template.go index 911efecc8eb..7e981737ed0 100644 --- a/pkg/skaffold/build/tag/tag_template.go +++ b/pkg/skaffold/build/tag/tag_template.go @@ -77,7 +77,7 @@ func (t *tagTemplateTagger) EvaluateComponents(workingDir, imageName string) (ma } tag, err := v.GenerateTag(workingDir, imageName) if err != nil { - return nil, fmt.Errorf("evaluating component: %w", err) + return nil, fmt.Errorf("evaluating tag template component: %w", err) } customMap[k] = tag } From 015382fb8075df892e284fb833d9670558d0232f Mon Sep 17 00:00:00 2001 From: Felix Date: Tue, 28 Jul 2020 16:40:48 -0400 Subject: [PATCH 006/138] unit tests for tagTemplate constructor --- pkg/skaffold/build/tag/tag_template_test.go | 36 +++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/pkg/skaffold/build/tag/tag_template_test.go b/pkg/skaffold/build/tag/tag_template_test.go index a718368a7e6..03d7ccb6b1f 100644 --- a/pkg/skaffold/build/tag/tag_template_test.go +++ b/pkg/skaffold/build/tag/tag_template_test.go @@ -110,6 +110,42 @@ func TestTagTemplate_GenerateTag(t *testing.T) { } } +func TestTagTemplate_NewTagTemplateTagger(t *testing.T) { + tests := []struct { + description string + template string + customMap map[string]Tagger + shouldErr bool + }{ + { + description: "valid template with nil map", + template: "{{.FOO}}", + }, + { + description: "valid template with atleast one mapping", + template: "{{.FOO}}", + customMap: map[string]Tagger{"FOO": &ChecksumTagger{}}, + }, + { + description: "invalid template with nil mapping", + template: "{{.FOO", + shouldErr: true, + }, + { + description: "invalid template with atleast one mapping", + template: "{{.FOO", + customMap: map[string]Tagger{"FOO": &ChecksumTagger{}}, + shouldErr: true, + }, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + _, err := NewTagTemplateTagger(test.template, test.customMap) + t.CheckError(test.shouldErr, err) + }) + } +} + func TestTagTemplate_ExecuteTagTemplate(t *testing.T) { tests := []struct { description string From 7f97ffd89514558f423e7ec94a14a5b53eb86898 Mon Sep 17 00:00:00 2001 From: Felix Date: Tue, 28 Jul 2020 17:05:53 -0400 Subject: [PATCH 007/138] remove unused package --- pkg/skaffold/build/tag/tag_template.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/skaffold/build/tag/tag_template.go b/pkg/skaffold/build/tag/tag_template.go index 7e981737ed0..3665d93f724 100644 --- a/pkg/skaffold/build/tag/tag_template.go +++ b/pkg/skaffold/build/tag/tag_template.go @@ -18,7 +18,6 @@ package tag import ( "bytes" - "errors" "fmt" "text/template" From 2b5dd4e37a1c06fe157efae47887af7a58266332 Mon Sep 17 00:00:00 2001 From: Felix Date: Tue, 28 Jul 2020 18:16:19 -0400 Subject: [PATCH 008/138] clarifying comment about missingkey=error --- pkg/skaffold/build/tag/env_template.go | 1 + pkg/skaffold/build/tag/tag_template.go | 1 + 2 files changed, 2 insertions(+) diff --git a/pkg/skaffold/build/tag/env_template.go b/pkg/skaffold/build/tag/env_template.go index 2a121798239..3226830fa59 100644 --- a/pkg/skaffold/build/tag/env_template.go +++ b/pkg/skaffold/build/tag/env_template.go @@ -44,6 +44,7 @@ func NewEnvTemplateTagger(t string) (Tagger, error) { // GenerateTag generates a tag from a template referencing environment variables. func (t *envTemplateTagger) GenerateTag(_, imageName string) (string, error) { + // missingkey=error throws error when map is indexed with an undefined key tag, err := util.ExecuteEnvTemplate(t.Template.Option("missingkey=error"), map[string]string{ "IMAGE_NAME": imageName, "DIGEST": "_DEPRECATED_DIGEST_", diff --git a/pkg/skaffold/build/tag/tag_template.go b/pkg/skaffold/build/tag/tag_template.go index 3665d93f724..7c563aa4f6f 100644 --- a/pkg/skaffold/build/tag/tag_template.go +++ b/pkg/skaffold/build/tag/tag_template.go @@ -50,6 +50,7 @@ func (t *tagTemplateTagger) GenerateTag(workingDir, imageName string) (string, e return "", err } + // missingkey=error throws error when map is indexed with an undefined key tag, err := ExecuteTagTemplate(t.Template.Option("missingkey=error"), customMap) if err != nil { return "", err From 2dc9e8cc18d19fcfdc4d3a564013a5f456d5cea8 Mon Sep 17 00:00:00 2001 From: Felix Date: Tue, 28 Jul 2020 19:13:07 -0400 Subject: [PATCH 009/138] Renaming of tagTemplateTagger to templateTagger. --- pkg/skaffold/build/tag/git_commit_test.go | 2 +- pkg/skaffold/build/tag/tag_template.go | 16 ++++++++-------- pkg/skaffold/build/tag/tag_template_test.go | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pkg/skaffold/build/tag/git_commit_test.go b/pkg/skaffold/build/tag/git_commit_test.go index 2d2f78d73b4..fc7991056a2 100644 --- a/pkg/skaffold/build/tag/git_commit_test.go +++ b/pkg/skaffold/build/tag/git_commit_test.go @@ -440,7 +440,7 @@ func TestGitCommit_TagTemplate(t *testing.T) { test.createGitRepo(tmpDir.Root()) workspace := tmpDir.Path(test.subDir) - c, err := NewTagTemplateTagger(test.template, test.customMap) + c, err := NewTemplateTagger(test.template, test.customMap) t.CheckNoError(err) diff --git a/pkg/skaffold/build/tag/tag_template.go b/pkg/skaffold/build/tag/tag_template.go index 7c563aa4f6f..260b3d831cf 100644 --- a/pkg/skaffold/build/tag/tag_template.go +++ b/pkg/skaffold/build/tag/tag_template.go @@ -24,27 +24,27 @@ import ( "github.com/sirupsen/logrus" ) -// tagTemplateTagger implements Tagger -type tagTemplateTagger struct { +// templateTagger implements Tagger +type templateTagger struct { Template *template.Template Components map[string]Tagger } -// NewTagTemplateTagger creates a new tagTemplateTagger -func NewTagTemplateTagger(t string, components map[string]Tagger) (Tagger, error) { +// NewTemplateTagger creates a new TemplateTagger +func NewTemplateTagger(t string, components map[string]Tagger) (Tagger, error) { tmpl, err := ParseTagTemplate(t) if err != nil { return nil, fmt.Errorf("parsing template: %w", err) } - return &tagTemplateTagger{ + return &templateTagger{ Template: tmpl, Components: components, }, nil } // GenerateTag generates a tag from a template referencing tagging strategies. -func (t *tagTemplateTagger) GenerateTag(workingDir, imageName string) (string, error) { +func (t *templateTagger) GenerateTag(workingDir, imageName string) (string, error) { customMap, err := t.EvaluateComponents(workingDir, imageName) if err != nil { return "", err @@ -60,7 +60,7 @@ func (t *tagTemplateTagger) GenerateTag(workingDir, imageName string) (string, e } // EvaluateComponents creates a custom mapping of component names to their tagger string representation. -func (t *tagTemplateTagger) EvaluateComponents(workingDir, imageName string) (map[string]string, error) { +func (t *templateTagger) EvaluateComponents(workingDir, imageName string) (map[string]string, error) { customMap := map[string]string{} gitTagger, _ := NewGitCommit("", "") @@ -72,7 +72,7 @@ func (t *tagTemplateTagger) EvaluateComponents(workingDir, imageName string) (ma } for k, v := range t.Components { - if _, ok := v.(*tagTemplateTagger); ok { + if _, ok := v.(*templateTagger); ok { return nil, fmt.Errorf("invalid component specified in tag template: %v", v) } tag, err := v.GenerateTag(workingDir, imageName) diff --git a/pkg/skaffold/build/tag/tag_template_test.go b/pkg/skaffold/build/tag/tag_template_test.go index 03d7ccb6b1f..0e5616fffbf 100644 --- a/pkg/skaffold/build/tag/tag_template_test.go +++ b/pkg/skaffold/build/tag/tag_template_test.go @@ -37,7 +37,7 @@ func TestTagTemplate_GenerateTag(t *testing.T) { invalidEnvTemplate, _ := NewEnvTemplateTagger("{{.BAR}}") env := []string{"FOO=BAR"} - tagTemplateExample, _ := NewTagTemplateTagger("", nil) + tagTemplateExample, _ := NewTemplateTagger("", nil) tests := []struct { description string @@ -99,7 +99,7 @@ func TestTagTemplate_GenerateTag(t *testing.T) { testutil.Run(t, test.description, func(t *testutil.T) { t.Override(&util.OSEnviron, func() []string { return env }) - c, err := NewTagTemplateTagger(test.template, test.customMap) + c, err := NewTemplateTagger(test.template, test.customMap) t.CheckNoError(err) @@ -110,7 +110,7 @@ func TestTagTemplate_GenerateTag(t *testing.T) { } } -func TestTagTemplate_NewTagTemplateTagger(t *testing.T) { +func TestTagTemplate_NewTemplateTagger(t *testing.T) { tests := []struct { description string template string @@ -140,7 +140,7 @@ func TestTagTemplate_NewTagTemplateTagger(t *testing.T) { } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - _, err := NewTagTemplateTagger(test.template, test.customMap) + _, err := NewTemplateTagger(test.template, test.customMap) t.CheckError(test.shouldErr, err) }) } From 375d24619032a7aaa5669f8ad4be6f0bd6d1a92f Mon Sep 17 00:00:00 2001 From: David Gageot Date: Wed, 29 Jul 2020 13:36:10 +0200 Subject: [PATCH 010/138] Add more debugging logs to helm (#4583) Signed-off-by: David Gageot --- pkg/skaffold/deploy/helm.go | 2 ++ pkg/skaffold/deploy/util.go | 1 + 2 files changed, 3 insertions(+) diff --git a/pkg/skaffold/deploy/helm.go b/pkg/skaffold/deploy/helm.go index b3eaa756edf..39f58ef2188 100644 --- a/pkg/skaffold/deploy/helm.go +++ b/pkg/skaffold/deploy/helm.go @@ -424,6 +424,8 @@ func (h *HelmDeployer) getRelease(ctx context.Context, helmVersion semver.Versio return nil }, opts) + logrus.Debug(b.String()) + return b, err } diff --git a/pkg/skaffold/deploy/util.go b/pkg/skaffold/deploy/util.go index f062d86628a..9a4a4798277 100644 --- a/pkg/skaffold/deploy/util.go +++ b/pkg/skaffold/deploy/util.go @@ -80,6 +80,7 @@ func parseReleaseInfo(namespace string, b *bufio.Reader) []Artifact { logrus.Infof(err.Error()) } else { results = append(results, *obj) + logrus.Debugf("found deployed object: %+v", obj.Obj) } } From f8efbee23c8d97a0733c716c3746babd4eeebdb7 Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Wed, 29 Jul 2020 09:42:55 -0400 Subject: [PATCH 011/138] Docs to accommodate the deprecation of envTemplate's use of {{.IMAGE_NAME}} (#4532) --- docs/content/en/docs/pipeline-stages/taggers.md | 7 +++---- docs/content/en/samples/taggers/envTemplate.yaml | 2 +- docs/content/en/samples/templating/env.yaml | 2 +- docs/content/en/schemas/v2beta6.json | 6 +++--- examples/tagging-with-environment-variables/README.md | 7 +++---- examples/tagging-with-environment-variables/skaffold.yaml | 2 +- .../examples/tagging-with-environment-variables/README.md | 7 +++---- .../tagging-with-environment-variables/skaffold.yaml | 2 +- pkg/skaffold/build/tag/git_commit_test.go | 2 -- pkg/skaffold/schema/latest/config.go | 5 ++--- 10 files changed, 18 insertions(+), 24 deletions(-) diff --git a/docs/content/en/docs/pipeline-stages/taggers.md b/docs/content/en/docs/pipeline-stages/taggers.md index 9ee99d93ae8..3e17c9e88fe 100644 --- a/docs/content/en/docs/pipeline-stages/taggers.md +++ b/docs/content/en/docs/pipeline-stages/taggers.md @@ -93,16 +93,15 @@ process. The following `build` section, for example, instructs Skaffold to build a Docker image `gcr.io/k8s-skaffold/example` with the `envTemplate` -tag policy. The tag template is `{{.IMAGE_NAME}}:{{.FOO}}`; when Skaffold +tag policy. The tag template is `{{.FOO}}`; when Skaffold finishes building the image, it will check the list of available environment variables in the system for the variable `FOO`, and use its value to tag the image. {{< alert >}} -Note
+Deprecated
-`IMAGE_NAME` is a built-in variable whose value is the `imageName` field in -the `artifacts` part of the `build` section. +The use of `IMAGE_NAME` as a built-in variable whose value is the `imageName` field in the `artifacts` part of the `build` section has been deprecated. Please use the envTemplate to express solely the tag value for the image. {{< /alert >}} ### Example diff --git a/docs/content/en/samples/taggers/envTemplate.yaml b/docs/content/en/samples/taggers/envTemplate.yaml index 45ed68ce876..98672aa478d 100644 --- a/docs/content/en/samples/taggers/envTemplate.yaml +++ b/docs/content/en/samples/taggers/envTemplate.yaml @@ -1,6 +1,6 @@ build: tagPolicy: envTemplate: - template: "{{.IMAGE_NAME}}:{{.FOO}}" + template: "{{.FOO}}" artifacts: - image: gcr.io/k8s-skaffold/example diff --git a/docs/content/en/samples/templating/env.yaml b/docs/content/en/samples/templating/env.yaml index 45ed68ce876..98672aa478d 100644 --- a/docs/content/en/samples/templating/env.yaml +++ b/docs/content/en/samples/templating/env.yaml @@ -1,6 +1,6 @@ build: tagPolicy: envTemplate: - template: "{{.IMAGE_NAME}}:{{.FOO}}" + template: "{{.FOO}}" artifacts: - image: gcr.io/k8s-skaffold/example diff --git a/docs/content/en/schemas/v2beta6.json b/docs/content/en/schemas/v2beta6.json index b08df6c0ece..e3155632aac 100755 --- a/docs/content/en/schemas/v2beta6.json +++ b/docs/content/en/schemas/v2beta6.json @@ -961,10 +961,10 @@ "properties": { "template": { "type": "string", - "description": "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.", - "x-intellij-html-description": "used to produce the image name and tag. See golang 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.", + "description": "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.", + "x-intellij-html-description": "used to produce the image name and tag. See golang text/template. The template is executed against the current environment, with those variables injected.", "examples": [ - "{{.RELEASE}}-{{.IMAGE_NAME}}" + "{{.RELEASE}}" ] } }, diff --git a/examples/tagging-with-environment-variables/README.md b/examples/tagging-with-environment-variables/README.md index beaed9c1cfa..83d9833d18e 100644 --- a/examples/tagging-with-environment-variables/README.md +++ b/examples/tagging-with-environment-variables/README.md @@ -1,6 +1,6 @@ ### Example: using the envTemplate tag policy -This example reuses the image name and uses an environment variable `FOO` to tag the image. +This example uses an environment variable `FOO` to tag the image. The way you configure it in `skaffold.yaml` is the following build stanza: ```yaml @@ -9,9 +9,8 @@ build: - image: skaffold-example tagPolicy: envTemplate: - template: "{{.IMAGE_NAME}}:{{.FOO}}" + template: "{{.FOO}}" ``` 1. define `tagPolicy` to be `envTemplate` -2. use [go templates](https://golang.org/pkg/text/template) syntax -3. The `IMAGE_NAME` variable is built-in and reuses the value defined in the artifacts' `image`. \ No newline at end of file +2. use [go templates](https://golang.org/pkg/text/template) syntax \ No newline at end of file diff --git a/examples/tagging-with-environment-variables/skaffold.yaml b/examples/tagging-with-environment-variables/skaffold.yaml index d2ea69d50f0..699293d5522 100644 --- a/examples/tagging-with-environment-variables/skaffold.yaml +++ b/examples/tagging-with-environment-variables/skaffold.yaml @@ -5,7 +5,7 @@ build: - image: skaffold-example tagPolicy: envTemplate: - template: "{{.IMAGE_NAME}}:{{.FOO}}" + template: "{{.FOO}}" deploy: kubectl: manifests: diff --git a/integration/examples/tagging-with-environment-variables/README.md b/integration/examples/tagging-with-environment-variables/README.md index beaed9c1cfa..83d9833d18e 100644 --- a/integration/examples/tagging-with-environment-variables/README.md +++ b/integration/examples/tagging-with-environment-variables/README.md @@ -1,6 +1,6 @@ ### Example: using the envTemplate tag policy -This example reuses the image name and uses an environment variable `FOO` to tag the image. +This example uses an environment variable `FOO` to tag the image. The way you configure it in `skaffold.yaml` is the following build stanza: ```yaml @@ -9,9 +9,8 @@ build: - image: skaffold-example tagPolicy: envTemplate: - template: "{{.IMAGE_NAME}}:{{.FOO}}" + template: "{{.FOO}}" ``` 1. define `tagPolicy` to be `envTemplate` -2. use [go templates](https://golang.org/pkg/text/template) syntax -3. The `IMAGE_NAME` variable is built-in and reuses the value defined in the artifacts' `image`. \ No newline at end of file +2. use [go templates](https://golang.org/pkg/text/template) syntax \ No newline at end of file diff --git a/integration/examples/tagging-with-environment-variables/skaffold.yaml b/integration/examples/tagging-with-environment-variables/skaffold.yaml index c003e7551e7..edfa4fdee9d 100644 --- a/integration/examples/tagging-with-environment-variables/skaffold.yaml +++ b/integration/examples/tagging-with-environment-variables/skaffold.yaml @@ -5,7 +5,7 @@ build: - image: skaffold-example tagPolicy: envTemplate: - template: "{{.IMAGE_NAME}}:{{.FOO}}" + template: "{{.FOO}}" deploy: kubectl: manifests: diff --git a/pkg/skaffold/build/tag/git_commit_test.go b/pkg/skaffold/build/tag/git_commit_test.go index 170fd5a2897..90234f8b099 100644 --- a/pkg/skaffold/build/tag/git_commit_test.go +++ b/pkg/skaffold/build/tag/git_commit_test.go @@ -377,8 +377,6 @@ func TestGitCommit_GenerateFullyQualifiedImageName(t *testing.T) { for _, test := range tests { test := test testutil.Run(t, test.description, func(t *testutil.T) { - t.Parallel() - tmpDir := t.NewTempDir() test.createGitRepo(tmpDir.Root()) workspace := tmpDir.Path(test.subDir) diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index d699b79deea..9db50a49989 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -152,9 +152,8 @@ 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}}`. + // with those variables injected. + // For example: `{{.RELEASE}}`. Template string `yaml:"template,omitempty" yamltags:"required"` } From 094934a54ea52a916517017aeb8ce246d538ec7e Mon Sep 17 00:00:00 2001 From: Daniel Sel Date: Wed, 29 Jul 2020 14:02:59 +0000 Subject: [PATCH 012/138] (minor) fix vendoring (#4585) --- .../go-containerregistry/pkg/v1/random/doc.go | 16 --- .../pkg/v1/random/image.go | 117 ------------------ .../pkg/v1/random/index.go | 110 ---------------- vendor/modules.txt | 1 - 4 files changed, 244 deletions(-) delete mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/random/doc.go delete mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/random/image.go delete mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/random/index.go diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/random/doc.go b/vendor/github.com/google/go-containerregistry/pkg/v1/random/doc.go deleted file mode 100644 index d3712767d26..00000000000 --- a/vendor/github.com/google/go-containerregistry/pkg/v1/random/doc.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2018 Google LLC All Rights Reserved. -// -// 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 random provides a facility for synthesizing pseudo-random images. -package random diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/random/image.go b/vendor/github.com/google/go-containerregistry/pkg/v1/random/image.go deleted file mode 100644 index 14cc43819d6..00000000000 --- a/vendor/github.com/google/go-containerregistry/pkg/v1/random/image.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2018 Google LLC All Rights Reserved. -// -// 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 random - -import ( - "archive/tar" - "bytes" - "crypto/rand" - "crypto/sha256" - "encoding/hex" - "fmt" - "io" - "io/ioutil" - mrand "math/rand" - "time" - - v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/google/go-containerregistry/pkg/v1/empty" - "github.com/google/go-containerregistry/pkg/v1/mutate" - "github.com/google/go-containerregistry/pkg/v1/partial" - "github.com/google/go-containerregistry/pkg/v1/types" -) - -// uncompressedLayer implements partial.UncompressedLayer from raw bytes. -type uncompressedLayer struct { - diffID v1.Hash - mediaType types.MediaType - content []byte -} - -// DiffID implements partial.UncompressedLayer -func (ul *uncompressedLayer) DiffID() (v1.Hash, error) { - return ul.diffID, nil -} - -// Uncompressed implements partial.UncompressedLayer -func (ul *uncompressedLayer) Uncompressed() (io.ReadCloser, error) { - return ioutil.NopCloser(bytes.NewBuffer(ul.content)), nil -} - -// MediaType returns the media type of the layer -func (ul *uncompressedLayer) MediaType() (types.MediaType, error) { - return ul.mediaType, nil -} - -var _ partial.UncompressedLayer = (*uncompressedLayer)(nil) - -// Image returns a pseudo-randomly generated Image. -func Image(byteSize, layers int64) (v1.Image, error) { - adds := make([]mutate.Addendum, 0, 5) - for i := int64(0); i < layers; i++ { - layer, err := Layer(byteSize, types.DockerLayer) - if err != nil { - return nil, err - } - adds = append(adds, mutate.Addendum{ - Layer: layer, - History: v1.History{ - Author: "random.Image", - Comment: fmt.Sprintf("this is a random history %d", i), - CreatedBy: "random", - Created: v1.Time{time.Now()}, - }, - }) - } - - return mutate.Append(empty.Image, adds...) -} - -// Layer returns a layer with pseudo-randomly generated content. -func Layer(byteSize int64, mt types.MediaType) (v1.Layer, error) { - fileName := fmt.Sprintf("random_file_%d.txt", mrand.Int()) - - // Hash the contents as we write it out to the buffer. - var b bytes.Buffer - hasher := sha256.New() - mw := io.MultiWriter(&b, hasher) - - // Write a single file with a random name and random contents. - tw := tar.NewWriter(mw) - if err := tw.WriteHeader(&tar.Header{ - Name: fileName, - Size: byteSize, - Typeflag: tar.TypeRegA, - }); err != nil { - return nil, err - } - if _, err := io.CopyN(tw, rand.Reader, byteSize); err != nil { - return nil, err - } - if err := tw.Close(); err != nil { - return nil, err - } - - h := v1.Hash{ - Algorithm: "sha256", - Hex: hex.EncodeToString(hasher.Sum(make([]byte, 0, hasher.Size()))), - } - - return partial.UncompressedToLayer(&uncompressedLayer{ - diffID: h, - mediaType: mt, - content: b.Bytes(), - }) -} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/random/index.go b/vendor/github.com/google/go-containerregistry/pkg/v1/random/index.go deleted file mode 100644 index fb992211a58..00000000000 --- a/vendor/github.com/google/go-containerregistry/pkg/v1/random/index.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2018 Google LLC All Rights Reserved. -// -// 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 random - -import ( - "bytes" - "encoding/json" - "fmt" - - v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/google/go-containerregistry/pkg/v1/partial" - "github.com/google/go-containerregistry/pkg/v1/types" -) - -type randomIndex struct { - images map[v1.Hash]v1.Image - manifest *v1.IndexManifest -} - -// Index returns a pseudo-randomly generated ImageIndex with count images, each -// having the given number of layers of size byteSize. -func Index(byteSize, layers, count int64) (v1.ImageIndex, error) { - manifest := v1.IndexManifest{ - SchemaVersion: 2, - Manifests: []v1.Descriptor{}, - } - - images := make(map[v1.Hash]v1.Image) - for i := int64(0); i < count; i++ { - img, err := Image(byteSize, layers) - if err != nil { - return nil, err - } - - rawManifest, err := img.RawManifest() - if err != nil { - return nil, err - } - digest, size, err := v1.SHA256(bytes.NewReader(rawManifest)) - if err != nil { - return nil, err - } - mediaType, err := img.MediaType() - if err != nil { - return nil, err - } - - manifest.Manifests = append(manifest.Manifests, v1.Descriptor{ - Digest: digest, - Size: size, - MediaType: mediaType, - }) - - images[digest] = img - } - - return &randomIndex{ - images: images, - manifest: &manifest, - }, nil -} - -func (i *randomIndex) MediaType() (types.MediaType, error) { - return types.OCIImageIndex, nil -} - -func (i *randomIndex) Digest() (v1.Hash, error) { - return partial.Digest(i) -} - -func (i *randomIndex) Size() (int64, error) { - return partial.Size(i) -} - -func (i *randomIndex) IndexManifest() (*v1.IndexManifest, error) { - return i.manifest, nil -} - -func (i *randomIndex) RawManifest() ([]byte, error) { - m, err := i.IndexManifest() - if err != nil { - return nil, err - } - return json.Marshal(m) -} - -func (i *randomIndex) Image(h v1.Hash) (v1.Image, error) { - if img, ok := i.images[h]; ok { - return img, nil - } - - return nil, fmt.Errorf("image not found: %v", h) -} - -func (i *randomIndex) ImageIndex(h v1.Hash) (v1.ImageIndex, error) { - // This is a single level index (for now?). - return nil, fmt.Errorf("image not found: %v", h) -} diff --git a/vendor/modules.txt b/vendor/modules.txt index d77405afe01..691b8ab4cac 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -288,7 +288,6 @@ github.com/google/go-containerregistry/pkg/v1/google github.com/google/go-containerregistry/pkg/v1/layout github.com/google/go-containerregistry/pkg/v1/mutate github.com/google/go-containerregistry/pkg/v1/partial -github.com/google/go-containerregistry/pkg/v1/random github.com/google/go-containerregistry/pkg/v1/remote github.com/google/go-containerregistry/pkg/v1/remote/transport github.com/google/go-containerregistry/pkg/v1/stream From 1a7edd88e2140d021272e97904525e7cc3c9455e Mon Sep 17 00:00:00 2001 From: David Gageot Date: Wed, 29 Jul 2020 17:24:52 +0200 Subject: [PATCH 013/138] Add helper to create log files (#4563) Not used yet. Signed-off-by: David Gageot --- pkg/skaffold/logfile/logfile.go | 41 +++++++++++++++ pkg/skaffold/logfile/logfile_test.go | 76 ++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 pkg/skaffold/logfile/logfile.go create mode 100644 pkg/skaffold/logfile/logfile_test.go diff --git a/pkg/skaffold/logfile/logfile.go b/pkg/skaffold/logfile/logfile.go new file mode 100644 index 00000000000..8e644e6049d --- /dev/null +++ b/pkg/skaffold/logfile/logfile.go @@ -0,0 +1,41 @@ +/* +Copyright 2020 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 logfile + +import ( + "fmt" + "os" + "path/filepath" + "regexp" +) + +// Create creates or truncates a file to be used to output logs. +func Create(name string) (*os.File, error) { + root := filepath.Join(os.TempDir(), "skaffold") + if err := os.MkdirAll(root, 0700); err != nil { + return nil, fmt.Errorf("unable to create temp directory %q: %w", root, err) + } + + path := filepath.Join(root, escape(name)) + return os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) +} + +var escapeRegexp = regexp.MustCompile(`[^a-zA-Z0-9-_.]`) + +func escape(s string) string { + return escapeRegexp.ReplaceAllString(s, "-") +} diff --git a/pkg/skaffold/logfile/logfile_test.go b/pkg/skaffold/logfile/logfile_test.go new file mode 100644 index 00000000000..32d731a5684 --- /dev/null +++ b/pkg/skaffold/logfile/logfile_test.go @@ -0,0 +1,76 @@ +/* +Copyright 2020 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 logfile + +import ( + "os" + "path/filepath" + "testing" + + "github.com/GoogleContainerTools/skaffold/testutil" +) + +func TestCreate(t *testing.T) { + var tests = []struct { + description string + name string + expectedName string + }{ + { + description: "create file", + name: "logs.txt", + expectedName: "logs.txt", + }, + { + description: "escape name", + name: "a/name.txt", + expectedName: "a-name.txt", + }, + } + + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + file, err := Create(test.name) + defer func() { + file.Close() + os.Remove(file.Name()) + }() + + t.CheckNoError(err) + t.CheckDeepEqual(filepath.Join(os.TempDir(), "skaffold", test.expectedName), file.Name()) + }) + } +} + +func TestEscape(t *testing.T) { + tests := []struct { + name string + expected string + }{ + {name: "img", expected: "img"}, + {name: "log.txt", expected: "log.txt"}, + {name: "project/img", expected: "project-img"}, + {name: "gcr.io/project/img", expected: "gcr.io-project-img"}, + } + for _, test := range tests { + testutil.Run(t, test.name, func(t *testutil.T) { + escaped := escape(test.name) + + t.CheckDeepEqual(test.expected, escaped) + }) + } +} From aa85937e4f66140dd437441f087f1e5f0ff13807 Mon Sep 17 00:00:00 2001 From: David Gageot Date: Wed, 29 Jul 2020 17:46:48 +0200 Subject: [PATCH 014/138] Support short build logs (#4528) Signed-off-by: David Gageot --- pkg/skaffold/build/cluster/cluster.go | 3 +- pkg/skaffold/build/cluster/types.go | 2 + pkg/skaffold/build/gcb/cloud_build.go | 3 +- pkg/skaffold/build/gcb/types.go | 2 + pkg/skaffold/build/local/local.go | 3 +- pkg/skaffold/build/local/types.go | 2 + pkg/skaffold/build/logfile.go | 59 ++++++++++++ pkg/skaffold/build/logfile_test.go | 123 ++++++++++++++++++++++++++ pkg/skaffold/build/parallel.go | 8 +- pkg/skaffold/build/parallel_test.go | 10 +-- pkg/skaffold/build/sequence.go | 2 +- pkg/skaffold/build/sequence_test.go | 2 +- 12 files changed, 205 insertions(+), 14 deletions(-) create mode 100644 pkg/skaffold/build/logfile.go create mode 100644 pkg/skaffold/build/logfile_test.go diff --git a/pkg/skaffold/build/cluster/cluster.go b/pkg/skaffold/build/cluster/cluster.go index 0f38ba38df4..db063d0dd2b 100644 --- a/pkg/skaffold/build/cluster/cluster.go +++ b/pkg/skaffold/build/cluster/cluster.go @@ -45,7 +45,8 @@ func (b *Builder) Build(ctx context.Context, out io.Writer, tags tag.ImageTags, defer teardownDockerConfigSecret() } - return build.InParallel(ctx, out, tags, artifacts, b.buildArtifact, b.ClusterDetails.Concurrency) + builder := build.WithLogFile(b.buildArtifact, b.suppressLogs) + return build.InParallel(ctx, out, tags, artifacts, builder, b.ClusterDetails.Concurrency) } func (b *Builder) buildArtifact(ctx context.Context, out io.Writer, artifact *latest.Artifact, tag string) (string, error) { diff --git a/pkg/skaffold/build/cluster/types.go b/pkg/skaffold/build/cluster/types.go index 32d5d48c673..ebff7278953 100644 --- a/pkg/skaffold/build/cluster/types.go +++ b/pkg/skaffold/build/cluster/types.go @@ -35,6 +35,7 @@ type Builder struct { kubeContext string timeout time.Duration insecureRegistries map[string]bool + suppressLogs []string } // NewBuilder creates a new Builder that builds artifacts on cluster. @@ -50,6 +51,7 @@ func NewBuilder(runCtx *runcontext.RunContext) (*Builder, error) { timeout: timeout, kubeContext: runCtx.KubeContext, insecureRegistries: runCtx.InsecureRegistries, + suppressLogs: runCtx.Opts.SuppressLogs, }, nil } diff --git a/pkg/skaffold/build/gcb/cloud_build.go b/pkg/skaffold/build/gcb/cloud_build.go index 28d04e9f8f8..38cf5b20bdd 100644 --- a/pkg/skaffold/build/gcb/cloud_build.go +++ b/pkg/skaffold/build/gcb/cloud_build.go @@ -46,7 +46,8 @@ import ( // Build builds a list of artifacts with Google Cloud Build. func (b *Builder) Build(ctx context.Context, out io.Writer, tags tag.ImageTags, artifacts []*latest.Artifact) ([]build.Artifact, error) { - return build.InParallel(ctx, out, tags, artifacts, b.buildArtifactWithCloudBuild, b.GoogleCloudBuild.Concurrency) + builder := build.WithLogFile(b.buildArtifactWithCloudBuild, b.suppressLogs) + return build.InParallel(ctx, out, tags, artifacts, builder, b.GoogleCloudBuild.Concurrency) } func (b *Builder) buildArtifactWithCloudBuild(ctx context.Context, out io.Writer, artifact *latest.Artifact, tag string) (string, error) { diff --git a/pkg/skaffold/build/gcb/types.go b/pkg/skaffold/build/gcb/types.go index 39e26bf0046..1539acc9abb 100644 --- a/pkg/skaffold/build/gcb/types.go +++ b/pkg/skaffold/build/gcb/types.go @@ -79,6 +79,7 @@ type Builder struct { *latest.GoogleCloudBuild skipTests bool insecureRegistries map[string]bool + suppressLogs []string } // NewBuilder creates a new Builder that builds artifacts with Google Cloud Build. @@ -87,6 +88,7 @@ func NewBuilder(runCtx *runcontext.RunContext) *Builder { GoogleCloudBuild: runCtx.Cfg.Build.GoogleCloudBuild, skipTests: runCtx.Opts.SkipTests, insecureRegistries: runCtx.InsecureRegistries, + suppressLogs: runCtx.Opts.SuppressLogs, } } diff --git a/pkg/skaffold/build/local/local.go b/pkg/skaffold/build/local/local.go index 114dbbea111..664ab396941 100644 --- a/pkg/skaffold/build/local/local.go +++ b/pkg/skaffold/build/local/local.go @@ -42,7 +42,8 @@ func (b *Builder) Build(ctx context.Context, out io.Writer, tags tag.ImageTags, } defer b.localDocker.Close() - return build.InParallel(ctx, out, tags, artifacts, b.buildArtifact, *b.cfg.Concurrency) + builder := build.WithLogFile(b.buildArtifact, b.suppressLogs) + return build.InParallel(ctx, out, tags, artifacts, builder, *b.cfg.Concurrency) } func (b *Builder) buildArtifact(ctx context.Context, out io.Writer, a *latest.Artifact, tag string) (string, error) { diff --git a/pkg/skaffold/build/local/types.go b/pkg/skaffold/build/local/types.go index 4458e2bd1ba..b488f87a51b 100644 --- a/pkg/skaffold/build/local/types.go +++ b/pkg/skaffold/build/local/types.go @@ -43,6 +43,7 @@ type Builder struct { kubeContext string builtImages []string insecureRegistries map[string]bool + suppressLogs []string } // external dependencies are wrapped @@ -85,6 +86,7 @@ func NewBuilder(runCtx *runcontext.RunContext) (*Builder, error) { prune: runCtx.Opts.Prune(), pruneChildren: !runCtx.Opts.NoPruneChildren, insecureRegistries: runCtx.InsecureRegistries, + suppressLogs: runCtx.Opts.SuppressLogs, }, nil } diff --git a/pkg/skaffold/build/logfile.go b/pkg/skaffold/build/logfile.go new file mode 100644 index 00000000000..9c9f048c683 --- /dev/null +++ b/pkg/skaffold/build/logfile.go @@ -0,0 +1,59 @@ +/* +Copyright 2020 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 build + +import ( + "bytes" + "context" + "fmt" + "io" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/logfile" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" +) + +// WithLogFile wraps an `artifactBuilder` so that it optionally outputs its logs to a file. +func WithLogFile(builder ArtifactBuilder, suppressedLogs []string) ArtifactBuilder { + // TODO(dgageot): this should probably be moved somewhere else. + if !(util.StrSliceContains(suppressedLogs, "build") || util.StrSliceContains(suppressedLogs, "all")) { + return builder + } + + return func(ctx context.Context, out io.Writer, artifact *latest.Artifact, tag string) (string, error) { + file, err := logfile.Create(artifact.ImageName + ".log") + if err != nil { + return "", fmt.Errorf("unable to create log file for %s: %w", artifact.ImageName, err) + } + fmt.Fprintln(out, " - writing logs to", file.Name()) + + // Print logs to a memory buffer and to a file. + var buf bytes.Buffer + w := io.MultiWriter(file, &buf) + + // Run the build. + digest, err := builder(ctx, w, artifact, tag) + + // After the build finishes, close the log file. If the build failed, print the full log to the console. + file.Close() + if err != nil { + buf.WriteTo(out) + } + + return digest, err + } +} diff --git a/pkg/skaffold/build/logfile_test.go b/pkg/skaffold/build/logfile_test.go new file mode 100644 index 00000000000..cc9dfd11a59 --- /dev/null +++ b/pkg/skaffold/build/logfile_test.go @@ -0,0 +1,123 @@ +/* +Copyright 2020 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 build + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "path/filepath" + "strings" + "testing" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/GoogleContainerTools/skaffold/testutil" +) + +func TestWithLogFile(t *testing.T) { + tests := []struct { + description string + builder ArtifactBuilder + suppress []string + shouldErr bool + expectedDigest string + logsFound []string + logsNotFound []string + }{ + { + description: "all logs", + builder: fakeBuilder, + suppress: nil, + shouldErr: false, + expectedDigest: "digest", + logsFound: []string{"building img with tag img:123"}, + logsNotFound: []string{" - writing logs to ", filepath.Join("skaffold", "img.log")}, + }, + { + description: "suppress build logs", + builder: fakeBuilder, + suppress: []string{"build"}, + shouldErr: false, + expectedDigest: "digest", + logsFound: []string{" - writing logs to ", filepath.Join("skaffold", "img.log")}, + logsNotFound: []string{"building img with tag img:123"}, + }, + { + description: "suppress all logs", + builder: fakeBuilder, + suppress: []string{"all"}, + shouldErr: false, + expectedDigest: "digest", + logsFound: []string{" - writing logs to ", filepath.Join("skaffold", "img.log")}, + logsNotFound: []string{"building img with tag img:123"}, + }, + { + description: "suppress only deploy logs", + builder: fakeBuilder, + suppress: []string{"deploy"}, + shouldErr: false, + expectedDigest: "digest", + logsFound: []string{"building img with tag img:123"}, + logsNotFound: []string{" - writing logs to ", filepath.Join("skaffold", "img.log")}, + }, + { + description: "failed build - all logs", + builder: fakeFailingBuilder, + suppress: nil, + shouldErr: true, + expectedDigest: "", + logsFound: []string{"failed to build img with tag img:123"}, + logsNotFound: []string{" - writing logs to ", filepath.Join("skaffold", "img.log")}, + }, + { + description: "failed build - suppressed logs", + builder: fakeFailingBuilder, + suppress: []string{"build"}, + shouldErr: true, + expectedDigest: "", + logsFound: []string{" - writing logs to ", filepath.Join("skaffold", "img.log"), "failed to build img with tag img:123"}, + }, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + var out bytes.Buffer + + builder := WithLogFile(test.builder, test.suppress) + digest, err := builder(context.Background(), &out, &latest.Artifact{ImageName: "img"}, "img:123") + + t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expectedDigest, digest) + for _, found := range test.logsFound { + t.CheckContains(found, out.String()) + } + for _, notFound := range test.logsNotFound { + t.CheckFalse(strings.Contains(out.String(), notFound)) + } + }) + } +} + +func fakeBuilder(_ context.Context, out io.Writer, a *latest.Artifact, tag string) (string, error) { + fmt.Fprintln(out, "building", a.ImageName, "with tag", tag) + return "digest", nil +} + +func fakeFailingBuilder(_ context.Context, out io.Writer, a *latest.Artifact, tag string) (string, error) { + fmt.Fprintln(out, "failed to build", a.ImageName, "with tag", tag) + return "", errors.New("bug") +} diff --git a/pkg/skaffold/build/parallel.go b/pkg/skaffold/build/parallel.go index 6dfae0ab021..2d04b4c2533 100644 --- a/pkg/skaffold/build/parallel.go +++ b/pkg/skaffold/build/parallel.go @@ -31,7 +31,7 @@ import ( const bufferedLinesPerArtifact = 10000 -type artifactBuilder func(ctx context.Context, out io.Writer, artifact *latest.Artifact, tag string) (string, error) +type ArtifactBuilder func(ctx context.Context, out io.Writer, artifact *latest.Artifact, tag string) (string, error) // For testing var ( @@ -40,7 +40,7 @@ var ( ) // InParallel builds a list of artifacts in parallel but prints the logs in sequential order. -func InParallel(ctx context.Context, out io.Writer, tags tag.ImageTags, artifacts []*latest.Artifact, buildArtifact artifactBuilder, concurrency int) ([]Artifact, error) { +func InParallel(ctx context.Context, out io.Writer, tags tag.ImageTags, artifacts []*latest.Artifact, buildArtifact ArtifactBuilder, concurrency int) ([]Artifact, error) { if len(artifacts) == 0 { return nil, nil } @@ -87,7 +87,7 @@ func InParallel(ctx context.Context, out io.Writer, tags tag.ImageTags, artifact return collectResults(out, artifacts, results, outputs) } -func runBuild(ctx context.Context, cw io.WriteCloser, tags tag.ImageTags, artifact *latest.Artifact, results *sync.Map, build artifactBuilder) { +func runBuild(ctx context.Context, cw io.WriteCloser, tags tag.ImageTags, artifact *latest.Artifact, results *sync.Map, build ArtifactBuilder) { event.BuildInProgress(artifact.ImageName) finalTag, err := getBuildResult(ctx, cw, tags, artifact, build) @@ -110,7 +110,7 @@ func readOutputAndWriteToChannel(r io.Reader, lines chan string) { close(lines) } -func getBuildResult(ctx context.Context, cw io.Writer, tags tag.ImageTags, artifact *latest.Artifact, build artifactBuilder) (string, error) { +func getBuildResult(ctx context.Context, cw io.Writer, tags tag.ImageTags, artifact *latest.Artifact, build ArtifactBuilder) (string, error) { color.Default.Fprintf(cw, "Building [%s]...\n", artifact.ImageName) tag, present := tags[artifact.ImageName] if !present { diff --git a/pkg/skaffold/build/parallel_test.go b/pkg/skaffold/build/parallel_test.go index 93c6e26f867..d73d8067e5e 100644 --- a/pkg/skaffold/build/parallel_test.go +++ b/pkg/skaffold/build/parallel_test.go @@ -35,7 +35,7 @@ import ( func TestGetBuild(t *testing.T) { tests := []struct { description string - buildArtifact artifactBuilder + buildArtifact ArtifactBuilder tags tag.ImageTags expectedTag string expectedOut string @@ -197,7 +197,7 @@ func TestCollectResults(t *testing.T) { func TestInParallel(t *testing.T) { tests := []struct { description string - buildFunc artifactBuilder + buildFunc ArtifactBuilder expected string }{ { @@ -302,14 +302,14 @@ func TestInParallelConcurrency(t *testing.T) { func TestInParallelForArgs(t *testing.T) { tests := []struct { description string - inSeqFunc func(context.Context, io.Writer, tag.ImageTags, []*latest.Artifact, artifactBuilder) ([]Artifact, error) - buildArtifact artifactBuilder + inSeqFunc func(context.Context, io.Writer, tag.ImageTags, []*latest.Artifact, ArtifactBuilder) ([]Artifact, error) + buildArtifact ArtifactBuilder artifactLen int expected []Artifact }{ { description: "runs in sequence for 1 artifact", - inSeqFunc: func(context.Context, io.Writer, tag.ImageTags, []*latest.Artifact, artifactBuilder) ([]Artifact, error) { + inSeqFunc: func(context.Context, io.Writer, tag.ImageTags, []*latest.Artifact, ArtifactBuilder) ([]Artifact, error) { return []Artifact{{ImageName: "singleArtifact", Tag: "one"}}, nil }, artifactLen: 1, diff --git a/pkg/skaffold/build/sequence.go b/pkg/skaffold/build/sequence.go index ff22ccdb268..e63ed7b70af 100644 --- a/pkg/skaffold/build/sequence.go +++ b/pkg/skaffold/build/sequence.go @@ -28,7 +28,7 @@ import ( ) // InSequence builds a list of artifacts in sequence. -func InSequence(ctx context.Context, out io.Writer, tags tag.ImageTags, artifacts []*latest.Artifact, buildArtifact artifactBuilder) ([]Artifact, error) { +func InSequence(ctx context.Context, out io.Writer, tags tag.ImageTags, artifacts []*latest.Artifact, buildArtifact ArtifactBuilder) ([]Artifact, error) { var builds []Artifact for _, artifact := range artifacts { diff --git a/pkg/skaffold/build/sequence_test.go b/pkg/skaffold/build/sequence_test.go index 48413a98455..d9dc3d19479 100644 --- a/pkg/skaffold/build/sequence_test.go +++ b/pkg/skaffold/build/sequence_test.go @@ -33,7 +33,7 @@ import ( func TestInSequence(t *testing.T) { tests := []struct { description string - buildArtifact artifactBuilder + buildArtifact ArtifactBuilder tags tag.ImageTags expectedArtifacts []Artifact expectedOut string From a99cc8cbe727d297b4a70de47faca7741d6f2222 Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 29 Jul 2020 14:22:17 -0400 Subject: [PATCH 015/138] tagTemplate user facing changes --- pkg/skaffold/runner/new.go | 44 +++++++++++++ pkg/skaffold/runner/new_test.go | 92 ++++++++++++++++++++++++++++ pkg/skaffold/schema/latest/config.go | 24 ++++++++ 3 files changed, 160 insertions(+) create mode 100644 pkg/skaffold/runner/new_test.go diff --git a/pkg/skaffold/runner/new.go b/pkg/skaffold/runner/new.go index 2c336917f74..2a0492174be 100644 --- a/pkg/skaffold/runner/new.go +++ b/pkg/skaffold/runner/new.go @@ -230,7 +230,51 @@ func getTagger(runCtx *runcontext.RunContext) (tag.Tagger, error) { case t.DateTimeTagger != nil: return tag.NewDateTimeTagger(t.DateTimeTagger.Format, t.DateTimeTagger.TimeZone), nil + case t.TagTemplateTagger != nil: + components, err := CreateComponents(t.TagTemplateTagger) + + if err != nil { + return nil, fmt.Errorf("creating components: %w", err) + } + + return tag.NewTemplateTagger(t.TagTemplateTagger.Template, components) + default: return nil, fmt.Errorf("unknown tagger for strategy %+v", t) } } + +// CreateComponents creates a map of taggers for TagTemplateTagger +func CreateComponents(t *latest.TagTemplateTagger) (map[string]tag.Tagger, error) { + components := map[string]tag.Tagger{} + + for _, taggerComponent := range t.Components { + name, c := taggerComponent.Name, taggerComponent.Component + + if _, ok := components[name]; ok { + return nil, fmt.Errorf("multiple components with name %s", name) + } + + switch { + case c.EnvTemplateTagger != nil: + components[name], _ = tag.NewEnvTemplateTagger(c.EnvTemplateTagger.Template) + + case c.ShaTagger != nil: + components[name] = &tag.ChecksumTagger{} + + case c.GitTagger != nil: + components[name], _ = tag.NewGitCommit(c.GitTagger.Prefix, c.GitTagger.Variant) + + case c.DateTimeTagger != nil: + components[name] = tag.NewDateTimeTagger(c.DateTimeTagger.Format, c.DateTimeTagger.TimeZone) + + case c.TagTemplateTagger != nil: + return nil, fmt.Errorf("cannot use tagTemplate as a component: %s %+v", name, c) + + default: + return nil, fmt.Errorf("unknown component for tagTemplate: %s %+v", name, c) + } + } + + return components, nil +} diff --git a/pkg/skaffold/runner/new_test.go b/pkg/skaffold/runner/new_test.go new file mode 100644 index 00000000000..09033748c14 --- /dev/null +++ b/pkg/skaffold/runner/new_test.go @@ -0,0 +1,92 @@ +/* +Copyright 2020 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 runner + +import ( + "testing" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/tag" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/GoogleContainerTools/skaffold/testutil" +) + +func TestCreateComponents(t *testing.T) { + gitExample, _ := tag.NewGitCommit("", "") + envExample, _ := tag.NewEnvTemplateTagger("test") + + tests := []struct { + description string + tagTemplateTagger *latest.TagTemplateTagger + expected map[string]tag.Tagger + shouldErr bool + }{ + { + description: "correct component types", + tagTemplateTagger: &latest.TagTemplateTagger{ + Components: []latest.TaggerComponent{ + latest.TaggerComponent{Name: "FOO", Component: latest.TagPolicy{GitTagger: &latest.GitTagger{}}}, + latest.TaggerComponent{Name: "FOE", Component: latest.TagPolicy{ShaTagger: &latest.ShaTagger{}}}, + latest.TaggerComponent{Name: "BAR", Component: latest.TagPolicy{EnvTemplateTagger: &latest.EnvTemplateTagger{Template: "test"}}}, + latest.TaggerComponent{Name: "BAT", Component: latest.TagPolicy{DateTimeTagger: &latest.DateTimeTagger{}}}, + }, + }, + expected: map[string]tag.Tagger{ + "FOO": gitExample, + "FOE": &tag.ChecksumTagger{}, + "BAR": envExample, + "BAT": tag.NewDateTimeTagger("", ""), + }, + }, + { + description: "tagTemplate is an invalid component", + tagTemplateTagger: &latest.TagTemplateTagger{ + Components: []latest.TaggerComponent{ + latest.TaggerComponent{Name: "FOO", Component: latest.TagPolicy{TagTemplateTagger: &latest.TagTemplateTagger{Template: "test"}}}, + }, + }, + shouldErr: true, + }, + { + description: "recurring names", + tagTemplateTagger: &latest.TagTemplateTagger{ + Components: []latest.TaggerComponent{ + latest.TaggerComponent{Name: "FOO", Component: latest.TagPolicy{GitTagger: &latest.GitTagger{}}}, + latest.TaggerComponent{Name: "FOO", Component: latest.TagPolicy{GitTagger: &latest.GitTagger{}}}, + }, + }, + shouldErr: true, + }, + { + description: "unknown component", + tagTemplateTagger: &latest.TagTemplateTagger{ + Components: []latest.TaggerComponent{ + latest.TaggerComponent{Name: "FOO", Component: latest.TagPolicy{}}, + }, + }, + shouldErr: true, + }, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + components, err := CreateComponents(test.tagTemplateTagger) + t.CheckErrorAndDeepEqual(test.shouldErr, err, len(test.expected), len(components)) + for k, v := range test.expected { + t.CheckTypeEquality(v, components[k]) + } + }) + } +} diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 9db50a49989..bd6c580cffd 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -128,6 +128,9 @@ type TagPolicy struct { // DateTimeTagger *beta* tags images with the build timestamp. DateTimeTagger *DateTimeTagger `yaml:"dateTime,omitempty" yamltags:"oneOf=tag"` + + // TagTemplateTagger *beta* tags images with a configurable template string *composed of other taggers*. + TagTemplateTagger *TagTemplateTagger `yaml:"tagTemplate,omitempty" yamltags:"oneOf=tag"` } // ShaTagger *beta* tags images with their sha256 digest. @@ -170,6 +173,27 @@ type DateTimeTagger struct { TimeZone string `yaml:"timezone,omitempty"` } +// TagTemplateTagger *beta* tags images with a configurable template string. +type TagTemplateTagger 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 provided components with those variables injected. + // For example: `{{.DATE}}` where DATE references a TaggerComponent. + Template string `yaml:"template,omitempty" yamltags:"required"` + + // Components lists TaggerComponents that the template (see field above) can be executed against. + Components []TaggerComponent `yaml:"components,omitempty"` +} + +// TaggerComponent *beta* is a component of TagTemplateTagger. +type TaggerComponent struct { + // Name is an identifier for the component. + Name string `yaml:"name,omitempty"` + + // Component is a tagging strategy to be used in TagTemplateTagger. + Component TagPolicy `yaml:",inline"` +} + // BuildType contains the specific implementation and parameters needed // for the build step. Only one field should be populated. type BuildType struct { From c01efca8967b6779af5b7d98e80a5969a131984d Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 29 Jul 2020 14:37:26 -0400 Subject: [PATCH 016/138] renaming of tagTemplateTagger to templateTagger --- pkg/skaffold/runner/new.go | 12 ++++++------ pkg/skaffold/runner/new_test.go | 20 ++++++++++---------- pkg/skaffold/schema/latest/config.go | 12 ++++++------ 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/pkg/skaffold/runner/new.go b/pkg/skaffold/runner/new.go index 2a0492174be..b8182a14013 100644 --- a/pkg/skaffold/runner/new.go +++ b/pkg/skaffold/runner/new.go @@ -230,22 +230,22 @@ func getTagger(runCtx *runcontext.RunContext) (tag.Tagger, error) { case t.DateTimeTagger != nil: return tag.NewDateTimeTagger(t.DateTimeTagger.Format, t.DateTimeTagger.TimeZone), nil - case t.TagTemplateTagger != nil: - components, err := CreateComponents(t.TagTemplateTagger) + case t.TemplateTagger != nil: + components, err := CreateComponents(t.TemplateTagger) if err != nil { return nil, fmt.Errorf("creating components: %w", err) } - return tag.NewTemplateTagger(t.TagTemplateTagger.Template, components) + return tag.NewTemplateTagger(t.TemplateTagger.Template, components) default: return nil, fmt.Errorf("unknown tagger for strategy %+v", t) } } -// CreateComponents creates a map of taggers for TagTemplateTagger -func CreateComponents(t *latest.TagTemplateTagger) (map[string]tag.Tagger, error) { +// CreateComponents creates a map of taggers for TemplateTagger +func CreateComponents(t *latest.TemplateTagger) (map[string]tag.Tagger, error) { components := map[string]tag.Tagger{} for _, taggerComponent := range t.Components { @@ -268,7 +268,7 @@ func CreateComponents(t *latest.TagTemplateTagger) (map[string]tag.Tagger, error case c.DateTimeTagger != nil: components[name] = tag.NewDateTimeTagger(c.DateTimeTagger.Format, c.DateTimeTagger.TimeZone) - case c.TagTemplateTagger != nil: + case c.TemplateTagger != nil: return nil, fmt.Errorf("cannot use tagTemplate as a component: %s %+v", name, c) default: diff --git a/pkg/skaffold/runner/new_test.go b/pkg/skaffold/runner/new_test.go index 09033748c14..96e8e305dd3 100644 --- a/pkg/skaffold/runner/new_test.go +++ b/pkg/skaffold/runner/new_test.go @@ -29,14 +29,14 @@ func TestCreateComponents(t *testing.T) { envExample, _ := tag.NewEnvTemplateTagger("test") tests := []struct { - description string - tagTemplateTagger *latest.TagTemplateTagger - expected map[string]tag.Tagger - shouldErr bool + description string + templateTagger *latest.TemplateTagger + expected map[string]tag.Tagger + shouldErr bool }{ { description: "correct component types", - tagTemplateTagger: &latest.TagTemplateTagger{ + templateTagger: &latest.TemplateTagger{ Components: []latest.TaggerComponent{ latest.TaggerComponent{Name: "FOO", Component: latest.TagPolicy{GitTagger: &latest.GitTagger{}}}, latest.TaggerComponent{Name: "FOE", Component: latest.TagPolicy{ShaTagger: &latest.ShaTagger{}}}, @@ -53,16 +53,16 @@ func TestCreateComponents(t *testing.T) { }, { description: "tagTemplate is an invalid component", - tagTemplateTagger: &latest.TagTemplateTagger{ + templateTagger: &latest.TemplateTagger{ Components: []latest.TaggerComponent{ - latest.TaggerComponent{Name: "FOO", Component: latest.TagPolicy{TagTemplateTagger: &latest.TagTemplateTagger{Template: "test"}}}, + latest.TaggerComponent{Name: "FOO", Component: latest.TagPolicy{TemplateTagger: &latest.TemplateTagger{Template: "test"}}}, }, }, shouldErr: true, }, { description: "recurring names", - tagTemplateTagger: &latest.TagTemplateTagger{ + templateTagger: &latest.TemplateTagger{ Components: []latest.TaggerComponent{ latest.TaggerComponent{Name: "FOO", Component: latest.TagPolicy{GitTagger: &latest.GitTagger{}}}, latest.TaggerComponent{Name: "FOO", Component: latest.TagPolicy{GitTagger: &latest.GitTagger{}}}, @@ -72,7 +72,7 @@ func TestCreateComponents(t *testing.T) { }, { description: "unknown component", - tagTemplateTagger: &latest.TagTemplateTagger{ + templateTagger: &latest.TemplateTagger{ Components: []latest.TaggerComponent{ latest.TaggerComponent{Name: "FOO", Component: latest.TagPolicy{}}, }, @@ -82,7 +82,7 @@ func TestCreateComponents(t *testing.T) { } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - components, err := CreateComponents(test.tagTemplateTagger) + components, err := CreateComponents(test.templateTagger) t.CheckErrorAndDeepEqual(test.shouldErr, err, len(test.expected), len(components)) for k, v := range test.expected { t.CheckTypeEquality(v, components[k]) diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index bd6c580cffd..565b8625ccb 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -129,8 +129,8 @@ type TagPolicy struct { // DateTimeTagger *beta* tags images with the build timestamp. DateTimeTagger *DateTimeTagger `yaml:"dateTime,omitempty" yamltags:"oneOf=tag"` - // TagTemplateTagger *beta* tags images with a configurable template string *composed of other taggers*. - TagTemplateTagger *TagTemplateTagger `yaml:"tagTemplate,omitempty" yamltags:"oneOf=tag"` + // TemplateTagger *beta* tags images with a configurable template string *composed of other taggers*. + TemplateTagger *TemplateTagger `yaml:"tagTemplate,omitempty" yamltags:"oneOf=tag"` } // ShaTagger *beta* tags images with their sha256 digest. @@ -173,8 +173,8 @@ type DateTimeTagger struct { TimeZone string `yaml:"timezone,omitempty"` } -// TagTemplateTagger *beta* tags images with a configurable template string. -type TagTemplateTagger struct { +// TemplateTagger *beta* tags images with a configurable template string. +type TemplateTagger 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 provided components with those variables injected. @@ -185,12 +185,12 @@ type TagTemplateTagger struct { Components []TaggerComponent `yaml:"components,omitempty"` } -// TaggerComponent *beta* is a component of TagTemplateTagger. +// TaggerComponent *beta* is a component of TemplateTagger. type TaggerComponent struct { // Name is an identifier for the component. Name string `yaml:"name,omitempty"` - // Component is a tagging strategy to be used in TagTemplateTagger. + // Component is a tagging strategy to be used in TemplateTagger. Component TagPolicy `yaml:",inline"` } From 3261fe49ea5984be93df7d21131a971de9f706cb Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 29 Jul 2020 14:50:33 -0400 Subject: [PATCH 017/138] generate schemas, fix formatting --- docs/content/en/schemas/v2beta6.json | 163 ++++++++++++++++++++++----- pkg/skaffold/runner/new_test.go | 16 +-- 2 files changed, 145 insertions(+), 34 deletions(-) diff --git a/docs/content/en/schemas/v2beta6.json b/docs/content/en/schemas/v2beta6.json index e3155632aac..38c583d3ad5 100755 --- a/docs/content/en/schemas/v2beta6.json +++ b/docs/content/en/schemas/v2beta6.json @@ -2087,38 +2087,149 @@ "description": "specifies which local files to sync to remote folders.", "x-intellij-html-description": "specifies which local files to sync to remote folders." }, - "TagPolicy": { + "TaggerComponent": { + "anyOf": [ + { + "properties": { + "name": { + "type": "string", + "description": "an identifier for the component.", + "x-intellij-html-description": "an identifier for the component." + } + }, + "preferredOrder": [ + "name" + ], + "additionalProperties": false + }, + { + "properties": { + "gitCommit": { + "$ref": "#/definitions/GitTagger", + "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." + }, + "name": { + "type": "string", + "description": "an identifier for the component.", + "x-intellij-html-description": "an identifier for the component." + } + }, + "preferredOrder": [ + "name", + "gitCommit" + ], + "additionalProperties": false + }, + { + "properties": { + "name": { + "type": "string", + "description": "an identifier for the component.", + "x-intellij-html-description": "an identifier for the component." + }, + "sha256": { + "$ref": "#/definitions/ShaTagger", + "description": "*beta* tags images with their sha256 digest.", + "x-intellij-html-description": "beta tags images with their sha256 digest." + } + }, + "preferredOrder": [ + "name", + "sha256" + ], + "additionalProperties": false + }, + { + "properties": { + "envTemplate": { + "$ref": "#/definitions/EnvTemplateTagger", + "description": "*beta* tags images with a configurable template string.", + "x-intellij-html-description": "beta tags images with a configurable template string." + }, + "name": { + "type": "string", + "description": "an identifier for the component.", + "x-intellij-html-description": "an identifier for the component." + } + }, + "preferredOrder": [ + "name", + "envTemplate" + ], + "additionalProperties": false + }, + { + "properties": { + "dateTime": { + "$ref": "#/definitions/DateTimeTagger", + "description": "*beta* tags images with the build timestamp.", + "x-intellij-html-description": "beta tags images with the build timestamp." + }, + "name": { + "type": "string", + "description": "an identifier for the component.", + "x-intellij-html-description": "an identifier for the component." + } + }, + "preferredOrder": [ + "name", + "dateTime" + ], + "additionalProperties": false + }, + { + "properties": { + "name": { + "type": "string", + "description": "an identifier for the component.", + "x-intellij-html-description": "an identifier for the component." + }, + "tagTemplate": { + "$ref": "#/definitions/TemplateTagger", + "description": "*beta* tags images with a configurable template string *composed of other taggers*.", + "x-intellij-html-description": "beta tags images with a configurable template string composed of other taggers." + } + }, + "preferredOrder": [ + "name", + "tagTemplate" + ], + "additionalProperties": false + } + ], + "description": "*beta* a component of TemplateTagger.", + "x-intellij-html-description": "beta a component of TemplateTagger." + }, + "TemplateTagger": { + "required": [ + "template" + ], "properties": { - "dateTime": { - "$ref": "#/definitions/DateTimeTagger", - "description": "*beta* tags images with the build timestamp.", - "x-intellij-html-description": "beta tags images with the build timestamp." - }, - "envTemplate": { - "$ref": "#/definitions/EnvTemplateTagger", - "description": "*beta* tags images with a configurable template string.", - "x-intellij-html-description": "beta tags images with a configurable template string." - }, - "gitCommit": { - "$ref": "#/definitions/GitTagger", - "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." - }, - "sha256": { - "$ref": "#/definitions/ShaTagger", - "description": "*beta* tags images with their sha256 digest.", - "x-intellij-html-description": "beta tags images with their sha256 digest." + "components": { + "items": { + "$ref": "#/definitions/TaggerComponent" + }, + "type": "array", + "description": "TaggerComponents that the template (see field above) can be executed against.", + "x-intellij-html-description": "TaggerComponents that the template (see field above) can be executed against." + }, + "template": { + "type": "string", + "description": "used to produce the image name and tag. See golang [text/template](https://golang.org/pkg/text/template/). The template is executed against the provided components with those variables injected.", + "x-intellij-html-description": "used to produce the image name and tag. See golang text/template. The template is executed against the provided components with those variables injected.", + "examples": [ + "{{.DATE}}" + ] } }, "preferredOrder": [ - "gitCommit", - "sha256", - "envTemplate", - "dateTime" + "template", + "components" ], "additionalProperties": false, - "description": "contains all the configuration for the tagging step.", - "x-intellij-html-description": "contains all the configuration for the tagging step." + "description": "*beta* tags images with a configurable template string.", + "x-intellij-html-description": "beta tags images with a configurable template string." }, "TestCase": { "required": [ diff --git a/pkg/skaffold/runner/new_test.go b/pkg/skaffold/runner/new_test.go index 96e8e305dd3..0a246d9db50 100644 --- a/pkg/skaffold/runner/new_test.go +++ b/pkg/skaffold/runner/new_test.go @@ -38,10 +38,10 @@ func TestCreateComponents(t *testing.T) { description: "correct component types", templateTagger: &latest.TemplateTagger{ Components: []latest.TaggerComponent{ - latest.TaggerComponent{Name: "FOO", Component: latest.TagPolicy{GitTagger: &latest.GitTagger{}}}, - latest.TaggerComponent{Name: "FOE", Component: latest.TagPolicy{ShaTagger: &latest.ShaTagger{}}}, - latest.TaggerComponent{Name: "BAR", Component: latest.TagPolicy{EnvTemplateTagger: &latest.EnvTemplateTagger{Template: "test"}}}, - latest.TaggerComponent{Name: "BAT", Component: latest.TagPolicy{DateTimeTagger: &latest.DateTimeTagger{}}}, + {Name: "FOO", Component: latest.TagPolicy{GitTagger: &latest.GitTagger{}}}, + {Name: "FOE", Component: latest.TagPolicy{ShaTagger: &latest.ShaTagger{}}}, + {Name: "BAR", Component: latest.TagPolicy{EnvTemplateTagger: &latest.EnvTemplateTagger{Template: "test"}}}, + {Name: "BAT", Component: latest.TagPolicy{DateTimeTagger: &latest.DateTimeTagger{}}}, }, }, expected: map[string]tag.Tagger{ @@ -55,7 +55,7 @@ func TestCreateComponents(t *testing.T) { description: "tagTemplate is an invalid component", templateTagger: &latest.TemplateTagger{ Components: []latest.TaggerComponent{ - latest.TaggerComponent{Name: "FOO", Component: latest.TagPolicy{TemplateTagger: &latest.TemplateTagger{Template: "test"}}}, + {Name: "FOO", Component: latest.TagPolicy{TemplateTagger: &latest.TemplateTagger{Template: "test"}}}, }, }, shouldErr: true, @@ -64,8 +64,8 @@ func TestCreateComponents(t *testing.T) { description: "recurring names", templateTagger: &latest.TemplateTagger{ Components: []latest.TaggerComponent{ - latest.TaggerComponent{Name: "FOO", Component: latest.TagPolicy{GitTagger: &latest.GitTagger{}}}, - latest.TaggerComponent{Name: "FOO", Component: latest.TagPolicy{GitTagger: &latest.GitTagger{}}}, + {Name: "FOO", Component: latest.TagPolicy{GitTagger: &latest.GitTagger{}}}, + {Name: "FOO", Component: latest.TagPolicy{GitTagger: &latest.GitTagger{}}}, }, }, shouldErr: true, @@ -74,7 +74,7 @@ func TestCreateComponents(t *testing.T) { description: "unknown component", templateTagger: &latest.TemplateTagger{ Components: []latest.TaggerComponent{ - latest.TaggerComponent{Name: "FOO", Component: latest.TagPolicy{}}, + {Name: "FOO", Component: latest.TagPolicy{}}, }, }, shouldErr: true, From decbb72eb02b814c27c00bb85fc268157b379884 Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 29 Jul 2020 17:26:20 -0400 Subject: [PATCH 018/138] docs --- .../en/docs/pipeline-stages/taggers.md | 34 +++++++++++++++++++ .../en/samples/taggers/tagTemplate.yaml | 14 ++++++++ pkg/skaffold/schema/v2beta5/upgrade.go | 4 +++ 3 files changed, 52 insertions(+) create mode 100644 docs/content/en/samples/taggers/tagTemplate.yaml diff --git a/docs/content/en/docs/pipeline-stages/taggers.md b/docs/content/en/docs/pipeline-stages/taggers.md index 3e17c9e88fe..4578caa0fbc 100644 --- a/docs/content/en/docs/pipeline-stages/taggers.md +++ b/docs/content/en/docs/pipeline-stages/taggers.md @@ -12,6 +12,7 @@ Skaffold supports multiple taggers or tag policies for tagging images: + the `sha256` tagger uses `latest` to tag images. + the `envTemplate` tagger uses environment variables to tag images. + the `datetime` tagger uses current date and time, with a configurable pattern. + + the `tagTemplate` tagger uses a combination of the previous taggers to tag images. The default tagger, if none is specified in the `skaffold.yaml`, is the `gitCommit` tagger. @@ -143,3 +144,36 @@ You can learn more about what time format and time zone you can use in [Go Programming Language Documentation: Time package/LoadLocation Function](https://golang.org/pkg/time#LoadLocation) respectively. As showcased in the example, `dateTime` tag policy features two optional parameters: `format` and `timezone`. + +## `tagTemplate`: uses a combination of the previous taggers as tags + +`tagTemplate` allows you to combine all previous taggers to create your own tagging policy. +This policy requires that you specify a tag template, +where part of template can be replaced with the result of other tagging strategies during the tagging process. +These other tagging strategies are called components, which can be +a `gitCommit`, `sha256`, `envTemplate`, or `dateTime` tagger. + +The following `build` section, for example, instructs Skaffold to build a Docker image +`gcr.io/k8s-skaffold/example` with the `tagTemplate` tag policy. +The tag template is `{{.FOO}}_{{.BAR}}`. The components are a `dateTime` tagger +named `FOO` and a `gitCommit` tagger named `BAR`. When Skaffold finishes building the image, +it will evaluate `FOO` and `BAR` and use their values to tag the image. + +{{< alert >}} +Note
+ +`GIT`, `DATE`, and `SHA` are built-in component values that will evaluate to the default gitCommit, dateTime, and sha256 taggers, respectively. +User can overwrite these values by defining a component with one of these names. +{{< /alert >}} + +### Example + +{{% readfile file="samples/taggers/tagTemplate.yaml" %}} + +Suppose current time is `15:04:09.999 January 2nd, 2006` and the abbreviated commit sha was `25c65e0`, the image built will be `gcr.io/k8s-skaffold/example:2006-01-02_25c65e0`. + +### Configuration + +The tag template uses the [Go Programming Language Syntax](https://golang.org/pkg/text/template/). +As showcased in the example, `tagTemplate` tag policy features one +**required** parameter, `template`, which is the tag template to use. To learn more about templating support in Skaffold.yaml see [Templated fields]({{< relref "../environment/templating.md" >}}) \ No newline at end of file diff --git a/docs/content/en/samples/taggers/tagTemplate.yaml b/docs/content/en/samples/taggers/tagTemplate.yaml new file mode 100644 index 00000000000..d879717931e --- /dev/null +++ b/docs/content/en/samples/taggers/tagTemplate.yaml @@ -0,0 +1,14 @@ +build: + tagPolicy: + tagTemplate: + template: "{{.FOO}}_{{.BAR}}" + components: + - name: FOO + dateTime: + format: "2006-01-02" + timezone: "UTC" + - name: BAR + gitCommit: + variant: AbbrevCommitSha + artifacts: + - image: gcr.io/k8s-skaffold/example \ No newline at end of file diff --git a/pkg/skaffold/schema/v2beta5/upgrade.go b/pkg/skaffold/schema/v2beta5/upgrade.go index 0d878ff740d..4c2f52ed194 100755 --- a/pkg/skaffold/schema/v2beta5/upgrade.go +++ b/pkg/skaffold/schema/v2beta5/upgrade.go @@ -24,6 +24,10 @@ import ( // Upgrade upgrades a configuration to the next version. // Config changes from v2beta5 to v2beta6 +// 1. Additions: +// New structs TemplateTagger and TaggerComponent used by TagPolicy. +// 2. Removals: +// 3. Updates: func (c *SkaffoldConfig) Upgrade() (util.VersionedConfig, error) { var newConfig next.SkaffoldConfig pkgutil.CloneThroughJSON(c, &newConfig) From 9939a5a79b27d3840ff8c96539390db2a55fc48e Mon Sep 17 00:00:00 2001 From: tejal29 Date: Wed, 29 Jul 2020 15:09:37 -0700 Subject: [PATCH 019/138] add debug logs and don't return on first terminated state --- docs/content/en/api/skaffold.swagger.json | 36 +- docs/content/en/docs/references/api/grpc.md | 1 + pkg/diag/validator/pod.go | 18 +- pkg/diag/validator/pod_test.go | 40 +++ proto/skaffold.pb.go | 365 ++++++++++---------- proto/skaffold.proto | 5 + 6 files changed, 266 insertions(+), 199 deletions(-) diff --git a/docs/content/en/api/skaffold.swagger.json b/docs/content/en/api/skaffold.swagger.json index 9775cb8b1ef..9b53768d1b9 100644 --- a/docs/content/en/api/skaffold.swagger.json +++ b/docs/content/en/api/skaffold.swagger.json @@ -151,7 +151,7 @@ }, { "name": "event.buildEvent.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -181,6 +181,7 @@ "STATUSCHECK_KUBECTL_PID_KILLED", "STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR", "STATUSCHECK_DEPLOYMENT_FETCH_ERR", + "STATUSCHECK_POD_INITIALIZING", "UNKNOWN_ERROR", "STATUSCHECK_UNKNOWN", "STATUSCHECK_UNKNOWN_UNSCHEDULABLE", @@ -203,7 +204,7 @@ }, { "name": "event.buildEvent.actionableErr.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -233,6 +234,7 @@ "STATUSCHECK_KUBECTL_PID_KILLED", "STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR", "STATUSCHECK_DEPLOYMENT_FETCH_ERR", + "STATUSCHECK_POD_INITIALIZING", "UNKNOWN_ERROR", "STATUSCHECK_UNKNOWN", "STATUSCHECK_UNKNOWN_UNSCHEDULABLE", @@ -273,7 +275,7 @@ }, { "name": "event.deployEvent.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -303,6 +305,7 @@ "STATUSCHECK_KUBECTL_PID_KILLED", "STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR", "STATUSCHECK_DEPLOYMENT_FETCH_ERR", + "STATUSCHECK_POD_INITIALIZING", "UNKNOWN_ERROR", "STATUSCHECK_UNKNOWN", "STATUSCHECK_UNKNOWN_UNSCHEDULABLE", @@ -325,7 +328,7 @@ }, { "name": "event.deployEvent.actionableErr.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -355,6 +358,7 @@ "STATUSCHECK_KUBECTL_PID_KILLED", "STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR", "STATUSCHECK_DEPLOYMENT_FETCH_ERR", + "STATUSCHECK_POD_INITIALIZING", "UNKNOWN_ERROR", "STATUSCHECK_UNKNOWN", "STATUSCHECK_UNKNOWN_UNSCHEDULABLE", @@ -457,7 +461,7 @@ }, { "name": "event.statusCheckEvent.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -487,6 +491,7 @@ "STATUSCHECK_KUBECTL_PID_KILLED", "STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR", "STATUSCHECK_DEPLOYMENT_FETCH_ERR", + "STATUSCHECK_POD_INITIALIZING", "UNKNOWN_ERROR", "STATUSCHECK_UNKNOWN", "STATUSCHECK_UNKNOWN_UNSCHEDULABLE", @@ -509,7 +514,7 @@ }, { "name": "event.statusCheckEvent.actionableErr.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -539,6 +544,7 @@ "STATUSCHECK_KUBECTL_PID_KILLED", "STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR", "STATUSCHECK_DEPLOYMENT_FETCH_ERR", + "STATUSCHECK_POD_INITIALIZING", "UNKNOWN_ERROR", "STATUSCHECK_UNKNOWN", "STATUSCHECK_UNKNOWN_UNSCHEDULABLE", @@ -591,7 +597,7 @@ }, { "name": "event.resourceStatusCheckEvent.statusCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -621,6 +627,7 @@ "STATUSCHECK_KUBECTL_PID_KILLED", "STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR", "STATUSCHECK_DEPLOYMENT_FETCH_ERR", + "STATUSCHECK_POD_INITIALIZING", "UNKNOWN_ERROR", "STATUSCHECK_UNKNOWN", "STATUSCHECK_UNKNOWN_UNSCHEDULABLE", @@ -643,7 +650,7 @@ }, { "name": "event.resourceStatusCheckEvent.actionableErr.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -673,6 +680,7 @@ "STATUSCHECK_KUBECTL_PID_KILLED", "STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR", "STATUSCHECK_DEPLOYMENT_FETCH_ERR", + "STATUSCHECK_POD_INITIALIZING", "UNKNOWN_ERROR", "STATUSCHECK_UNKNOWN", "STATUSCHECK_UNKNOWN_UNSCHEDULABLE", @@ -726,7 +734,7 @@ }, { "name": "event.fileSyncEvent.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -756,6 +764,7 @@ "STATUSCHECK_KUBECTL_PID_KILLED", "STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR", "STATUSCHECK_DEPLOYMENT_FETCH_ERR", + "STATUSCHECK_POD_INITIALIZING", "UNKNOWN_ERROR", "STATUSCHECK_UNKNOWN", "STATUSCHECK_UNKNOWN_UNSCHEDULABLE", @@ -778,7 +787,7 @@ }, { "name": "event.fileSyncEvent.actionableErr.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -808,6 +817,7 @@ "STATUSCHECK_KUBECTL_PID_KILLED", "STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR", "STATUSCHECK_DEPLOYMENT_FETCH_ERR", + "STATUSCHECK_POD_INITIALIZING", "UNKNOWN_ERROR", "STATUSCHECK_UNKNOWN", "STATUSCHECK_UNKNOWN_UNSCHEDULABLE", @@ -891,7 +901,7 @@ }, { "name": "event.devLoopEvent.err.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -921,6 +931,7 @@ "STATUSCHECK_KUBECTL_PID_KILLED", "STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR", "STATUSCHECK_DEPLOYMENT_FETCH_ERR", + "STATUSCHECK_POD_INITIALIZING", "UNKNOWN_ERROR", "STATUSCHECK_UNKNOWN", "STATUSCHECK_UNKNOWN_UNSCHEDULABLE", @@ -1627,6 +1638,7 @@ "STATUSCHECK_KUBECTL_PID_KILLED", "STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR", "STATUSCHECK_DEPLOYMENT_FETCH_ERR", + "STATUSCHECK_POD_INITIALIZING", "UNKNOWN_ERROR", "STATUSCHECK_UNKNOWN", "STATUSCHECK_UNKNOWN_UNSCHEDULABLE", @@ -1646,7 +1658,7 @@ "STATUSCHECK_DEADLINE_EXCEEDED" ], "default": "OK", - "description": "Enum for Status codes\nThese error codes are prepended by Phase Name e.g.\nBUILD, DEPLOY, STATUSCHECK, DEVINIT\n- OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded" + "description": "Enum for Status codes\nThese error codes are prepended by Phase Name e.g.\nBUILD, DEPLOY, STATUSCHECK, DEVINIT\n- OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded" }, "protoSuggestion": { "type": "object", diff --git a/docs/content/en/docs/references/api/grpc.md b/docs/content/en/docs/references/api/grpc.md index 67fa0dc7346..cf936de0f7f 100644 --- a/docs/content/en/docs/references/api/grpc.md +++ b/docs/content/en/docs/references/api/grpc.md @@ -774,6 +774,7 @@ BUILD, DEPLOY, STATUSCHECK, DEVINIT | STATUSCHECK_KUBECTL_PID_KILLED | 410 | Kubectl process killed error | | STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR | 411 | Kubectl client fetch err | | STATUSCHECK_DEPLOYMENT_FETCH_ERR | 412 | | +| STATUSCHECK_POD_INITIALIZING | 451 | Pod Initializing | | UNKNOWN_ERROR | 500 | Could not determine error and phase | | STATUSCHECK_UNKNOWN | 501 | Status Check error unknown | | STATUSCHECK_UNKNOWN_UNSCHEDULABLE | 502 | Container is unschedulable due to unknown reasons | diff --git a/pkg/diag/validator/pod.go b/pkg/diag/validator/pod.go index f04e7c41929..b0a6d959f1f 100644 --- a/pkg/diag/validator/pod.go +++ b/pkg/diag/validator/pod.go @@ -126,14 +126,17 @@ func getContainerStatus(pod *v1.Pod) (proto.StatusCode, []string, error) { if c.Type == v1.PodScheduled { switch c.Status { case v1.ConditionFalse: + logrus.Debugf("Getting tolerations for pod %s", pod.Name) sc, err := getUntoleratedTaints(c.Reason, c.Message) return sc, nil, err case v1.ConditionTrue: + logrus.Debugf("Fetching container statuses for pod %s", pod.Name) // TODO(dgageot): Add EphemeralContainerStatuses cs := append(pod.Status.InitContainerStatuses, pod.Status.ContainerStatuses...) // See https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-states return getWaitingContainerStatus(pod, cs) case v1.ConditionUnknown: + logrus.Debugf("Could not determine scheduling condition for pod %s", pod.Name) return proto.StatusCode_STATUSCHECK_UNKNOWN, nil, fmt.Errorf(c.Message) } } @@ -148,11 +151,10 @@ func getWaitingContainerStatus(po *v1.Pod, cs []v1.ContainerStatus) (proto.Statu case c.State.Waiting != nil: return extractErrorMessageFromWaitingContainerStatus(po, c) case c.State.Terminated != nil: - if c.State.Terminated.ExitCode == 0 { - return proto.StatusCode_STATUSCHECK_SUCCESS, nil, nil + if c.State.Terminated.ExitCode != 0 { + l := getPodLogs(po, c.Name) + return proto.StatusCode_STATUSCHECK_CONTAINER_TERMINATED, l, fmt.Errorf("container %s terminated with exit code %d", c.Name, c.State.Terminated.ExitCode) } - l := getPodLogs(po, c.Name) - return proto.StatusCode_STATUSCHECK_CONTAINER_TERMINATED, l, fmt.Errorf("container %s terminated with exit code %d", c.Name, c.State.Terminated.ExitCode) } } // No waiting or terminated containers, pod should be in good health. @@ -211,12 +213,13 @@ func processPodEvents(e corev1.EventInterface, pod v1.Pod, ps *podStatus) { if _, ok := unknownConditionsOrSuccess[ps.ae.ErrCode]; !ok { return } + logrus.Debugf("Fetching events for pod %s", pod.Name) // Get pod events. scheme := runtime.NewScheme() scheme.AddKnownTypes(v1.SchemeGroupVersion, &pod) events, err := e.Search(scheme, &pod) if err != nil { - logrus.Debugf("could not fetch events for resource %s due to %v", pod.Name, err) + logrus.Debugf("Could not fetch events for resource %s due to %v", pod.Name, err) return } // find the latest failed event. @@ -291,7 +294,7 @@ func extractErrorMessageFromWaitingContainerStatus(po *v1.Pod, c v1.ContainerSta switch c.State.Waiting.Reason { case podInitializing: // container is waiting to run - return proto.StatusCode_STATUSCHECK_SUCCESS, nil, nil + return proto.StatusCode_STATUSCHECK_POD_INITIALIZING, nil, nil case containerCreating: return proto.StatusCode_STATUSCHECK_CONTAINER_CREATING, nil, fmt.Errorf("creating container %s", c.Name) case crashLoopBackOff: @@ -306,7 +309,7 @@ func extractErrorMessageFromWaitingContainerStatus(po *v1.Pod, c v1.ContainerSta return proto.StatusCode_STATUSCHECK_RUN_CONTAINER_ERR, nil, fmt.Errorf("container %s in error: %s", c.Name, trimSpace(match[3])) } } - logrus.Debugf("Failed to extract error condition for waiting container %q: %v", c.Name, c.State) + logrus.Debugf("Failed to extract error message for waiting container %q: %v", c.Name, c.State) return proto.StatusCode_STATUSCHECK_CONTAINER_WAITING_UNKNOWN, nil, fmt.Errorf("container %s in error: %v", c.Name, c.State.Waiting) } @@ -326,6 +329,7 @@ func trimSpace(msg string) string { } func getPodLogs(po *v1.Pod, c string) []string { + logrus.Debugf("Fetching logs for container %v", c) logCommand := []string{"kubectl", "logs", po.Name, "-n", po.Namespace, "-c", c} logs, err := runCli(logCommand[0], logCommand[1:]) if err != nil { diff --git a/pkg/diag/validator/pod_test.go b/pkg/diag/validator/pod_test.go index 3d152da3d87..b3ca594e204 100644 --- a/pkg/diag/validator/pod_test.go +++ b/pkg/diag/validator/pod_test.go @@ -558,6 +558,46 @@ func TestRun(t *testing.T) { }, }, nil)}, }, + { + description: "One of the pod containers is in Terminated State with non zero exit code followed by Waiting state", + pods: []*v1.Pod{{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "test", + }, + TypeMeta: metav1.TypeMeta{Kind: "Pod"}, + Status: v1.PodStatus{ + Phase: v1.PodRunning, + Conditions: []v1.PodCondition{{Type: v1.PodScheduled, Status: v1.ConditionTrue}}, + ContainerStatuses: []v1.ContainerStatus{ + { + Name: "foo-success", + Image: "foo-image", + State: v1.ContainerState{ + Terminated: &v1.ContainerStateTerminated{ExitCode: 0}, + }, + }, + { + Name: "foo-container", + Image: "foo-image", + State: v1.ContainerState{ + Waiting: &v1.ContainerStateWaiting{Reason: "CrashLoopBackOff", + Message: "Back off restarting container", + }, + }, + }, + }, + }, + }}, + logOutput: mockLogOutput{ + output: []byte("some panic"), + }, + expected: []Resource{NewResource("test", "Pod", "foo", "Running", + proto.ActionableErr{ + Message: "container foo-container is backing off waiting to restart", + ErrCode: proto.StatusCode_STATUSCHECK_CONTAINER_RESTARTING, + }, []string{"[foo foo-container] some panic"})}, + }, } for _, test := range tests { diff --git a/proto/skaffold.pb.go b/proto/skaffold.pb.go index 3ad8db95c8c..e8e09c5e8da 100644 --- a/proto/skaffold.pb.go +++ b/proto/skaffold.pb.go @@ -239,6 +239,8 @@ const ( // Kubectl client fetch err StatusCode_STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR StatusCode = 411 StatusCode_STATUSCHECK_DEPLOYMENT_FETCH_ERR StatusCode = 412 + // Pod Initializing + StatusCode_STATUSCHECK_POD_INITIALIZING StatusCode = 451 // Could not determine error and phase StatusCode_UNKNOWN_ERROR StatusCode = 500 // Status Check error unknown @@ -301,6 +303,7 @@ var StatusCode_name = map[int32]string{ 410: "STATUSCHECK_KUBECTL_PID_KILLED", 411: "STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR", 412: "STATUSCHECK_DEPLOYMENT_FETCH_ERR", + 451: "STATUSCHECK_POD_INITIALIZING", 500: "UNKNOWN_ERROR", 501: "STATUSCHECK_UNKNOWN", 502: "STATUSCHECK_UNKNOWN_UNSCHEDULABLE", @@ -346,6 +349,7 @@ var StatusCode_value = map[string]int32{ "STATUSCHECK_KUBECTL_PID_KILLED": 410, "STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR": 411, "STATUSCHECK_DEPLOYMENT_FETCH_ERR": 412, + "STATUSCHECK_POD_INITIALIZING": 451, "UNKNOWN_ERROR": 500, "STATUSCHECK_UNKNOWN": 501, "STATUSCHECK_UNKNOWN_UNSCHEDULABLE": 502, @@ -2436,186 +2440,187 @@ func init() { func init() { proto.RegisterFile("skaffold.proto", fileDescriptor_4f2d38e344f9dbf5) } var fileDescriptor_4f2d38e344f9dbf5 = []byte{ - // 2855 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0x4d, 0x8c, 0xdb, 0xc6, - 0xf5, 0x5f, 0x89, 0x92, 0x56, 0x7a, 0xfb, 0x61, 0x7a, 0xec, 0xb5, 0x65, 0x79, 0x63, 0xaf, 0xf9, - 0xb7, 0x1d, 0x67, 0x93, 0xff, 0x3a, 0x89, 0x8b, 0x22, 0x75, 0x93, 0x16, 0x5c, 0x72, 0xbc, 0xa2, - 0x97, 0x4b, 0x0a, 0x14, 0x95, 0xc4, 0x01, 0x0a, 0x81, 0x96, 0xb8, 0x8a, 0x60, 0xad, 0xb8, 0xa5, - 0x28, 0xa7, 0xdb, 0x43, 0x0f, 0xbd, 0xf6, 0xd2, 0x36, 0x4d, 0xbf, 0x0f, 0xe9, 0xa1, 0xb7, 0x7e, - 0x5d, 0x8b, 0xa0, 0x4d, 0x6f, 0xfd, 0x3a, 0x16, 0x28, 0xd0, 0x53, 0x11, 0x20, 0x39, 0xf4, 0x9e, - 0xf4, 0xbb, 0x40, 0x31, 0x1f, 0x24, 0x87, 0xfa, 0xf0, 0x66, 0x53, 0x14, 0x3d, 0xad, 0xe6, 0xcd, - 0x6f, 0x7e, 0xef, 0xcd, 0x9b, 0x37, 0xef, 0x3d, 0xce, 0xc2, 0xea, 0xe8, 0x81, 0xb7, 0xbf, 0x1f, - 0x0c, 0xba, 0x5b, 0x87, 0x61, 0x10, 0x05, 0xa8, 0x48, 0xff, 0xd4, 0xd6, 0x7b, 0x41, 0xd0, 0x1b, - 0xf8, 0x37, 0xbd, 0xc3, 0xfe, 0x4d, 0x6f, 0x38, 0x0c, 0x22, 0x2f, 0xea, 0x07, 0xc3, 0x11, 0x03, - 0xd5, 0x2e, 0xf3, 0x59, 0x3a, 0xba, 0x3f, 0xde, 0xbf, 0x19, 0xf5, 0x0f, 0xfc, 0x51, 0xe4, 0x1d, - 0x1c, 0x72, 0xc0, 0xc5, 0x49, 0x80, 0x7f, 0x70, 0x18, 0x1d, 0xb1, 0x49, 0xe5, 0x16, 0xac, 0x34, - 0x23, 0x2f, 0xf2, 0x1d, 0x7f, 0x74, 0x18, 0x0c, 0x47, 0x3e, 0x52, 0xa0, 0x38, 0x22, 0x82, 0x6a, - 0x6e, 0x23, 0x77, 0x63, 0xe9, 0xd9, 0x65, 0x86, 0xdb, 0x62, 0x20, 0x36, 0xa5, 0xac, 0x43, 0x39, - 0xc1, 0xcb, 0x20, 0x1d, 0x8c, 0x7a, 0x14, 0x5d, 0x71, 0xc8, 0x4f, 0xe5, 0x31, 0x58, 0x74, 0xfc, - 0xcf, 0x8e, 0xfd, 0x51, 0x84, 0x10, 0x14, 0x86, 0xde, 0x81, 0xcf, 0x67, 0xe9, 0x6f, 0xe5, 0x8d, - 0x02, 0x14, 0x29, 0x1b, 0x7a, 0x06, 0xe0, 0xfe, 0xb8, 0x3f, 0xe8, 0x36, 0x05, 0x7d, 0xa7, 0xb9, - 0xbe, 0xed, 0x64, 0xc2, 0x11, 0x40, 0xe8, 0x63, 0xb0, 0xd4, 0xf5, 0x0f, 0x07, 0xc1, 0x11, 0x5b, - 0x93, 0xa7, 0x6b, 0x10, 0x5f, 0xa3, 0xa7, 0x33, 0x8e, 0x08, 0x43, 0x75, 0x58, 0xdd, 0x0f, 0xc2, - 0xd7, 0xbc, 0xb0, 0xeb, 0x77, 0x1b, 0x41, 0x18, 0x8d, 0xaa, 0x85, 0x0d, 0xe9, 0xc6, 0xd2, 0xb3, - 0x1b, 0xe2, 0xe6, 0xb6, 0xee, 0x64, 0x20, 0x78, 0x18, 0x85, 0x47, 0xce, 0xc4, 0x3a, 0xa4, 0x81, - 0x4c, 0x5c, 0x30, 0x1e, 0x69, 0xaf, 0xfa, 0x9d, 0x07, 0xcc, 0x88, 0x22, 0x35, 0xe2, 0xbc, 0xc0, - 0x25, 0x4e, 0x3b, 0x53, 0x0b, 0xd0, 0x6d, 0x58, 0xd9, 0xef, 0x0f, 0xfc, 0xe6, 0xd1, 0xb0, 0xc3, - 0x18, 0x4a, 0x94, 0xe1, 0x2c, 0x67, 0xb8, 0x23, 0xce, 0x39, 0x59, 0x28, 0x6a, 0xc0, 0x99, 0xae, - 0x7f, 0x7f, 0xdc, 0xeb, 0xf5, 0x87, 0x3d, 0x2d, 0x18, 0x46, 0x5e, 0x7f, 0xe8, 0x87, 0xa3, 0xea, - 0x22, 0xdd, 0xcf, 0xa5, 0xc4, 0x11, 0x93, 0x08, 0xfc, 0xd0, 0x1f, 0x46, 0xce, 0xac, 0xa5, 0xe8, - 0x49, 0x28, 0x1f, 0xf8, 0x91, 0xd7, 0xf5, 0x22, 0xaf, 0x5a, 0xa6, 0x86, 0x9c, 0xe2, 0x34, 0x7b, - 0x5c, 0xec, 0x24, 0x80, 0x5a, 0x13, 0xce, 0xcc, 0x70, 0x13, 0x09, 0x82, 0x07, 0xfe, 0x11, 0x3d, - 0xc2, 0xa2, 0x43, 0x7e, 0xa2, 0xeb, 0x50, 0x7c, 0xe8, 0x0d, 0xc6, 0xf1, 0x11, 0xc9, 0x9c, 0x92, - 0xac, 0x61, 0xb6, 0xb0, 0xe9, 0xdb, 0xf9, 0xe7, 0x72, 0x77, 0x0b, 0x65, 0x49, 0x2e, 0x28, 0xef, - 0xe5, 0xa0, 0x1c, 0x6b, 0x44, 0x9b, 0x50, 0xa4, 0xa7, 0xce, 0xa3, 0xe2, 0xac, 0x18, 0x15, 0x89, - 0x59, 0x0c, 0x82, 0xfe, 0x1f, 0x4a, 0xec, 0xb0, 0xb9, 0xae, 0xb5, 0x4c, 0x38, 0x24, 0x68, 0x0e, - 0x42, 0x9f, 0x06, 0xf0, 0xba, 0xdd, 0x3e, 0xb9, 0x42, 0xde, 0xa0, 0xda, 0xa1, 0x8e, 0xbb, 0x3c, - 0xb1, 0xe3, 0x2d, 0x35, 0x41, 0xb0, 0x38, 0x10, 0x96, 0xd4, 0x5e, 0x80, 0x53, 0x13, 0xd3, 0xe2, - 0xfe, 0x2b, 0x6c, 0xff, 0x67, 0xc5, 0xfd, 0x57, 0x84, 0xdd, 0x2a, 0x1f, 0xe4, 0x61, 0x25, 0xb3, - 0x0f, 0xf4, 0x14, 0x9c, 0x1e, 0x8e, 0x0f, 0xee, 0xfb, 0xa1, 0xbd, 0xaf, 0x86, 0x51, 0x7f, 0xdf, - 0xeb, 0x44, 0x23, 0xee, 0xcb, 0xe9, 0x09, 0xf4, 0x02, 0x94, 0xe9, 0xbe, 0xc9, 0xb1, 0xe7, 0xa9, - 0xf5, 0x57, 0x66, 0x79, 0x67, 0xcb, 0x38, 0xf0, 0x7a, 0xfe, 0x36, 0x43, 0x3a, 0xc9, 0x12, 0x74, - 0x15, 0x0a, 0xd1, 0xd1, 0xa1, 0x5f, 0x95, 0x36, 0x72, 0x37, 0x56, 0x93, 0x73, 0xa1, 0x38, 0xf7, - 0xe8, 0xd0, 0x77, 0xe8, 0x2c, 0xd2, 0x67, 0x38, 0xe9, 0xea, 0x4c, 0x35, 0x8f, 0xf2, 0x94, 0x09, - 0xcb, 0xa2, 0x15, 0xe8, 0x3a, 0xd7, 0x9d, 0xa3, 0xba, 0x91, 0xc8, 0xe7, 0x87, 0x82, 0xf6, 0xb3, - 0x50, 0xec, 0x04, 0xe3, 0x61, 0x44, 0x9d, 0x57, 0x74, 0xd8, 0xe0, 0x3f, 0xf5, 0xfb, 0x2f, 0x73, - 0xb0, 0x9a, 0x0d, 0x09, 0xf4, 0x3c, 0x54, 0x58, 0x50, 0x10, 0x5f, 0xe6, 0x26, 0xae, 0x90, 0x88, - 0xe4, 0x43, 0x3f, 0x74, 0xd2, 0x05, 0xe8, 0x29, 0x58, 0xec, 0x0c, 0xc6, 0xa3, 0xc8, 0x0f, 0xa9, - 0xb2, 0x74, 0x43, 0x1a, 0x93, 0xd2, 0x0d, 0xc5, 0x90, 0x9a, 0x01, 0xe5, 0x98, 0x04, 0x3d, 0x9e, - 0xf1, 0xc3, 0x99, 0x8c, 0xca, 0xe3, 0x1d, 0xa1, 0xfc, 0x31, 0x07, 0x90, 0xe6, 0x47, 0xf4, 0x29, - 0xa8, 0x78, 0x42, 0xd8, 0x88, 0x89, 0x2d, 0x45, 0x6d, 0x25, 0x01, 0xc4, 0x8e, 0x29, 0x5d, 0x82, - 0x36, 0x60, 0xc9, 0x1b, 0x47, 0x81, 0x1b, 0xf6, 0x7b, 0x3d, 0xbe, 0x97, 0xb2, 0x23, 0x8a, 0x48, - 0xa2, 0xe6, 0x49, 0x2c, 0xe8, 0xc6, 0x91, 0x73, 0x3a, 0x9b, 0xef, 0x82, 0xae, 0xef, 0x08, 0xa0, - 0xda, 0xf3, 0xb0, 0x9a, 0xd5, 0x78, 0xa2, 0xb3, 0xfa, 0x3c, 0x2c, 0x09, 0xc9, 0x1c, 0x9d, 0x83, - 0x12, 0xa3, 0xe6, 0xab, 0xf9, 0xe8, 0xbf, 0x62, 0xb9, 0xf2, 0x4e, 0x0e, 0xe4, 0xc9, 0x24, 0x3e, - 0xd7, 0x02, 0x1d, 0x2a, 0xa1, 0x3f, 0x0a, 0xc6, 0x61, 0xc7, 0x8f, 0x6f, 0xe3, 0xf5, 0x39, 0x85, - 0x60, 0xcb, 0x89, 0x81, 0xfc, 0x04, 0x92, 0x85, 0x1f, 0xd1, 0xbf, 0x59, 0xbe, 0x13, 0xf9, 0xd7, - 0x80, 0x95, 0x4c, 0x95, 0xf9, 0xe8, 0x1e, 0x56, 0xde, 0x29, 0x40, 0x91, 0x66, 0x74, 0xf4, 0x34, - 0x54, 0x48, 0x9d, 0xa0, 0x03, 0x9e, 0xb7, 0x65, 0x21, 0xaf, 0x52, 0x79, 0x7d, 0xc1, 0x49, 0x41, - 0xe8, 0x16, 0x6f, 0x00, 0xd8, 0x92, 0xfc, 0x74, 0x03, 0x10, 0xaf, 0x11, 0x60, 0xe8, 0xe3, 0x71, - 0x0b, 0xc0, 0x56, 0x49, 0x33, 0x5a, 0x80, 0x78, 0x99, 0x08, 0x24, 0xe6, 0x1d, 0xc6, 0xd5, 0xa7, - 0x5a, 0x98, 0x5d, 0x95, 0x88, 0x79, 0x09, 0x08, 0xe1, 0x4c, 0xb1, 0x67, 0x0b, 0xe7, 0x16, 0xfb, - 0x78, 0xfd, 0xd4, 0x12, 0xf4, 0x19, 0xa8, 0xc6, 0x47, 0x3d, 0x89, 0xe7, 0x95, 0x3f, 0x2e, 0x3f, - 0xce, 0x1c, 0x58, 0x7d, 0xc1, 0x99, 0x4b, 0x81, 0x9e, 0x4f, 0xbb, 0x09, 0xc6, 0xb9, 0x38, 0xb3, - 0x9b, 0x88, 0x89, 0xb2, 0x60, 0xf4, 0x0a, 0x9c, 0xef, 0xce, 0xee, 0x16, 0x78, 0x33, 0x70, 0x4c, - 0x4f, 0x51, 0x5f, 0x70, 0xe6, 0x11, 0xa0, 0x4f, 0xc0, 0x72, 0xd7, 0x7f, 0x68, 0x06, 0xc1, 0x21, - 0x23, 0xac, 0x50, 0xc2, 0x34, 0xdd, 0xa5, 0x53, 0xf5, 0x05, 0x27, 0x03, 0xdd, 0x5e, 0x06, 0xf0, - 0xc9, 0x8f, 0x36, 0x49, 0x83, 0xca, 0x00, 0x96, 0x45, 0x34, 0x5a, 0x87, 0x4a, 0x3f, 0xf2, 0x43, - 0xda, 0x06, 0xf3, 0x42, 0x99, 0x0a, 0x84, 0x58, 0xce, 0x67, 0x62, 0xf9, 0x3a, 0x48, 0x7e, 0x18, - 0xf2, 0x80, 0x89, 0xdd, 0xa3, 0x76, 0x68, 0x3d, 0xb9, 0x3f, 0xf0, 0x71, 0x18, 0x3a, 0x04, 0xa0, - 0x7c, 0x29, 0x07, 0x2b, 0x19, 0x31, 0x7a, 0x12, 0x16, 0xfd, 0x30, 0xa4, 0x97, 0x33, 0x37, 0xef, - 0x72, 0xc6, 0x08, 0x54, 0x85, 0xc5, 0x03, 0x7f, 0x34, 0xf2, 0x7a, 0xf1, 0xbd, 0x8b, 0x87, 0xe8, - 0x16, 0x2c, 0x8d, 0xc6, 0xbd, 0x9e, 0x3f, 0xa2, 0xed, 0x7b, 0x55, 0xa2, 0xe9, 0x22, 0xa1, 0x4a, - 0x66, 0x1c, 0x11, 0xa5, 0x58, 0x50, 0x49, 0x6e, 0x0f, 0xb9, 0xd1, 0x3e, 0xb9, 0xec, 0xfc, 0x96, - 0xb2, 0x41, 0xa6, 0x83, 0xcb, 0x1f, 0xd3, 0xc1, 0x29, 0x6f, 0xc5, 0xc5, 0x83, 0x31, 0xd6, 0xa0, - 0x1c, 0x57, 0x02, 0x4e, 0x9a, 0x8c, 0xe7, 0x3a, 0x52, 0x4e, 0x1d, 0x59, 0xa1, 0x2e, 0x13, 0x1d, - 0x54, 0x38, 0xd6, 0x41, 0xb7, 0x61, 0xc5, 0x13, 0xdd, 0xcb, 0xef, 0xd4, 0xec, 0x13, 0xc9, 0x42, - 0x95, 0x37, 0x73, 0x71, 0x65, 0x60, 0xe6, 0xcf, 0xcb, 0x5b, 0xdc, 0xc4, 0xfc, 0x4c, 0x13, 0xa5, - 0x93, 0x9b, 0x58, 0xf8, 0xf0, 0x26, 0xbe, 0x9d, 0xad, 0x1f, 0x8f, 0xb6, 0x73, 0x7e, 0xb0, 0xfc, - 0x0f, 0x9d, 0xfc, 0xa7, 0x1c, 0x54, 0xe7, 0xa5, 0x22, 0x12, 0x30, 0x71, 0x2a, 0x8a, 0x03, 0x26, - 0x1e, 0xcf, 0x0d, 0x18, 0x61, 0x97, 0xd2, 0xcc, 0x5d, 0x16, 0xd2, 0x5d, 0x66, 0x6b, 0x61, 0xf1, - 0x43, 0xd4, 0xc2, 0xe9, 0xbd, 0x96, 0x3e, 0xfc, 0x5e, 0xbf, 0x9f, 0x87, 0x4a, 0x92, 0xfe, 0x49, - 0x62, 0x19, 0x04, 0x1d, 0x6f, 0x40, 0x24, 0x71, 0x62, 0x49, 0x04, 0xe8, 0x12, 0x40, 0xe8, 0x1f, - 0x04, 0x91, 0x4f, 0xa7, 0x59, 0x4b, 0x26, 0x48, 0xc8, 0x36, 0x0f, 0x83, 0xae, 0x45, 0x3e, 0x78, - 0xf9, 0x36, 0xf9, 0x10, 0x5d, 0x85, 0x95, 0x4e, 0x9c, 0x1b, 0xe9, 0x3c, 0xdb, 0x70, 0x56, 0x48, - 0xb4, 0x93, 0x2f, 0xe4, 0xd1, 0xa1, 0xd7, 0x61, 0x3b, 0xaf, 0x38, 0xa9, 0x80, 0x38, 0x9e, 0x94, - 0x26, 0xba, 0xbc, 0xc4, 0x1c, 0x1f, 0x8f, 0x91, 0x02, 0xcb, 0xf1, 0x21, 0x90, 0xee, 0x91, 0x96, - 0x80, 0x8a, 0x93, 0x91, 0x89, 0x18, 0xca, 0x51, 0xce, 0x62, 0x28, 0x4f, 0x15, 0x16, 0xbd, 0x6e, - 0x37, 0xf4, 0x47, 0x23, 0x9a, 0xac, 0x2b, 0x4e, 0x3c, 0x54, 0x7e, 0x9f, 0x4b, 0x5b, 0x86, 0xc4, - 0x57, 0xa4, 0x94, 0x68, 0xb4, 0x3f, 0xe5, 0xbe, 0x4a, 0x04, 0x24, 0x53, 0xf5, 0x0f, 0xd2, 0xb0, - 0x66, 0x03, 0x21, 0x40, 0xa4, 0x59, 0xd7, 0xb5, 0x30, 0x33, 0xd8, 0x8b, 0x27, 0x0f, 0xf6, 0x13, - 0x04, 0xc0, 0xfb, 0x79, 0x38, 0x3f, 0xa7, 0xb6, 0x3d, 0xea, 0xd6, 0xc6, 0x07, 0x9d, 0x3f, 0xe6, - 0xa0, 0xa5, 0x63, 0x0f, 0xba, 0x30, 0xe3, 0xa0, 0x93, 0x94, 0x5c, 0x9c, 0x48, 0xc9, 0x55, 0x58, - 0x0c, 0xc7, 0xc3, 0xa8, 0x9f, 0xc4, 0x40, 0x3c, 0x24, 0xc1, 0xf9, 0x5a, 0x10, 0x3e, 0xe8, 0x0f, - 0x7b, 0x7a, 0x3f, 0xe4, 0x01, 0x20, 0x48, 0x90, 0x05, 0x40, 0xeb, 0x34, 0x7b, 0xff, 0x28, 0xd3, - 0xda, 0xb3, 0xf5, 0xe8, 0xda, 0xce, 0xe4, 0xc2, 0x6b, 0x88, 0xc0, 0x40, 0xbe, 0xc6, 0x26, 0xa6, - 0x8f, 0xeb, 0x40, 0x57, 0xc4, 0x0e, 0xf4, 0x0b, 0x50, 0x36, 0x83, 0x1e, 0x5b, 0xf7, 0x1c, 0x54, - 0x92, 0x37, 0x2b, 0xde, 0x38, 0xd6, 0xb6, 0xd8, 0xa3, 0xd5, 0x56, 0xfc, 0x68, 0xb5, 0xe5, 0xc6, - 0x08, 0x27, 0x05, 0x23, 0x05, 0x8a, 0xbe, 0xd0, 0x3b, 0xc6, 0x8f, 0x55, 0xfc, 0x85, 0xc1, 0xcf, - 0xd6, 0x4c, 0x49, 0xa8, 0x99, 0xca, 0x6d, 0x38, 0xdd, 0x1a, 0xf9, 0xa1, 0x31, 0x8c, 0x08, 0x94, - 0x3f, 0x57, 0x5d, 0x83, 0x52, 0x9f, 0x0a, 0xb8, 0x15, 0x2b, 0x9c, 0x8f, 0xa3, 0xf8, 0xa4, 0xf2, - 0x49, 0x58, 0xe5, 0xdd, 0x6f, 0xbc, 0xf0, 0x89, 0xec, 0xa3, 0x59, 0xdc, 0xe2, 0x70, 0x54, 0xe6, - 0xed, 0xec, 0x19, 0x58, 0x16, 0xc5, 0xa8, 0x06, 0x8b, 0x3e, 0x0d, 0x46, 0xf6, 0xd6, 0x51, 0xae, - 0x2f, 0x38, 0xb1, 0x60, 0xbb, 0x08, 0xd2, 0x43, 0x6f, 0xa0, 0xdc, 0x85, 0x12, 0xb3, 0x80, 0xec, - 0x25, 0x7d, 0x16, 0x29, 0xc7, 0x0f, 0x20, 0x08, 0x0a, 0xa3, 0xa3, 0x61, 0x87, 0x77, 0xe7, 0xf4, - 0x37, 0x09, 0x5d, 0xfe, 0x28, 0x22, 0x51, 0x29, 0x1f, 0x29, 0x1d, 0x80, 0xb4, 0xd3, 0x40, 0x2f, - 0xc0, 0x6a, 0xda, 0x6b, 0x08, 0xfd, 0xcd, 0xda, 0x54, 0x53, 0x42, 0x2f, 0xdc, 0x04, 0x98, 0x28, - 0x61, 0x97, 0x29, 0xce, 0xf7, 0x6c, 0xb4, 0x19, 0xc0, 0x92, 0xf0, 0x51, 0x8f, 0xaa, 0x70, 0xb6, - 0x65, 0xed, 0x5a, 0xf6, 0x4b, 0x56, 0x7b, 0xbb, 0x65, 0x98, 0x3a, 0x76, 0xda, 0xee, 0xbd, 0x06, - 0x96, 0x17, 0xd0, 0x22, 0x48, 0x77, 0x8d, 0x6d, 0x39, 0x87, 0x2a, 0x50, 0xdc, 0x56, 0x5f, 0xc1, - 0xa6, 0x9c, 0x47, 0xab, 0x00, 0x14, 0xd5, 0x50, 0xb5, 0xdd, 0xa6, 0x2c, 0x21, 0x80, 0x92, 0xd6, - 0x6a, 0xba, 0xf6, 0x9e, 0x5c, 0x20, 0xbf, 0x77, 0x55, 0xcb, 0xd8, 0xb5, 0xe5, 0x22, 0xf9, 0xad, - 0xdb, 0xda, 0x2e, 0x76, 0xe4, 0xd2, 0xa6, 0x0e, 0x95, 0xe4, 0x05, 0x03, 0x9d, 0x03, 0x94, 0x51, - 0x17, 0x2b, 0x5b, 0x82, 0x45, 0xcd, 0x6c, 0x35, 0x5d, 0xec, 0xc8, 0x39, 0xa2, 0x79, 0x47, 0xdb, - 0x96, 0xf3, 0x44, 0xb3, 0x69, 0x6b, 0xaa, 0x29, 0x4b, 0x9b, 0x36, 0x69, 0x33, 0xd3, 0x6f, 0x70, - 0x74, 0x01, 0xd6, 0x62, 0x22, 0x1d, 0x37, 0x4c, 0xfb, 0x5e, 0x6a, 0x78, 0x19, 0x0a, 0x75, 0x6c, - 0xee, 0xc9, 0x39, 0xb4, 0x02, 0x95, 0x5d, 0x6a, 0x9e, 0xf1, 0x0a, 0x96, 0xf3, 0x44, 0xc9, 0x6e, - 0x6b, 0x1b, 0x6b, 0x2e, 0x21, 0x34, 0x60, 0x49, 0x78, 0x0b, 0x10, 0xfd, 0xc0, 0x0d, 0x89, 0xe9, - 0x96, 0xa1, 0xbc, 0x67, 0x58, 0x06, 0x59, 0xc9, 0x6d, 0xdb, 0xc5, 0xcc, 0x36, 0xdb, 0xad, 0x63, - 0x47, 0x96, 0x36, 0x7f, 0x0b, 0x00, 0x69, 0xea, 0x43, 0x25, 0xc8, 0xdb, 0xbb, 0xf2, 0x02, 0xaa, - 0xc2, 0x99, 0xa6, 0xab, 0xba, 0xad, 0xa6, 0x56, 0xc7, 0xda, 0x6e, 0xbb, 0xd9, 0xd2, 0x34, 0xdc, - 0x6c, 0xca, 0xbf, 0xca, 0x21, 0x04, 0x2b, 0x6c, 0xf7, 0xb1, 0xec, 0xd7, 0x39, 0x74, 0x06, 0x56, - 0xd9, 0x46, 0x12, 0xe1, 0x6f, 0x72, 0x68, 0x1d, 0xaa, 0x0c, 0xd8, 0x68, 0x35, 0xeb, 0x6d, 0x95, - 0xca, 0xdb, 0x3a, 0xb6, 0x0c, 0xac, 0xcb, 0x3e, 0xba, 0x08, 0xe7, 0xf9, 0xac, 0x63, 0xdf, 0xc5, - 0x9a, 0xdb, 0xb6, 0x6c, 0xb7, 0x7d, 0xc7, 0x6e, 0x59, 0xba, 0xbc, 0x8f, 0x2e, 0x43, 0x4d, 0xd4, - 0x6e, 0xec, 0xa9, 0x3b, 0xb8, 0xdd, 0x68, 0x99, 0x66, 0x1b, 0x3b, 0x8e, 0xfc, 0x83, 0x3c, 0xfa, - 0x3f, 0xb8, 0x24, 0x02, 0x34, 0xdb, 0x72, 0x55, 0xc3, 0xc2, 0x4e, 0x5b, 0x73, 0xb0, 0xea, 0x1a, - 0xd6, 0x8e, 0xfc, 0xc3, 0x3c, 0x52, 0xe0, 0x31, 0x11, 0xe4, 0xb4, 0x2c, 0x01, 0x48, 0x88, 0x7e, - 0x94, 0x47, 0xd7, 0x60, 0x63, 0x36, 0x91, 0x8b, 0x9d, 0x3d, 0xc3, 0x52, 0x5d, 0xac, 0xcb, 0x3f, - 0xce, 0xa3, 0x27, 0xe1, 0xba, 0x08, 0x63, 0x9b, 0xdd, 0xc3, 0x96, 0xdb, 0x76, 0x6c, 0xd3, 0xb4, - 0x5b, 0x6e, 0xbb, 0x81, 0x2d, 0x9d, 0xe8, 0xfd, 0xc9, 0x23, 0x38, 0x1d, 0xdc, 0x74, 0x55, 0x87, - 0x9a, 0xf7, 0x6e, 0x1e, 0xd5, 0x60, 0x4d, 0x84, 0xb5, 0xac, 0x3a, 0x56, 0x4d, 0xb7, 0x7e, 0x4f, - 0x7e, 0x6f, 0x8a, 0xc2, 0xb2, 0x75, 0xdc, 0xde, 0xc3, 0x7b, 0xb6, 0x73, 0xaf, 0xdd, 0x70, 0x70, - 0xb3, 0xd9, 0x72, 0xb0, 0xfc, 0x65, 0x69, 0xd2, 0x0d, 0x14, 0xa6, 0x1b, 0xcd, 0xdd, 0x14, 0xf4, - 0x15, 0x09, 0x3d, 0x01, 0x57, 0xa7, 0x40, 0x16, 0x76, 0x5f, 0xb2, 0x1d, 0xa2, 0x54, 0x7d, 0x51, - 0x35, 0x4c, 0x75, 0xdb, 0xc4, 0xf2, 0x57, 0xa5, 0x49, 0x8f, 0x51, 0x68, 0xc3, 0xd0, 0x53, 0xba, - 0xd7, 0x67, 0xeb, 0x6c, 0x59, 0x64, 0xa4, 0xb7, 0x18, 0xd1, 0xd7, 0x24, 0x74, 0x05, 0xd6, 0x67, - 0x80, 0x1c, 0xac, 0x6a, 0x75, 0x0a, 0x79, 0x43, 0x9a, 0x3c, 0x63, 0x66, 0x96, 0xed, 0xb6, 0x1d, - 0xac, 0xea, 0xf7, 0xe4, 0xaf, 0x4f, 0x19, 0x73, 0x47, 0x35, 0x4c, 0xac, 0xb7, 0xb9, 0x22, 0xe2, - 0xc3, 0x6f, 0x48, 0xe8, 0x71, 0x50, 0x44, 0x0c, 0xbf, 0x21, 0xc4, 0xe5, 0x16, 0xd6, 0x5c, 0xc3, - 0xb6, 0xe8, 0x39, 0x7f, 0x6b, 0xca, 0xea, 0x18, 0x48, 0x36, 0xb7, 0x6b, 0x98, 0x26, 0xd6, 0xe5, - 0x6f, 0x4f, 0x79, 0x2a, 0x61, 0x33, 0x0d, 0x72, 0xd2, 0x77, 0xb0, 0xab, 0xd5, 0x29, 0xdf, 0x77, - 0xa4, 0xc9, 0x03, 0x12, 0x02, 0x22, 0x85, 0x7d, 0x57, 0x22, 0x97, 0x25, 0xbe, 0x99, 0xd8, 0x71, - 0x6c, 0x47, 0x7e, 0x5f, 0x9a, 0xbc, 0x5a, 0x7c, 0x5e, 0xfe, 0x40, 0x42, 0xd7, 0xe1, 0xca, 0x8c, - 0x99, 0x09, 0xef, 0xfe, 0x59, 0x42, 0x9b, 0x70, 0x6d, 0x76, 0x80, 0xbd, 0xa4, 0x1a, 0x24, 0xba, - 0x12, 0xce, 0xbf, 0x48, 0xe8, 0x12, 0x5c, 0x98, 0xc5, 0x89, 0x5f, 0xc4, 0x96, 0x2b, 0xff, 0x4b, - 0x12, 0xae, 0x6e, 0xbc, 0xe8, 0xaf, 0x12, 0x3a, 0x0d, 0xcb, 0xcd, 0x7b, 0x96, 0x96, 0x88, 0xfe, - 0x26, 0xa5, 0xd7, 0x3e, 0x96, 0xfd, 0x5d, 0x42, 0x67, 0xe1, 0x94, 0x8e, 0x5f, 0x34, 0x2c, 0xc3, - 0x4d, 0xa4, 0xff, 0xa0, 0x52, 0xcd, 0xc4, 0xaa, 0xd5, 0x6a, 0x24, 0xd2, 0x7f, 0x52, 0x29, 0xa5, - 0xa4, 0x68, 0xe6, 0x8b, 0x3f, 0x14, 0xd0, 0x06, 0x5c, 0x8c, 0x19, 0x1c, 0xbc, 0x63, 0xd0, 0xd4, - 0xc5, 0xd4, 0xe8, 0xb8, 0xd1, 0x94, 0x7f, 0x56, 0x24, 0x61, 0x32, 0x85, 0x70, 0x71, 0xd3, 0x65, - 0x80, 0x9f, 0x17, 0x49, 0xa8, 0x4d, 0x01, 0xf8, 0x8e, 0x28, 0xe4, 0xed, 0xe2, 0x4c, 0x2d, 0x9a, - 0x6d, 0xdd, 0x31, 0x76, 0x08, 0x44, 0xfe, 0x45, 0x71, 0x32, 0xd6, 0x88, 0x47, 0xf1, 0xcb, 0x6e, - 0x5b, 0x53, 0x2d, 0x0d, 0xd3, 0xe8, 0x78, 0xb3, 0x34, 0x89, 0xd1, 0xb1, 0xaa, 0x9b, 0x86, 0x85, - 0xdb, 0xf8, 0x65, 0x0d, 0x63, 0x1d, 0xeb, 0xf2, 0xf7, 0x4a, 0x9b, 0x6f, 0x15, 0x60, 0x35, 0x5b, - 0xdb, 0x48, 0xd2, 0xb5, 0x0c, 0x53, 0x5e, 0x40, 0x67, 0x41, 0x56, 0x75, 0xb2, 0xb1, 0x3b, 0x6a, - 0xcb, 0x24, 0x96, 0x34, 0x6c, 0xb9, 0x4b, 0x8a, 0x4a, 0xcc, 0x27, 0xc8, 0x49, 0xc3, 0xb7, 0x31, - 0x2d, 0x6f, 0xef, 0x98, 0xf6, 0xb6, 0x6a, 0x72, 0xe3, 0xe5, 0x7d, 0xb4, 0x01, 0xeb, 0x3b, 0x9a, - 0x69, 0xb7, 0xf4, 0x36, 0x2b, 0x59, 0x6d, 0xb5, 0xe5, 0xd6, 0xf9, 0x34, 0xb9, 0xaf, 0x3d, 0x52, - 0x6b, 0x66, 0x4f, 0xbd, 0x4a, 0xca, 0x06, 0x53, 0xc1, 0x29, 0x78, 0x26, 0x96, 0xfb, 0xe8, 0x42, - 0x3c, 0x93, 0x86, 0x96, 0x69, 0xef, 0x34, 0x49, 0x52, 0xad, 0xc1, 0x1a, 0x4f, 0xa7, 0x58, 0xd5, - 0x0d, 0x8b, 0xe4, 0xf4, 0x86, 0x63, 0x6f, 0x63, 0x92, 0x4c, 0x93, 0xb9, 0x74, 0x19, 0x4d, 0xdd, - 0x24, 0x83, 0x5e, 0x81, 0x75, 0x55, 0xd7, 0x49, 0x1e, 0x99, 0x9b, 0xcd, 0x2e, 0x43, 0x2d, 0x03, - 0x99, 0xca, 0x64, 0xd7, 0x60, 0x23, 0x03, 0x98, 0x93, 0xc5, 0x2e, 0xc1, 0x85, 0x0c, 0x6c, 0x32, - 0x83, 0x4d, 0xea, 0x99, 0xca, 0x5e, 0x8f, 0x41, 0x75, 0x02, 0x90, 0xc9, 0x5c, 0x17, 0xe1, 0x5c, - 0xd6, 0x0c, 0x31, 0x6b, 0x09, 0xca, 0x67, 0x66, 0xac, 0xc4, 0x47, 0x75, 0xbb, 0xe9, 0x0a, 0x89, - 0x4a, 0xfe, 0xa6, 0xf4, 0xec, 0x4f, 0x8b, 0x70, 0xaa, 0xc9, 0xff, 0x53, 0xdb, 0xf4, 0xc3, 0x87, - 0xfd, 0x8e, 0x8f, 0x34, 0x28, 0xef, 0xf8, 0x11, 0x7f, 0x4c, 0x9d, 0x6a, 0x5e, 0xf1, 0xc1, 0x61, - 0x74, 0x54, 0xcb, 0xfc, 0x2f, 0x55, 0x39, 0xfd, 0xc5, 0xdf, 0xbd, 0xfb, 0x7a, 0x7e, 0x09, 0x55, - 0x6e, 0x3e, 0x7c, 0xe6, 0x26, 0xed, 0x0d, 0xd1, 0x0e, 0x94, 0x69, 0xeb, 0x6a, 0x06, 0x3d, 0x14, - 0x3f, 0xe1, 0xc4, 0x5d, 0x72, 0x6d, 0x52, 0xa0, 0xac, 0x51, 0x82, 0x53, 0x68, 0x85, 0x10, 0xb0, - 0xd7, 0xb2, 0x41, 0xd0, 0xbb, 0x91, 0x7b, 0x3a, 0x87, 0x76, 0xa0, 0x44, 0x89, 0x46, 0x73, 0x6d, - 0x99, 0x62, 0x43, 0x94, 0x6d, 0x19, 0x41, 0xc2, 0x36, 0x7a, 0x3a, 0x87, 0x5e, 0x86, 0x45, 0xfc, - 0x39, 0xbf, 0x33, 0x8e, 0x7c, 0x54, 0xe5, 0x2b, 0xa6, 0xda, 0xe6, 0xda, 0x1c, 0x1d, 0xca, 0x45, - 0x4a, 0xb9, 0xa6, 0x2c, 0x51, 0x4a, 0x46, 0x73, 0x9b, 0x37, 0xd1, 0xc8, 0x83, 0x8a, 0x3a, 0x8e, - 0x02, 0xda, 0xb6, 0xa1, 0xb5, 0x6c, 0xc3, 0x7c, 0x1c, 0xf1, 0x35, 0x4a, 0x7c, 0xb9, 0x76, 0x8e, - 0x10, 0xd3, 0x1e, 0xf8, 0xa6, 0x37, 0x8e, 0x82, 0x76, 0xac, 0x83, 0xb5, 0xda, 0xa8, 0x0d, 0x65, - 0xa2, 0x82, 0x7c, 0xb2, 0x9e, 0x54, 0xc3, 0x55, 0xaa, 0xe1, 0x52, 0x6d, 0x8d, 0x1e, 0xce, 0xd1, - 0xb0, 0x33, 0x53, 0x41, 0x07, 0x80, 0x28, 0x60, 0x4d, 0xe3, 0x49, 0x55, 0x5c, 0xa7, 0x2a, 0x36, - 0x6a, 0xe7, 0x89, 0x0a, 0xd6, 0x9d, 0xcf, 0x54, 0x62, 0x42, 0xa9, 0xee, 0x0d, 0xbb, 0x03, 0x1f, - 0x65, 0x3e, 0x6f, 0xe6, 0xf2, 0xae, 0x53, 0xde, 0x73, 0xca, 0xe9, 0xf4, 0x20, 0x6f, 0xbe, 0x4a, - 0x09, 0x6e, 0xe7, 0x36, 0xef, 0x97, 0x28, 0xfa, 0xd6, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x7a, - 0x11, 0xe4, 0xc8, 0x6b, 0x20, 0x00, 0x00, + // 2869 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0x4b, 0x8c, 0xdb, 0xc6, + 0x19, 0x5e, 0x89, 0x92, 0x56, 0xfa, 0xf7, 0x61, 0x7a, 0xec, 0xb5, 0x65, 0x79, 0x63, 0xaf, 0x59, + 0xdb, 0x71, 0x36, 0xe9, 0x3a, 0x89, 0x8b, 0x22, 0x75, 0x93, 0x16, 0x5c, 0x72, 0xbc, 0xa2, 0x97, + 0x4b, 0x0a, 0x14, 0x95, 0xc4, 0x06, 0x0a, 0x81, 0x96, 0xb8, 0x8a, 0x60, 0xad, 0xb8, 0xa5, 0x28, + 0xa7, 0xdb, 0x43, 0x0f, 0xbd, 0xf6, 0xd2, 0x36, 0x4d, 0xdf, 0x87, 0xf4, 0xd0, 0x5b, 0x5f, 0xd7, + 0x22, 0x68, 0x53, 0xa0, 0x87, 0x3e, 0xae, 0x05, 0x0a, 0xf4, 0x54, 0x04, 0x48, 0x0e, 0xbd, 0xf4, + 0x94, 0xf4, 0x5d, 0xa0, 0x98, 0x07, 0xc9, 0xa1, 0x1e, 0xde, 0x38, 0x45, 0xd1, 0xd3, 0x6a, 0xfe, + 0xf9, 0xfe, 0xe7, 0xfc, 0xf3, 0xff, 0x3f, 0x67, 0x61, 0x75, 0x74, 0xdf, 0xdb, 0xdf, 0x0f, 0x06, + 0xdd, 0xad, 0xc3, 0x30, 0x88, 0x02, 0x54, 0xa4, 0x7f, 0x6a, 0xeb, 0xbd, 0x20, 0xe8, 0x0d, 0xfc, + 0xeb, 0xde, 0x61, 0xff, 0xba, 0x37, 0x1c, 0x06, 0x91, 0x17, 0xf5, 0x83, 0xe1, 0x88, 0x81, 0x6a, + 0x17, 0xf9, 0x2e, 0x5d, 0xdd, 0x1b, 0xef, 0x5f, 0x8f, 0xfa, 0x07, 0xfe, 0x28, 0xf2, 0x0e, 0x0e, + 0x39, 0xe0, 0xfc, 0x24, 0xc0, 0x3f, 0x38, 0x8c, 0x8e, 0xd8, 0xa6, 0x72, 0x03, 0x56, 0x9a, 0x91, + 0x17, 0xf9, 0x8e, 0x3f, 0x3a, 0x0c, 0x86, 0x23, 0x1f, 0x29, 0x50, 0x1c, 0x11, 0x42, 0x35, 0xb7, + 0x91, 0xbb, 0xb6, 0xf4, 0xec, 0x32, 0xc3, 0x6d, 0x31, 0x10, 0xdb, 0x52, 0xd6, 0xa1, 0x9c, 0xe0, + 0x65, 0x90, 0x0e, 0x46, 0x3d, 0x8a, 0xae, 0x38, 0xe4, 0xa7, 0xf2, 0x18, 0x2c, 0x3a, 0xfe, 0x67, + 0xc7, 0xfe, 0x28, 0x42, 0x08, 0x0a, 0x43, 0xef, 0xc0, 0xe7, 0xbb, 0xf4, 0xb7, 0xf2, 0x7a, 0x01, + 0x8a, 0x54, 0x1a, 0x7a, 0x06, 0xe0, 0xde, 0xb8, 0x3f, 0xe8, 0x36, 0x05, 0x7d, 0x27, 0xb9, 0xbe, + 0xed, 0x64, 0xc3, 0x11, 0x40, 0xe8, 0x63, 0xb0, 0xd4, 0xf5, 0x0f, 0x07, 0xc1, 0x11, 0xe3, 0xc9, + 0x53, 0x1e, 0xc4, 0x79, 0xf4, 0x74, 0xc7, 0x11, 0x61, 0xa8, 0x0e, 0xab, 0xfb, 0x41, 0xf8, 0xaa, + 0x17, 0x76, 0xfd, 0x6e, 0x23, 0x08, 0xa3, 0x51, 0xb5, 0xb0, 0x21, 0x5d, 0x5b, 0x7a, 0x76, 0x43, + 0x74, 0x6e, 0xeb, 0x56, 0x06, 0x82, 0x87, 0x51, 0x78, 0xe4, 0x4c, 0xf0, 0x21, 0x0d, 0x64, 0x12, + 0x82, 0xf1, 0x48, 0x7b, 0xc5, 0xef, 0xdc, 0x67, 0x46, 0x14, 0xa9, 0x11, 0x67, 0x05, 0x59, 0xe2, + 0xb6, 0x33, 0xc5, 0x80, 0x6e, 0xc2, 0xca, 0x7e, 0x7f, 0xe0, 0x37, 0x8f, 0x86, 0x1d, 0x26, 0xa1, + 0x44, 0x25, 0x9c, 0xe6, 0x12, 0x6e, 0x89, 0x7b, 0x4e, 0x16, 0x8a, 0x1a, 0x70, 0xaa, 0xeb, 0xdf, + 0x1b, 0xf7, 0x7a, 0xfd, 0x61, 0x4f, 0x0b, 0x86, 0x91, 0xd7, 0x1f, 0xfa, 0xe1, 0xa8, 0xba, 0x48, + 0xfd, 0xb9, 0x90, 0x04, 0x62, 0x12, 0x81, 0x1f, 0xf8, 0xc3, 0xc8, 0x99, 0xc5, 0x8a, 0x9e, 0x84, + 0xf2, 0x81, 0x1f, 0x79, 0x5d, 0x2f, 0xf2, 0xaa, 0x65, 0x6a, 0xc8, 0x09, 0x2e, 0x66, 0x8f, 0x93, + 0x9d, 0x04, 0x50, 0x6b, 0xc2, 0xa9, 0x19, 0x61, 0x22, 0x49, 0x70, 0xdf, 0x3f, 0xa2, 0x47, 0x58, + 0x74, 0xc8, 0x4f, 0x74, 0x15, 0x8a, 0x0f, 0xbc, 0xc1, 0x38, 0x3e, 0x22, 0x99, 0x8b, 0x24, 0x3c, + 0xcc, 0x16, 0xb6, 0x7d, 0x33, 0xff, 0x5c, 0xee, 0x76, 0xa1, 0x2c, 0xc9, 0x05, 0xe5, 0xdd, 0x1c, + 0x94, 0x63, 0x8d, 0x68, 0x13, 0x8a, 0xf4, 0xd4, 0x79, 0x56, 0x9c, 0x16, 0xb3, 0x22, 0x31, 0x8b, + 0x41, 0xd0, 0x47, 0xa1, 0xc4, 0x0e, 0x9b, 0xeb, 0x5a, 0xcb, 0xa4, 0x43, 0x82, 0xe6, 0x20, 0xf4, + 0x69, 0x00, 0xaf, 0xdb, 0xed, 0x93, 0x2b, 0xe4, 0x0d, 0xaa, 0x1d, 0x1a, 0xb8, 0x8b, 0x13, 0x1e, + 0x6f, 0xa9, 0x09, 0x82, 0xe5, 0x81, 0xc0, 0x52, 0x7b, 0x01, 0x4e, 0x4c, 0x6c, 0x8b, 0xfe, 0x57, + 0x98, 0xff, 0xa7, 0x45, 0xff, 0x2b, 0x82, 0xb7, 0xca, 0xfb, 0x79, 0x58, 0xc9, 0xf8, 0x81, 0x9e, + 0x82, 0x93, 0xc3, 0xf1, 0xc1, 0x3d, 0x3f, 0xb4, 0xf7, 0xd5, 0x30, 0xea, 0xef, 0x7b, 0x9d, 0x68, + 0xc4, 0x63, 0x39, 0xbd, 0x81, 0x5e, 0x80, 0x32, 0xf5, 0x9b, 0x1c, 0x7b, 0x9e, 0x5a, 0x7f, 0x69, + 0x56, 0x74, 0xb6, 0x8c, 0x03, 0xaf, 0xe7, 0x6f, 0x33, 0xa4, 0x93, 0xb0, 0xa0, 0xcb, 0x50, 0x88, + 0x8e, 0x0e, 0xfd, 0xaa, 0xb4, 0x91, 0xbb, 0xb6, 0x9a, 0x9c, 0x0b, 0xc5, 0xb9, 0x47, 0x87, 0xbe, + 0x43, 0x77, 0x91, 0x3e, 0x23, 0x48, 0x97, 0x67, 0xaa, 0x79, 0x58, 0xa4, 0x4c, 0x58, 0x16, 0xad, + 0x40, 0x57, 0xb9, 0xee, 0x1c, 0xd5, 0x8d, 0x44, 0x79, 0x7e, 0x28, 0x68, 0x3f, 0x0d, 0xc5, 0x4e, + 0x30, 0x1e, 0x46, 0x34, 0x78, 0x45, 0x87, 0x2d, 0xfe, 0xdb, 0xb8, 0xff, 0x2a, 0x07, 0xab, 0xd9, + 0x94, 0x40, 0xcf, 0x43, 0x85, 0x25, 0x05, 0x89, 0x65, 0x6e, 0xe2, 0x0a, 0x89, 0x48, 0xbe, 0xf4, + 0x43, 0x27, 0x65, 0x40, 0x4f, 0xc1, 0x62, 0x67, 0x30, 0x1e, 0x45, 0x7e, 0x48, 0x95, 0xa5, 0x0e, + 0x69, 0x8c, 0x4a, 0x1d, 0x8a, 0x21, 0x35, 0x03, 0xca, 0xb1, 0x10, 0xf4, 0x78, 0x26, 0x0e, 0xa7, + 0x32, 0x2a, 0x8f, 0x0f, 0x84, 0xf2, 0xc7, 0x1c, 0x40, 0x5a, 0x1f, 0xd1, 0xa7, 0xa0, 0xe2, 0x09, + 0x69, 0x23, 0x16, 0xb6, 0x14, 0xb5, 0x95, 0x24, 0x10, 0x3b, 0xa6, 0x94, 0x05, 0x6d, 0xc0, 0x92, + 0x37, 0x8e, 0x02, 0x37, 0xec, 0xf7, 0x7a, 0xdc, 0x97, 0xb2, 0x23, 0x92, 0x48, 0xa1, 0xe6, 0x45, + 0x2c, 0xe8, 0xc6, 0x99, 0x73, 0x32, 0x5b, 0xef, 0x82, 0xae, 0xef, 0x08, 0xa0, 0xda, 0xf3, 0xb0, + 0x9a, 0xd5, 0xf8, 0x48, 0x67, 0xf5, 0x79, 0x58, 0x12, 0x8a, 0x39, 0x3a, 0x03, 0x25, 0x26, 0x9a, + 0x73, 0xf3, 0xd5, 0xff, 0xc4, 0x72, 0xe5, 0xed, 0x1c, 0xc8, 0x93, 0x45, 0x7c, 0xae, 0x05, 0x3a, + 0x54, 0x42, 0x7f, 0x14, 0x8c, 0xc3, 0x8e, 0x1f, 0xdf, 0xc6, 0xab, 0x73, 0x1a, 0xc1, 0x96, 0x13, + 0x03, 0xf9, 0x09, 0x24, 0x8c, 0x1f, 0x32, 0xbe, 0x59, 0x79, 0x8f, 0x14, 0x5f, 0x03, 0x56, 0x32, + 0x5d, 0xe6, 0xc3, 0x47, 0x58, 0x79, 0xbb, 0x00, 0x45, 0x5a, 0xd1, 0xd1, 0xd3, 0x50, 0x21, 0x7d, + 0x82, 0x2e, 0x78, 0xdd, 0x96, 0x85, 0xba, 0x4a, 0xe9, 0xf5, 0x05, 0x27, 0x05, 0xa1, 0x1b, 0x7c, + 0x00, 0x60, 0x2c, 0xf9, 0xe9, 0x01, 0x20, 0xe6, 0x11, 0x60, 0xe8, 0xe3, 0xf1, 0x08, 0xc0, 0xb8, + 0xa4, 0x19, 0x23, 0x40, 0xcc, 0x26, 0x02, 0x89, 0x79, 0x87, 0x71, 0xf7, 0xa9, 0x16, 0x66, 0x77, + 0x25, 0x62, 0x5e, 0x02, 0x42, 0x38, 0xd3, 0xec, 0x19, 0xe3, 0xdc, 0x66, 0x1f, 0xf3, 0x4f, 0xb1, + 0xa0, 0xcf, 0x40, 0x35, 0x3e, 0xea, 0x49, 0x3c, 0xef, 0xfc, 0x71, 0xfb, 0x71, 0xe6, 0xc0, 0xea, + 0x0b, 0xce, 0x5c, 0x11, 0xe8, 0xf9, 0x74, 0x9a, 0x60, 0x32, 0x17, 0x67, 0x4e, 0x13, 0xb1, 0xa0, + 0x2c, 0x18, 0xdd, 0x85, 0xb3, 0xdd, 0xd9, 0xd3, 0x02, 0x1f, 0x06, 0x8e, 0x99, 0x29, 0xea, 0x0b, + 0xce, 0x3c, 0x01, 0xe8, 0x13, 0xb0, 0xdc, 0xf5, 0x1f, 0x98, 0x41, 0x70, 0xc8, 0x04, 0x56, 0xa8, + 0xc0, 0xb4, 0xdc, 0xa5, 0x5b, 0xf5, 0x05, 0x27, 0x03, 0xdd, 0x5e, 0x06, 0xf0, 0xc9, 0x8f, 0x36, + 0x29, 0x83, 0xca, 0x00, 0x96, 0x45, 0x34, 0x5a, 0x87, 0x4a, 0x3f, 0xf2, 0x43, 0x3a, 0x06, 0xf3, + 0x46, 0x99, 0x12, 0x84, 0x5c, 0xce, 0x67, 0x72, 0xf9, 0x2a, 0x48, 0x7e, 0x18, 0xf2, 0x84, 0x89, + 0xc3, 0xa3, 0x76, 0x68, 0x3f, 0xb9, 0x37, 0xf0, 0x71, 0x18, 0x3a, 0x04, 0xa0, 0x7c, 0x29, 0x07, + 0x2b, 0x19, 0x32, 0x7a, 0x12, 0x16, 0xfd, 0x30, 0xa4, 0x97, 0x33, 0x37, 0xef, 0x72, 0xc6, 0x08, + 0x54, 0x85, 0xc5, 0x03, 0x7f, 0x34, 0xf2, 0x7a, 0xf1, 0xbd, 0x8b, 0x97, 0xe8, 0x06, 0x2c, 0x8d, + 0xc6, 0xbd, 0x9e, 0x3f, 0xa2, 0xe3, 0x7b, 0x55, 0xa2, 0xe5, 0x22, 0x11, 0x95, 0xec, 0x38, 0x22, + 0x4a, 0xb1, 0xa0, 0x92, 0xdc, 0x1e, 0x72, 0xa3, 0x7d, 0x72, 0xd9, 0xf9, 0x2d, 0x65, 0x8b, 0xcc, + 0x04, 0x97, 0x3f, 0x66, 0x82, 0x53, 0xde, 0x8c, 0x9b, 0x07, 0x93, 0x58, 0x83, 0x72, 0xdc, 0x09, + 0xb8, 0xd0, 0x64, 0x3d, 0x37, 0x90, 0x72, 0x1a, 0xc8, 0x0a, 0x0d, 0x99, 0x18, 0xa0, 0xc2, 0xb1, + 0x01, 0xba, 0x09, 0x2b, 0x9e, 0x18, 0x5e, 0x7e, 0xa7, 0x66, 0x9f, 0x48, 0x16, 0xaa, 0xbc, 0x91, + 0x8b, 0x3b, 0x03, 0x33, 0x7f, 0x5e, 0xdd, 0xe2, 0x26, 0xe6, 0x67, 0x9a, 0x28, 0x3d, 0xba, 0x89, + 0x85, 0x0f, 0x6e, 0xe2, 0x5b, 0xd9, 0xfe, 0xf1, 0x70, 0x3b, 0xe7, 0x27, 0xcb, 0xff, 0x31, 0xc8, + 0x7f, 0xca, 0x41, 0x75, 0x5e, 0x29, 0x22, 0x09, 0x13, 0x97, 0xa2, 0x38, 0x61, 0xe2, 0xf5, 0xdc, + 0x84, 0x11, 0xbc, 0x94, 0x66, 0x7a, 0x59, 0x48, 0xbd, 0xcc, 0xf6, 0xc2, 0xe2, 0x07, 0xe8, 0x85, + 0xd3, 0xbe, 0x96, 0x3e, 0xb8, 0xaf, 0xdf, 0xcf, 0x43, 0x25, 0x29, 0xff, 0xa4, 0xb0, 0x0c, 0x82, + 0x8e, 0x37, 0x20, 0x94, 0xb8, 0xb0, 0x24, 0x04, 0x74, 0x01, 0x20, 0xf4, 0x0f, 0x82, 0xc8, 0xa7, + 0xdb, 0x6c, 0x24, 0x13, 0x28, 0xc4, 0xcd, 0xc3, 0xa0, 0x6b, 0x91, 0x0f, 0x5e, 0xee, 0x26, 0x5f, + 0xa2, 0xcb, 0xb0, 0xd2, 0x89, 0x6b, 0x23, 0xdd, 0x67, 0x0e, 0x67, 0x89, 0x44, 0x3b, 0xf9, 0x42, + 0x1e, 0x1d, 0x7a, 0x1d, 0xe6, 0x79, 0xc5, 0x49, 0x09, 0x24, 0xf0, 0xa4, 0x35, 0x51, 0xf6, 0x12, + 0x0b, 0x7c, 0xbc, 0x46, 0x0a, 0x2c, 0xc7, 0x87, 0x40, 0xa6, 0x47, 0xda, 0x02, 0x2a, 0x4e, 0x86, + 0x26, 0x62, 0xa8, 0x8c, 0x72, 0x16, 0x43, 0xe5, 0x54, 0x61, 0xd1, 0xeb, 0x76, 0x43, 0x7f, 0x34, + 0xa2, 0xc5, 0xba, 0xe2, 0xc4, 0x4b, 0xe5, 0xf7, 0xb9, 0x74, 0x64, 0x48, 0x62, 0x45, 0x5a, 0x89, + 0x46, 0xe7, 0x53, 0x1e, 0xab, 0x84, 0x40, 0x2a, 0x55, 0xff, 0x20, 0x4d, 0x6b, 0xb6, 0x10, 0x12, + 0x44, 0x9a, 0x75, 0x5d, 0x0b, 0x33, 0x93, 0xbd, 0xf8, 0xe8, 0xc9, 0xfe, 0x08, 0x09, 0xf0, 0x5e, + 0x1e, 0xce, 0xce, 0xe9, 0x6d, 0x0f, 0xbb, 0xb5, 0xf1, 0x41, 0xe7, 0x8f, 0x39, 0x68, 0xe9, 0xd8, + 0x83, 0x2e, 0xcc, 0x38, 0xe8, 0xa4, 0x24, 0x17, 0x27, 0x4a, 0x72, 0x15, 0x16, 0xc3, 0xf1, 0x30, + 0xea, 0x27, 0x39, 0x10, 0x2f, 0x49, 0x72, 0xbe, 0x1a, 0x84, 0xf7, 0xfb, 0xc3, 0x9e, 0xde, 0x0f, + 0x79, 0x02, 0x08, 0x14, 0x64, 0x01, 0xd0, 0x3e, 0xcd, 0xde, 0x3f, 0xca, 0xb4, 0xf7, 0x6c, 0x3d, + 0xbc, 0xb7, 0x33, 0xba, 0xf0, 0x1a, 0x22, 0x48, 0x20, 0x5f, 0x63, 0x13, 0xdb, 0xc7, 0x4d, 0xa0, + 0x2b, 0xe2, 0x04, 0xfa, 0x05, 0x28, 0x9b, 0x41, 0x8f, 0xf1, 0x3d, 0x07, 0x95, 0xe4, 0xcd, 0x8a, + 0x0f, 0x8e, 0xb5, 0x2d, 0xf6, 0x68, 0xb5, 0x15, 0x3f, 0x5a, 0x6d, 0xb9, 0x31, 0xc2, 0x49, 0xc1, + 0x48, 0x81, 0xa2, 0x2f, 0xcc, 0x8e, 0xf1, 0x63, 0x15, 0x7f, 0x61, 0xf0, 0xb3, 0x3d, 0x53, 0x12, + 0x7a, 0xa6, 0x72, 0x13, 0x4e, 0xb6, 0x46, 0x7e, 0x68, 0x0c, 0x23, 0x02, 0xe5, 0xcf, 0x55, 0x57, + 0xa0, 0xd4, 0xa7, 0x04, 0x6e, 0xc5, 0x0a, 0x97, 0xc7, 0x51, 0x7c, 0x53, 0xf9, 0x24, 0xac, 0xf2, + 0xe9, 0x37, 0x66, 0x7c, 0x22, 0xfb, 0x68, 0x16, 0x8f, 0x38, 0x1c, 0x95, 0x79, 0x3b, 0x7b, 0x06, + 0x96, 0x45, 0x32, 0xaa, 0xc1, 0xa2, 0x4f, 0x93, 0x91, 0xbd, 0x75, 0x94, 0xeb, 0x0b, 0x4e, 0x4c, + 0xd8, 0x2e, 0x82, 0xf4, 0xc0, 0x1b, 0x28, 0xb7, 0xa1, 0xc4, 0x2c, 0x20, 0xbe, 0xa4, 0xcf, 0x22, + 0xe5, 0xf8, 0x01, 0x04, 0x41, 0x61, 0x74, 0x34, 0xec, 0xf0, 0xe9, 0x9c, 0xfe, 0x26, 0xa9, 0xcb, + 0x1f, 0x45, 0x24, 0x4a, 0xe5, 0x2b, 0xa5, 0x03, 0x90, 0x4e, 0x1a, 0xe8, 0x05, 0x58, 0x4d, 0x67, + 0x0d, 0x61, 0xbe, 0x59, 0x9b, 0x1a, 0x4a, 0xe8, 0x85, 0x9b, 0x00, 0x13, 0x25, 0xec, 0x32, 0xc5, + 0xf5, 0x9e, 0xad, 0x36, 0x03, 0x58, 0x12, 0x3e, 0xea, 0x51, 0x15, 0x4e, 0xb7, 0xac, 0x5d, 0xcb, + 0x7e, 0xc9, 0x6a, 0x6f, 0xb7, 0x0c, 0x53, 0xc7, 0x4e, 0xdb, 0xbd, 0xd3, 0xc0, 0xf2, 0x02, 0x5a, + 0x04, 0xe9, 0xb6, 0xb1, 0x2d, 0xe7, 0x50, 0x05, 0x8a, 0xdb, 0xea, 0x5d, 0x6c, 0xca, 0x79, 0xb4, + 0x0a, 0x40, 0x51, 0x0d, 0x55, 0xdb, 0x6d, 0xca, 0x12, 0x02, 0x28, 0x69, 0xad, 0xa6, 0x6b, 0xef, + 0xc9, 0x05, 0xf2, 0x7b, 0x57, 0xb5, 0x8c, 0x5d, 0x5b, 0x2e, 0x92, 0xdf, 0xba, 0xad, 0xed, 0x62, + 0x47, 0x2e, 0x6d, 0xea, 0x50, 0x49, 0x5e, 0x30, 0xd0, 0x19, 0x40, 0x19, 0x75, 0xb1, 0xb2, 0x25, + 0x58, 0xd4, 0xcc, 0x56, 0xd3, 0xc5, 0x8e, 0x9c, 0x23, 0x9a, 0x77, 0xb4, 0x6d, 0x39, 0x4f, 0x34, + 0x9b, 0xb6, 0xa6, 0x9a, 0xb2, 0xb4, 0x69, 0x93, 0x31, 0x33, 0xfd, 0x06, 0x47, 0xe7, 0x60, 0x2d, + 0x16, 0xa4, 0xe3, 0x86, 0x69, 0xdf, 0x49, 0x0d, 0x2f, 0x43, 0xa1, 0x8e, 0xcd, 0x3d, 0x39, 0x87, + 0x56, 0xa0, 0xb2, 0x4b, 0xcd, 0x33, 0xee, 0x62, 0x39, 0x4f, 0x94, 0xec, 0xb6, 0xb6, 0xb1, 0xe6, + 0x12, 0x81, 0x06, 0x2c, 0x09, 0x6f, 0x01, 0x62, 0x1c, 0xb8, 0x21, 0xb1, 0xb8, 0x65, 0x28, 0xef, + 0x19, 0x96, 0x41, 0x38, 0xb9, 0x6d, 0xbb, 0x98, 0xd9, 0x66, 0xbb, 0x75, 0xec, 0xc8, 0xd2, 0xe6, + 0x9f, 0x01, 0x20, 0x2d, 0x7d, 0xa8, 0x04, 0x79, 0x7b, 0x57, 0x5e, 0x40, 0x55, 0x38, 0xd5, 0x74, + 0x55, 0xb7, 0xd5, 0xd4, 0xea, 0x58, 0xdb, 0x6d, 0x37, 0x5b, 0x9a, 0x86, 0x9b, 0x4d, 0xf9, 0xd7, + 0x39, 0x84, 0x60, 0x85, 0x79, 0x1f, 0xd3, 0x7e, 0x93, 0x43, 0xa7, 0x60, 0x95, 0x39, 0x92, 0x10, + 0x7f, 0x9b, 0x43, 0xeb, 0x50, 0x65, 0xc0, 0x46, 0xab, 0x59, 0x6f, 0xab, 0x94, 0xde, 0xd6, 0xb1, + 0x65, 0x60, 0x5d, 0xf6, 0xd1, 0x79, 0x38, 0xcb, 0x77, 0x1d, 0xfb, 0x36, 0xd6, 0xdc, 0xb6, 0x65, + 0xbb, 0xed, 0x5b, 0x76, 0xcb, 0xd2, 0xe5, 0x7d, 0x74, 0x11, 0x6a, 0xa2, 0x76, 0x63, 0x4f, 0xdd, + 0xc1, 0xed, 0x46, 0xcb, 0x34, 0xdb, 0xd8, 0x71, 0xe4, 0x1f, 0xe4, 0xd1, 0x47, 0xe0, 0x82, 0x08, + 0xd0, 0x6c, 0xcb, 0x55, 0x0d, 0x0b, 0x3b, 0x6d, 0xcd, 0xc1, 0xaa, 0x6b, 0x58, 0x3b, 0xf2, 0x0f, + 0xf3, 0x48, 0x81, 0xc7, 0x44, 0x90, 0xd3, 0xb2, 0x04, 0x20, 0x11, 0xf4, 0xa3, 0x3c, 0xba, 0x02, + 0x1b, 0xb3, 0x05, 0xb9, 0xd8, 0xd9, 0x33, 0x2c, 0xd5, 0xc5, 0xba, 0xfc, 0xe3, 0x3c, 0x7a, 0x12, + 0xae, 0x8a, 0x30, 0xe6, 0xec, 0x1e, 0xb6, 0xdc, 0xb6, 0x63, 0x9b, 0xa6, 0xdd, 0x72, 0xdb, 0x0d, + 0x6c, 0xe9, 0x44, 0xef, 0x4f, 0x1e, 0x22, 0xd3, 0xc1, 0x4d, 0x57, 0x75, 0xa8, 0x79, 0xef, 0xe4, + 0x51, 0x0d, 0xd6, 0x44, 0x58, 0xcb, 0xaa, 0x63, 0xd5, 0x74, 0xeb, 0x77, 0xe4, 0x77, 0xa7, 0x44, + 0x58, 0xb6, 0x8e, 0xdb, 0x7b, 0x78, 0xcf, 0x76, 0xee, 0xb4, 0x1b, 0x0e, 0x6e, 0x36, 0x5b, 0x0e, + 0x96, 0xbf, 0x2c, 0x4d, 0x86, 0x81, 0xc2, 0x74, 0xa3, 0xb9, 0x9b, 0x82, 0xbe, 0x22, 0xa1, 0x27, + 0xe0, 0xf2, 0x14, 0xc8, 0xc2, 0xee, 0x4b, 0xb6, 0x43, 0x94, 0xaa, 0x2f, 0xaa, 0x86, 0xa9, 0x6e, + 0x9b, 0x58, 0xfe, 0xaa, 0x34, 0x19, 0x31, 0x0a, 0x6d, 0x18, 0x7a, 0x2a, 0xee, 0xb5, 0xd9, 0x3a, + 0x5b, 0x16, 0x59, 0xe9, 0x2d, 0x26, 0xe8, 0x6b, 0x12, 0xba, 0x04, 0xeb, 0x33, 0x40, 0x0e, 0x56, + 0xb5, 0x3a, 0x85, 0xbc, 0x2e, 0x4d, 0x9e, 0x31, 0x33, 0xcb, 0x76, 0xdb, 0x0e, 0x56, 0xf5, 0x3b, + 0xf2, 0xd7, 0xa7, 0x8c, 0xb9, 0xa5, 0x1a, 0x26, 0xd6, 0xdb, 0x5c, 0x11, 0x89, 0xe1, 0x37, 0x24, + 0xf4, 0x38, 0x28, 0x22, 0x86, 0xdf, 0x10, 0x12, 0x72, 0x0b, 0x6b, 0xae, 0x61, 0x5b, 0xf4, 0x9c, + 0xbf, 0x35, 0x65, 0x75, 0x0c, 0x24, 0xce, 0xed, 0x1a, 0xa6, 0x89, 0x75, 0xf9, 0xdb, 0x53, 0x91, + 0x4a, 0xa4, 0x99, 0x06, 0x39, 0xe9, 0x5b, 0xd8, 0xd5, 0xea, 0x54, 0xde, 0x77, 0xa4, 0xc9, 0x03, + 0x12, 0x12, 0x22, 0x85, 0x7d, 0x77, 0x2a, 0x0e, 0x0d, 0x5b, 0x6f, 0x1b, 0x96, 0xe1, 0x1a, 0xaa, + 0x69, 0xdc, 0x25, 0x2e, 0xfc, 0x52, 0x22, 0xf7, 0x29, 0xbe, 0xbc, 0xd8, 0x71, 0x6c, 0x47, 0x7e, + 0x4f, 0x9a, 0xbc, 0x7d, 0x7c, 0x5f, 0x7e, 0x5f, 0x42, 0x57, 0xe1, 0xd2, 0x8c, 0x9d, 0x89, 0x03, + 0xf8, 0x8b, 0x84, 0x36, 0xe1, 0xca, 0xec, 0x1c, 0x7c, 0x49, 0x35, 0x48, 0x02, 0x26, 0x32, 0xff, + 0x2a, 0xa1, 0x0b, 0x70, 0x6e, 0x96, 0x4c, 0xfc, 0x22, 0xb6, 0x5c, 0xf9, 0xdf, 0x92, 0x70, 0xbb, + 0x63, 0xa6, 0xbf, 0x49, 0xe8, 0x24, 0x2c, 0x37, 0xef, 0x58, 0x5a, 0x42, 0xfa, 0xbb, 0x94, 0x56, + 0x86, 0x98, 0xf6, 0x0f, 0x09, 0x9d, 0x86, 0x13, 0x3a, 0x7e, 0x91, 0xf8, 0x9c, 0x50, 0xff, 0x49, + 0xa9, 0x9a, 0x89, 0x55, 0xab, 0xd5, 0x48, 0xa8, 0xff, 0xa2, 0x54, 0x2a, 0x92, 0xa2, 0x59, 0x2c, + 0xfe, 0x50, 0x40, 0x1b, 0x70, 0x3e, 0x96, 0xe0, 0xe0, 0x1d, 0x83, 0x56, 0x37, 0xa6, 0x46, 0xc7, + 0x8d, 0xa6, 0xfc, 0xb3, 0x22, 0xc9, 0xa4, 0x29, 0x84, 0x8b, 0x9b, 0x2e, 0x03, 0xfc, 0xbc, 0x48, + 0x4e, 0x61, 0x0a, 0xc0, 0x3d, 0xa2, 0x90, 0xb7, 0x8a, 0x33, 0xb5, 0x68, 0xb6, 0x75, 0xcb, 0xd8, + 0x21, 0x10, 0xf9, 0x17, 0xc5, 0xc9, 0x74, 0x24, 0x11, 0xc5, 0x2f, 0xbb, 0x6d, 0x4d, 0xb5, 0x34, + 0x4c, 0x13, 0xe8, 0x8d, 0xd2, 0x24, 0x46, 0xc7, 0xaa, 0x6e, 0x1a, 0x16, 0x6e, 0xe3, 0x97, 0x35, + 0x8c, 0x75, 0xac, 0xcb, 0xdf, 0x2b, 0x6d, 0xbe, 0x59, 0x80, 0xd5, 0x6c, 0xfb, 0x23, 0x75, 0xd9, + 0x32, 0x4c, 0x79, 0x01, 0x9d, 0x06, 0x59, 0xd5, 0x89, 0x63, 0xb7, 0xd4, 0x96, 0x49, 0x2c, 0x69, + 0xd8, 0x72, 0x97, 0xf4, 0x9d, 0x58, 0x9e, 0x40, 0x27, 0x33, 0xe1, 0xc6, 0x34, 0xbd, 0xbd, 0x63, + 0xda, 0xdb, 0xaa, 0xc9, 0x8d, 0x97, 0xf7, 0xd1, 0x06, 0xac, 0xef, 0x68, 0xa6, 0xdd, 0xd2, 0xdb, + 0xac, 0xab, 0xb5, 0xd5, 0x96, 0x5b, 0xe7, 0xdb, 0xe4, 0x4a, 0xf7, 0x48, 0x3b, 0x9a, 0xbd, 0xf5, + 0x0a, 0xe9, 0x2c, 0x4c, 0x05, 0x17, 0xc1, 0x8b, 0xb5, 0xdc, 0x47, 0xe7, 0xe2, 0x9d, 0x34, 0xb5, + 0x4c, 0x7b, 0xa7, 0x49, 0xea, 0x6e, 0x0d, 0xd6, 0x78, 0xc5, 0xc5, 0xaa, 0x6e, 0x58, 0xa4, 0xec, + 0x37, 0x1c, 0x7b, 0x1b, 0x93, 0x7a, 0x9b, 0xec, 0xa5, 0x6c, 0xb4, 0xba, 0x93, 0x22, 0x7b, 0x09, + 0xd6, 0x55, 0x5d, 0x27, 0xa5, 0x66, 0x6e, 0xc1, 0xbb, 0x08, 0xb5, 0x0c, 0x64, 0xaa, 0xd8, 0x5d, + 0x81, 0x8d, 0x0c, 0x60, 0x4e, 0xa1, 0xbb, 0x00, 0xe7, 0x32, 0xb0, 0xc9, 0x22, 0x37, 0xa9, 0x67, + 0xaa, 0xc0, 0x3d, 0x06, 0xd5, 0x09, 0x40, 0xa6, 0xb8, 0x9d, 0x87, 0x33, 0x59, 0x33, 0xc4, 0xc2, + 0x26, 0x28, 0x9f, 0x59, 0xd4, 0x92, 0x18, 0xd5, 0xed, 0xa6, 0x2b, 0xd4, 0x32, 0xf9, 0x9b, 0xd2, + 0xb3, 0x3f, 0x2d, 0xc2, 0x89, 0x26, 0xff, 0x67, 0x6e, 0xd3, 0x0f, 0x1f, 0xf4, 0x3b, 0x3e, 0xd2, + 0xa0, 0xbc, 0xe3, 0x47, 0xfc, 0xbd, 0x75, 0x6a, 0xbe, 0xc5, 0x07, 0x87, 0xd1, 0x51, 0x2d, 0xf3, + 0xef, 0x56, 0xe5, 0xe4, 0x17, 0x7f, 0xf7, 0xce, 0x6b, 0xf9, 0x25, 0x54, 0xb9, 0xfe, 0xe0, 0x99, + 0xeb, 0x74, 0x7c, 0x44, 0x3b, 0x50, 0xa6, 0xd3, 0xad, 0x19, 0xf4, 0x50, 0xfc, 0xca, 0x13, 0x0f, + 0xd2, 0xb5, 0x49, 0x82, 0xb2, 0x46, 0x05, 0x9c, 0x40, 0x2b, 0x44, 0x00, 0x7b, 0x50, 0x1b, 0x04, + 0xbd, 0x6b, 0xb9, 0xa7, 0x73, 0x68, 0x07, 0x4a, 0x54, 0xd0, 0x68, 0xae, 0x2d, 0x53, 0xd2, 0x10, + 0x95, 0xb6, 0x8c, 0x20, 0x91, 0x36, 0x7a, 0x3a, 0x87, 0x5e, 0x86, 0x45, 0xfc, 0x39, 0xbf, 0x33, + 0x8e, 0x7c, 0x54, 0xe5, 0x1c, 0x53, 0x93, 0x75, 0x6d, 0x8e, 0x0e, 0xe5, 0x3c, 0x15, 0xb9, 0xa6, + 0x2c, 0x51, 0x91, 0x4c, 0xcc, 0x4d, 0x3e, 0x67, 0x23, 0x0f, 0x2a, 0xea, 0x38, 0x0a, 0xe8, 0x64, + 0x87, 0xd6, 0xb2, 0x33, 0xf5, 0x71, 0x82, 0xaf, 0x50, 0xc1, 0x17, 0x6b, 0x67, 0x88, 0x60, 0x3a, + 0x26, 0x5f, 0xf7, 0xc6, 0x51, 0xd0, 0x8e, 0x75, 0xb0, 0x69, 0x1c, 0xb5, 0xa1, 0x4c, 0x54, 0x90, + 0xaf, 0xda, 0x47, 0xd5, 0x70, 0x99, 0x6a, 0xb8, 0x50, 0x5b, 0xa3, 0x87, 0x73, 0x34, 0xec, 0xcc, + 0x54, 0xd0, 0x01, 0x20, 0x0a, 0xd8, 0x5c, 0xf9, 0xa8, 0x2a, 0xae, 0x52, 0x15, 0x1b, 0xb5, 0xb3, + 0x44, 0x05, 0x1b, 0xe0, 0x67, 0x2a, 0x31, 0xa1, 0x54, 0xf7, 0x86, 0xdd, 0x81, 0x8f, 0x32, 0x5f, + 0x40, 0x73, 0xe5, 0xae, 0x53, 0xb9, 0x67, 0x94, 0x93, 0xe9, 0x41, 0x5e, 0x7f, 0x85, 0x0a, 0xb8, + 0x99, 0xdb, 0xbc, 0x57, 0xa2, 0xe8, 0x1b, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0xfa, 0xb4, 0x59, + 0x34, 0x8e, 0x20, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/proto/skaffold.proto b/proto/skaffold.proto index 73e1307888a..410d5c8eaea 100644 --- a/proto/skaffold.proto +++ b/proto/skaffold.proto @@ -443,6 +443,11 @@ enum StatusCode { STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR = 411; STATUSCHECK_DEPLOYMENT_FETCH_ERR = 412; + // Pod States + + // Pod Initializing + STATUSCHECK_POD_INITIALIZING= 451; + // Unknown Error Codes // Could not determine error and phase From bc7bad10a5c20bd751d46c4813b653605c874410 Mon Sep 17 00:00:00 2001 From: tejal29 Date: Wed, 29 Jul 2020 23:13:56 -0700 Subject: [PATCH 020/138] Return poll deployment if status has a non retry-able container error is in first iteration --- pkg/skaffold/deploy/resource/deployment.go | 21 ++++++++++++++++++ pkg/skaffold/deploy/status_check.go | 25 ++++++++++++++++------ pkg/skaffold/deploy/status_check_test.go | 10 ++++----- pkg/skaffold/runner/deploy.go | 2 +- 4 files changed, 46 insertions(+), 12 deletions(-) diff --git a/pkg/skaffold/deploy/resource/deployment.go b/pkg/skaffold/deploy/resource/deployment.go index 3512e4187f1..da98264dca8 100644 --- a/pkg/skaffold/deploy/resource/deployment.go +++ b/pkg/skaffold/deploy/resource/deployment.go @@ -45,6 +45,13 @@ const ( var ( msgKubectlKilled = "kubectl rollout status command interrupted" MsgKubectlConnection = "kubectl connection error" + + nonRetryContainerErrors = map[proto.StatusCode]struct{}{ + proto.StatusCode_STATUSCHECK_IMAGE_PULL_ERR: {}, + proto.StatusCode_STATUSCHECK_RUN_CONTAINER_ERR: {}, + proto.StatusCode_STATUSCHECK_CONTAINER_TERMINATED: {}, + proto.StatusCode_STATUSCHECK_CONTAINER_RESTARTING: {}, + } ) type Deployment struct { @@ -133,6 +140,10 @@ func (d *Deployment) IsStatusCheckComplete() bool { return d.done } +func (d *Deployment) MarkComplete() { + d.done = true +} + // This returns a string representing deployment status along with tab header // e.g. // - testNs:deployment/leeroy-app: waiting for rollout to complete. (1/2) pending @@ -209,6 +220,16 @@ func isErrAndNotRetryAble(statusCode proto.StatusCode) bool { statusCode != proto.StatusCode_STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING } +func (d *Deployment) AreContainersNotRetryAble() bool { + // Go through all pod statuses and make sure they are not retriable. + for _, p := range d.pods { + if _, ok := nonRetryContainerErrors[p.ActionableError().ErrCode]; ok { + return true + } + } + return false +} + func (d *Deployment) fetchPods(ctx context.Context) error { timeoutContext, cancel := context.WithTimeout(ctx, defaultPodCheckDeadline) defer cancel() diff --git a/pkg/skaffold/deploy/status_check.go b/pkg/skaffold/deploy/status_check.go index c5de81a0c01..d1e8fa20c40 100644 --- a/pkg/skaffold/deploy/status_check.go +++ b/pkg/skaffold/deploy/status_check.go @@ -63,14 +63,21 @@ type counter struct { failed int32 } -func StatusCheck(ctx context.Context, defaultLabeller *DefaultLabeller, runCtx *runcontext.RunContext, out io.Writer) error { +func StatusCheck(ctx context.Context, defaultLabeller *DefaultLabeller, runCtx *runcontext.RunContext, out io.Writer, iteration int) error { event.StatusCheckEventStarted() - errCode, err := statusCheck(ctx, defaultLabeller, runCtx, out) + s := &checker{ + iteration: iteration, + } + errCode, err := s.statusCheck(ctx, defaultLabeller, runCtx, out) event.StatusCheckEventEnded(errCode, err) return err } -func statusCheck(ctx context.Context, defaultLabeller *DefaultLabeller, runCtx *runcontext.RunContext, out io.Writer) (proto.StatusCode, error) { +type checker struct { + iteration int +} + +func (s checker) statusCheck(ctx context.Context, defaultLabeller *DefaultLabeller, runCtx *runcontext.RunContext, out io.Writer) (proto.StatusCode, error) { client, err := pkgkubernetes.Client() if err != nil { return proto.StatusCode_STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR, fmt.Errorf("getting Kubernetes client: %w", err) @@ -101,7 +108,7 @@ func statusCheck(ctx context.Context, defaultLabeller *DefaultLabeller, runCtx * go func(r *resource.Deployment) { defer wg.Done() // keep updating the resource status until it fails/succeeds/times out - pollDeploymentStatus(ctx, runCtx, r) + s.pollDeploymentStatus(ctx, runCtx, r) rcCopy := c.markProcessed(r.Status().Error()) printStatusCheckSummary(out, r, rcCopy) }(d) @@ -143,11 +150,10 @@ func getDeployments(client kubernetes.Interface, ns string, l *DefaultLabeller, deployments[i] = resource.NewDeployment(d.Name, d.Namespace, deadline).WithValidator(pd) } - return deployments, nil } -func pollDeploymentStatus(ctx context.Context, runCtx *runcontext.RunContext, r *resource.Deployment) { +func (s checker) pollDeploymentStatus(ctx context.Context, runCtx *runcontext.RunContext, r *resource.Deployment) { pollDuration := time.Duration(defaultPollPeriodInMilliseconds) * time.Millisecond // Add poll duration to account for one last attempt after progressDeadlineSeconds. timeoutContext, cancel := context.WithTimeout(ctx, r.Deadline()+pollDuration) @@ -165,6 +171,10 @@ func pollDeploymentStatus(ctx context.Context, runCtx *runcontext.RunContext, r if r.IsStatusCheckComplete() { return } + if s.iteration == 0 && r.AreContainersNotRetryAble() { + r.MarkComplete() + return + } } } } @@ -197,6 +207,9 @@ func printStatusCheckSummary(out io.Writer, r *resource.Deployment, c counter) { event.ResourceStatusCheckEventCompleted(r.String(), r.Status().ActionableError()) status := fmt.Sprintf("%s %s", tabHeader, r) if ae.ErrCode != proto.StatusCode_STATUSCHECK_SUCCESS { + if str := r.ReportSinceLastUpdated(); str != "" { + fmt.Fprintln(out, trimNewLine(str)) + } status = fmt.Sprintf("%s failed.%s Error: %s.", status, trimNewLine(getPendingMessage(c.pending, c.total)), diff --git a/pkg/skaffold/deploy/status_check_test.go b/pkg/skaffold/deploy/status_check_test.go index 552a2e2ba3b..acf8904d797 100644 --- a/pkg/skaffold/deploy/status_check_test.go +++ b/pkg/skaffold/deploy/status_check_test.go @@ -343,11 +343,11 @@ func TestPrintSummaryStatus(t *testing.T) { rc := newCounter(10) rc.pending = test.pending event.InitializeState(latest.Pipeline{}, "test", true, true, true) - printStatusCheckSummary( - out, - withStatus(resource.NewDeployment(test.deployment, test.namespace, 0), test.ae), - *rc, - ) + r := withStatus(resource.NewDeployment(test.deployment, test.namespace, 0), test.ae) + // report status once and set it changed to false. + r.ReportSinceLastUpdated() + r.UpdateStatus(test.ae) + printStatusCheckSummary(out, r, *rc) t.CheckDeepEqual(test.expected, out.String()) }) } diff --git a/pkg/skaffold/runner/deploy.go b/pkg/skaffold/runner/deploy.go index 70465bccfea..5b9ab4144b4 100644 --- a/pkg/skaffold/runner/deploy.go +++ b/pkg/skaffold/runner/deploy.go @@ -139,7 +139,7 @@ func (r *SkaffoldRunner) performStatusCheck(ctx context.Context, out io.Writer) start := time.Now() color.Default.Fprintln(out, "Waiting for deployments to stabilize...") - err := statusCheck(ctx, r.labeller, r.runCtx, out) + err := statusCheck(ctx, r.labeller, r.runCtx, out, r.devIteration) if err != nil { return err } From 73d0030e5cf61f95c7bfd8670a049f69d8352520 Mon Sep 17 00:00:00 2001 From: David Gageot Date: Thu, 30 Jul 2020 18:03:27 +0200 Subject: [PATCH 021/138] Improve muted logs mechanism (#4594) * Rename suppress-logs to mute-logs Signed-off-by: David Gageot * Write build logs to a separate folder Signed-off-by: David Gageot * Introduce a Muted type to extract utility functions Signed-off-by: David Gageot --- cmd/skaffold/app/cmd/flags.go | 6 +- docs/content/en/docs/references/cli/_index.md | 20 ++--- pkg/skaffold/build/cluster/cluster.go | 2 +- pkg/skaffold/build/cluster/types.go | 5 +- pkg/skaffold/build/cluster/types_test.go | 2 + pkg/skaffold/build/gcb/cloud_build.go | 2 +- pkg/skaffold/build/gcb/types.go | 5 +- pkg/skaffold/build/local/local.go | 2 +- pkg/skaffold/build/local/local_test.go | 3 + pkg/skaffold/build/local/types.go | 5 +- pkg/skaffold/build/logfile.go | 12 +-- pkg/skaffold/build/logfile_test.go | 59 ++++++------- pkg/skaffold/config/options.go | 2 +- pkg/skaffold/config/types.go | 15 ++++ pkg/skaffold/config/types_test.go | 82 +++++++++++++++++++ pkg/skaffold/logfile/logfile.go | 16 ++-- pkg/skaffold/logfile/logfile_test.go | 13 ++- 17 files changed, 180 insertions(+), 71 deletions(-) diff --git a/cmd/skaffold/app/cmd/flags.go b/cmd/skaffold/app/cmd/flags.go index 2507fd4162b..575b910c880 100644 --- a/cmd/skaffold/app/cmd/flags.go +++ b/cmd/skaffold/app/cmd/flags.go @@ -367,9 +367,9 @@ var flagRegistry = []Flag{ DefinedOn: []string{"render"}, }, { - Name: "suppress-logs", - Usage: "Suppress logs for specified stages in pipeline (build, deploy, status-check, none, all)", - Value: &opts.SuppressLogs, + Name: "mute-logs", + Usage: "mute logs for specified stages in pipeline (build, deploy, status-check, none, all)", + Value: &opts.Muted.Phases, DefValue: []string{}, FlagAddMethod: "StringSliceVar", DefinedOn: []string{"dev", "run", "debug", "build", "deploy"}, diff --git a/docs/content/en/docs/references/cli/_index.md b/docs/content/en/docs/references/cli/_index.md index 10b10744603..76379ad6da9 100644 --- a/docs/content/en/docs/references/cli/_index.md +++ b/docs/content/en/docs/references/cli/_index.md @@ -141,6 +141,7 @@ Options: --insecure-registry=[]: Target registries for built images which are not secure --kube-context='': Deploy to this Kubernetes context --kubeconfig='': Path to the kubeconfig file to use for CLI requests. + --mute-logs=[]: mute logs for specified stages in pipeline (build, deploy, status-check, none, all) -n, --namespace='': Run deployments in the specified namespace -o, --output={{json .}}: Used in conjunction with --quiet flag. Format output with go-template. For full struct documentation, see https://godoc.org/github.com/GoogleContainerTools/skaffold/cmd/skaffold/app/flags#BuildOutput -p, --profile=[]: Activate profiles by name (prefixed with `-` to disable a profile) @@ -149,7 +150,6 @@ Options: --rpc-http-port=50052: tcp port to expose event REST API over HTTP --rpc-port=50051: tcp port to expose event API --skip-tests=false: Whether to skip the tests after building - --suppress-logs=[]: Suppress logs for specified stages in pipeline (build, deploy, status-check, none, all) -t, --tag='': The optional custom tag to use for images which overrides the current Tagger configuration --toot=false: Emit a terminal beep after the deploy is complete @@ -174,6 +174,7 @@ Env vars: * `SKAFFOLD_INSECURE_REGISTRY` (same as `--insecure-registry`) * `SKAFFOLD_KUBE_CONTEXT` (same as `--kube-context`) * `SKAFFOLD_KUBECONFIG` (same as `--kubeconfig`) +* `SKAFFOLD_MUTE_LOGS` (same as `--mute-logs`) * `SKAFFOLD_NAMESPACE` (same as `--namespace`) * `SKAFFOLD_OUTPUT` (same as `--output`) * `SKAFFOLD_PROFILE` (same as `--profile`) @@ -182,7 +183,6 @@ Env vars: * `SKAFFOLD_RPC_HTTP_PORT` (same as `--rpc-http-port`) * `SKAFFOLD_RPC_PORT` (same as `--rpc-port`) * `SKAFFOLD_SKIP_TESTS` (same as `--skip-tests`) -* `SKAFFOLD_SUPPRESS_LOGS` (same as `--suppress-logs`) * `SKAFFOLD_TAG` (same as `--tag`) * `SKAFFOLD_TOOT` (same as `--toot`) @@ -348,6 +348,7 @@ Options: --kube-context='': Deploy to this Kubernetes context --kubeconfig='': Path to the kubeconfig file to use for CLI requests. -l, --label=[]: Add custom labels to deployed objects. Set multiple times for multiple labels + --mute-logs=[]: mute logs for specified stages in pipeline (build, deploy, status-check, none, all) -n, --namespace='': Run deployments in the specified namespace --no-prune=false: Skip removing images and containers built by Skaffold --no-prune-children=false: Skip removing layers reused by Skaffold @@ -358,7 +359,6 @@ Options: --rpc-port=50051: tcp port to expose event API --skip-tests=false: Whether to skip the tests after building --status-check=true: Wait for deployed resources to stabilize - --suppress-logs=[]: Suppress logs for specified stages in pipeline (build, deploy, status-check, none, all) -t, --tag='': The optional custom tag to use for images which overrides the current Tagger configuration --tail=false: Stream logs from deployed objects (true by default for `skaffold dev` and `skaffold debug`) --toot=false: Emit a terminal beep after the deploy is complete @@ -390,6 +390,7 @@ Env vars: * `SKAFFOLD_KUBE_CONTEXT` (same as `--kube-context`) * `SKAFFOLD_KUBECONFIG` (same as `--kubeconfig`) * `SKAFFOLD_LABEL` (same as `--label`) +* `SKAFFOLD_MUTE_LOGS` (same as `--mute-logs`) * `SKAFFOLD_NAMESPACE` (same as `--namespace`) * `SKAFFOLD_NO_PRUNE` (same as `--no-prune`) * `SKAFFOLD_NO_PRUNE_CHILDREN` (same as `--no-prune-children`) @@ -400,7 +401,6 @@ Env vars: * `SKAFFOLD_RPC_PORT` (same as `--rpc-port`) * `SKAFFOLD_SKIP_TESTS` (same as `--skip-tests`) * `SKAFFOLD_STATUS_CHECK` (same as `--status-check`) -* `SKAFFOLD_SUPPRESS_LOGS` (same as `--suppress-logs`) * `SKAFFOLD_TAG` (same as `--tag`) * `SKAFFOLD_TAIL` (same as `--tail`) * `SKAFFOLD_TOOT` (same as `--toot`) @@ -477,6 +477,7 @@ Options: --kube-context='': Deploy to this Kubernetes context --kubeconfig='': Path to the kubeconfig file to use for CLI requests. -l, --label=[]: Add custom labels to deployed objects. Set multiple times for multiple labels + --mute-logs=[]: mute logs for specified stages in pipeline (build, deploy, status-check, none, all) -n, --namespace='': Run deployments in the specified namespace --port-forward=false: Port-forward exposed container ports within pods -p, --profile=[]: Activate profiles by name (prefixed with `-` to disable a profile) @@ -485,7 +486,6 @@ Options: --rpc-port=50051: tcp port to expose event API --skip-render=false: Don't render the manifests, just deploy them --status-check=true: Wait for deployed resources to stabilize - --suppress-logs=[]: Suppress logs for specified stages in pipeline (build, deploy, status-check, none, all) --tail=false: Stream logs from deployed objects (true by default for `skaffold dev` and `skaffold debug`) --toot=false: Emit a terminal beep after the deploy is complete --wait-for-deletions=true: Wait for pending deletions to complete before a deployment @@ -511,6 +511,7 @@ Env vars: * `SKAFFOLD_KUBE_CONTEXT` (same as `--kube-context`) * `SKAFFOLD_KUBECONFIG` (same as `--kubeconfig`) * `SKAFFOLD_LABEL` (same as `--label`) +* `SKAFFOLD_MUTE_LOGS` (same as `--mute-logs`) * `SKAFFOLD_NAMESPACE` (same as `--namespace`) * `SKAFFOLD_PORT_FORWARD` (same as `--port-forward`) * `SKAFFOLD_PROFILE` (same as `--profile`) @@ -519,7 +520,6 @@ Env vars: * `SKAFFOLD_RPC_PORT` (same as `--rpc-port`) * `SKAFFOLD_SKIP_RENDER` (same as `--skip-render`) * `SKAFFOLD_STATUS_CHECK` (same as `--status-check`) -* `SKAFFOLD_SUPPRESS_LOGS` (same as `--suppress-logs`) * `SKAFFOLD_TAIL` (same as `--tail`) * `SKAFFOLD_TOOT` (same as `--toot`) * `SKAFFOLD_WAIT_FOR_DELETIONS` (same as `--wait-for-deletions`) @@ -546,6 +546,7 @@ Options: --kube-context='': Deploy to this Kubernetes context --kubeconfig='': Path to the kubeconfig file to use for CLI requests. -l, --label=[]: Add custom labels to deployed objects. Set multiple times for multiple labels + --mute-logs=[]: mute logs for specified stages in pipeline (build, deploy, status-check, none, all) -n, --namespace='': Run deployments in the specified namespace --no-prune=false: Skip removing images and containers built by Skaffold --no-prune-children=false: Skip removing layers reused by Skaffold @@ -557,7 +558,6 @@ Options: --rpc-port=50051: tcp port to expose event API --skip-tests=false: Whether to skip the tests after building --status-check=true: Wait for deployed resources to stabilize - --suppress-logs=[]: Suppress logs for specified stages in pipeline (build, deploy, status-check, none, all) -t, --tag='': The optional custom tag to use for images which overrides the current Tagger configuration --tail=false: Stream logs from deployed objects (true by default for `skaffold dev` and `skaffold debug`) --toot=false: Emit a terminal beep after the deploy is complete @@ -589,6 +589,7 @@ Env vars: * `SKAFFOLD_KUBE_CONTEXT` (same as `--kube-context`) * `SKAFFOLD_KUBECONFIG` (same as `--kubeconfig`) * `SKAFFOLD_LABEL` (same as `--label`) +* `SKAFFOLD_MUTE_LOGS` (same as `--mute-logs`) * `SKAFFOLD_NAMESPACE` (same as `--namespace`) * `SKAFFOLD_NO_PRUNE` (same as `--no-prune`) * `SKAFFOLD_NO_PRUNE_CHILDREN` (same as `--no-prune-children`) @@ -600,7 +601,6 @@ Env vars: * `SKAFFOLD_RPC_PORT` (same as `--rpc-port`) * `SKAFFOLD_SKIP_TESTS` (same as `--skip-tests`) * `SKAFFOLD_STATUS_CHECK` (same as `--status-check`) -* `SKAFFOLD_SUPPRESS_LOGS` (same as `--suppress-logs`) * `SKAFFOLD_TAG` (same as `--tag`) * `SKAFFOLD_TAIL` (same as `--tail`) * `SKAFFOLD_TOOT` (same as `--toot`) @@ -804,6 +804,7 @@ Options: --kube-context='': Deploy to this Kubernetes context --kubeconfig='': Path to the kubeconfig file to use for CLI requests. -l, --label=[]: Add custom labels to deployed objects. Set multiple times for multiple labels + --mute-logs=[]: mute logs for specified stages in pipeline (build, deploy, status-check, none, all) -n, --namespace='': Run deployments in the specified namespace --no-prune=false: Skip removing images and containers built by Skaffold --no-prune-children=false: Skip removing layers reused by Skaffold @@ -816,7 +817,6 @@ Options: --rpc-port=50051: tcp port to expose event API --skip-tests=false: Whether to skip the tests after building --status-check=true: Wait for deployed resources to stabilize - --suppress-logs=[]: Suppress logs for specified stages in pipeline (build, deploy, status-check, none, all) -t, --tag='': The optional custom tag to use for images which overrides the current Tagger configuration --tail=false: Stream logs from deployed objects (true by default for `skaffold dev` and `skaffold debug`) --toot=false: Emit a terminal beep after the deploy is complete @@ -845,6 +845,7 @@ Env vars: * `SKAFFOLD_KUBE_CONTEXT` (same as `--kube-context`) * `SKAFFOLD_KUBECONFIG` (same as `--kubeconfig`) * `SKAFFOLD_LABEL` (same as `--label`) +* `SKAFFOLD_MUTE_LOGS` (same as `--mute-logs`) * `SKAFFOLD_NAMESPACE` (same as `--namespace`) * `SKAFFOLD_NO_PRUNE` (same as `--no-prune`) * `SKAFFOLD_NO_PRUNE_CHILDREN` (same as `--no-prune-children`) @@ -857,7 +858,6 @@ Env vars: * `SKAFFOLD_RPC_PORT` (same as `--rpc-port`) * `SKAFFOLD_SKIP_TESTS` (same as `--skip-tests`) * `SKAFFOLD_STATUS_CHECK` (same as `--status-check`) -* `SKAFFOLD_SUPPRESS_LOGS` (same as `--suppress-logs`) * `SKAFFOLD_TAG` (same as `--tag`) * `SKAFFOLD_TAIL` (same as `--tail`) * `SKAFFOLD_TOOT` (same as `--toot`) diff --git a/pkg/skaffold/build/cluster/cluster.go b/pkg/skaffold/build/cluster/cluster.go index db063d0dd2b..7e47da0c207 100644 --- a/pkg/skaffold/build/cluster/cluster.go +++ b/pkg/skaffold/build/cluster/cluster.go @@ -45,7 +45,7 @@ func (b *Builder) Build(ctx context.Context, out io.Writer, tags tag.ImageTags, defer teardownDockerConfigSecret() } - builder := build.WithLogFile(b.buildArtifact, b.suppressLogs) + builder := build.WithLogFile(b.buildArtifact, b.muted) return build.InParallel(ctx, out, tags, artifacts, builder, b.ClusterDetails.Concurrency) } diff --git a/pkg/skaffold/build/cluster/types.go b/pkg/skaffold/build/cluster/types.go index ebff7278953..3448b4be8d7 100644 --- a/pkg/skaffold/build/cluster/types.go +++ b/pkg/skaffold/build/cluster/types.go @@ -22,6 +22,7 @@ import ( "io" "time" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" @@ -35,7 +36,7 @@ type Builder struct { kubeContext string timeout time.Duration insecureRegistries map[string]bool - suppressLogs []string + muted build.Muted } // NewBuilder creates a new Builder that builds artifacts on cluster. @@ -51,7 +52,7 @@ func NewBuilder(runCtx *runcontext.RunContext) (*Builder, error) { timeout: timeout, kubeContext: runCtx.KubeContext, insecureRegistries: runCtx.InsecureRegistries, - suppressLogs: runCtx.Opts.SuppressLogs, + muted: runCtx.Opts.Muted, }, nil } diff --git a/pkg/skaffold/build/cluster/types_test.go b/pkg/skaffold/build/cluster/types_test.go index 14f2f85e6b8..e35f422985e 100644 --- a/pkg/skaffold/build/cluster/types_test.go +++ b/pkg/skaffold/build/cluster/types_test.go @@ -69,6 +69,7 @@ func TestNewBuilder(t *testing.T) { KubeContext: kubeContext, Namespace: namespace, }, + muted: config.Muted{}, }, }, { @@ -90,6 +91,7 @@ func TestNewBuilder(t *testing.T) { KubeContext: kubeContext, Namespace: namespace, }, + muted: config.Muted{}, }, }, } diff --git a/pkg/skaffold/build/gcb/cloud_build.go b/pkg/skaffold/build/gcb/cloud_build.go index 38cf5b20bdd..84f51c084e1 100644 --- a/pkg/skaffold/build/gcb/cloud_build.go +++ b/pkg/skaffold/build/gcb/cloud_build.go @@ -46,7 +46,7 @@ import ( // Build builds a list of artifacts with Google Cloud Build. func (b *Builder) Build(ctx context.Context, out io.Writer, tags tag.ImageTags, artifacts []*latest.Artifact) ([]build.Artifact, error) { - builder := build.WithLogFile(b.buildArtifactWithCloudBuild, b.suppressLogs) + builder := build.WithLogFile(b.buildArtifactWithCloudBuild, b.muted) return build.InParallel(ctx, out, tags, artifacts, builder, b.GoogleCloudBuild.Concurrency) } diff --git a/pkg/skaffold/build/gcb/types.go b/pkg/skaffold/build/gcb/types.go index 1539acc9abb..dea1a7eeb0d 100644 --- a/pkg/skaffold/build/gcb/types.go +++ b/pkg/skaffold/build/gcb/types.go @@ -23,6 +23,7 @@ import ( "k8s.io/apimachinery/pkg/util/wait" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" ) @@ -79,7 +80,7 @@ type Builder struct { *latest.GoogleCloudBuild skipTests bool insecureRegistries map[string]bool - suppressLogs []string + muted build.Muted } // NewBuilder creates a new Builder that builds artifacts with Google Cloud Build. @@ -88,7 +89,7 @@ func NewBuilder(runCtx *runcontext.RunContext) *Builder { GoogleCloudBuild: runCtx.Cfg.Build.GoogleCloudBuild, skipTests: runCtx.Opts.SkipTests, insecureRegistries: runCtx.InsecureRegistries, - suppressLogs: runCtx.Opts.SuppressLogs, + muted: runCtx.Opts.Muted, } } diff --git a/pkg/skaffold/build/local/local.go b/pkg/skaffold/build/local/local.go index 664ab396941..700cf66c9ab 100644 --- a/pkg/skaffold/build/local/local.go +++ b/pkg/skaffold/build/local/local.go @@ -42,7 +42,7 @@ func (b *Builder) Build(ctx context.Context, out io.Writer, tags tag.ImageTags, } defer b.localDocker.Close() - builder := build.WithLogFile(b.buildArtifact, b.suppressLogs) + builder := build.WithLogFile(b.buildArtifact, b.muted) return build.InParallel(ctx, out, tags, artifacts, builder, *b.cfg.Concurrency) } diff --git a/pkg/skaffold/build/local/local_test.go b/pkg/skaffold/build/local/local_test.go index 65b81829bb1..eb2aa159c5a 100644 --- a/pkg/skaffold/build/local/local_test.go +++ b/pkg/skaffold/build/local/local_test.go @@ -27,6 +27,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/tag" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" @@ -300,6 +301,7 @@ func TestNewBuilder(t *testing.T) { prune: true, pruneChildren: true, insecureRegistries: nil, + muted: config.Muted{}, }, }, { @@ -328,6 +330,7 @@ func TestNewBuilder(t *testing.T) { prune: true, pruneChildren: true, insecureRegistries: nil, + muted: config.Muted{}, }, }, } diff --git a/pkg/skaffold/build/local/types.go b/pkg/skaffold/build/local/types.go index b488f87a51b..ac1e6d09103 100644 --- a/pkg/skaffold/build/local/types.go +++ b/pkg/skaffold/build/local/types.go @@ -23,6 +23,7 @@ import ( "github.com/sirupsen/logrus" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" @@ -43,7 +44,7 @@ type Builder struct { kubeContext string builtImages []string insecureRegistries map[string]bool - suppressLogs []string + muted build.Muted } // external dependencies are wrapped @@ -86,7 +87,7 @@ func NewBuilder(runCtx *runcontext.RunContext) (*Builder, error) { prune: runCtx.Opts.Prune(), pruneChildren: !runCtx.Opts.NoPruneChildren, insecureRegistries: runCtx.InsecureRegistries, - suppressLogs: runCtx.Opts.SuppressLogs, + muted: runCtx.Opts.Muted, }, nil } diff --git a/pkg/skaffold/build/logfile.go b/pkg/skaffold/build/logfile.go index 9c9f048c683..12e861c2513 100644 --- a/pkg/skaffold/build/logfile.go +++ b/pkg/skaffold/build/logfile.go @@ -24,18 +24,20 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/logfile" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" ) +type Muted interface { + MuteBuild() bool +} + // WithLogFile wraps an `artifactBuilder` so that it optionally outputs its logs to a file. -func WithLogFile(builder ArtifactBuilder, suppressedLogs []string) ArtifactBuilder { - // TODO(dgageot): this should probably be moved somewhere else. - if !(util.StrSliceContains(suppressedLogs, "build") || util.StrSliceContains(suppressedLogs, "all")) { +func WithLogFile(builder ArtifactBuilder, muted Muted) ArtifactBuilder { + if !muted.MuteBuild() { return builder } return func(ctx context.Context, out io.Writer, artifact *latest.Artifact, tag string) (string, error) { - file, err := logfile.Create(artifact.ImageName + ".log") + file, err := logfile.Create("build", artifact.ImageName+".log") if err != nil { return "", fmt.Errorf("unable to create log file for %s: %w", artifact.ImageName, err) } diff --git a/pkg/skaffold/build/logfile_test.go b/pkg/skaffold/build/logfile_test.go index cc9dfd11a59..0f534d3e642 100644 --- a/pkg/skaffold/build/logfile_test.go +++ b/pkg/skaffold/build/logfile_test.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "io" + "os" "path/filepath" "strings" "testing" @@ -31,10 +32,14 @@ import ( ) func TestWithLogFile(t *testing.T) { + logBuildInProgress := "building img with tag img:123" + logBuildFailed := "failed to build img with tag img:123" + logFilename := " - writing logs to " + filepath.Join(os.TempDir(), "skaffold", "build", "img.log") + tests := []struct { description string builder ArtifactBuilder - suppress []string + muted Muted shouldErr bool expectedDigest string logsFound []string @@ -43,62 +48,44 @@ func TestWithLogFile(t *testing.T) { { description: "all logs", builder: fakeBuilder, - suppress: nil, - shouldErr: false, - expectedDigest: "digest", - logsFound: []string{"building img with tag img:123"}, - logsNotFound: []string{" - writing logs to ", filepath.Join("skaffold", "img.log")}, - }, - { - description: "suppress build logs", - builder: fakeBuilder, - suppress: []string{"build"}, - shouldErr: false, - expectedDigest: "digest", - logsFound: []string{" - writing logs to ", filepath.Join("skaffold", "img.log")}, - logsNotFound: []string{"building img with tag img:123"}, - }, - { - description: "suppress all logs", - builder: fakeBuilder, - suppress: []string{"all"}, + muted: muted(false), shouldErr: false, expectedDigest: "digest", - logsFound: []string{" - writing logs to ", filepath.Join("skaffold", "img.log")}, - logsNotFound: []string{"building img with tag img:123"}, + logsFound: []string{logBuildInProgress}, + logsNotFound: []string{logFilename}, }, { - description: "suppress only deploy logs", + description: "mute build logs", builder: fakeBuilder, - suppress: []string{"deploy"}, + muted: muted(true), shouldErr: false, expectedDigest: "digest", - logsFound: []string{"building img with tag img:123"}, - logsNotFound: []string{" - writing logs to ", filepath.Join("skaffold", "img.log")}, + logsFound: []string{logFilename}, + logsNotFound: []string{logBuildInProgress}, }, { description: "failed build - all logs", builder: fakeFailingBuilder, - suppress: nil, + muted: muted(false), shouldErr: true, expectedDigest: "", - logsFound: []string{"failed to build img with tag img:123"}, - logsNotFound: []string{" - writing logs to ", filepath.Join("skaffold", "img.log")}, + logsFound: []string{logBuildFailed}, + logsNotFound: []string{logFilename}, }, { - description: "failed build - suppressed logs", + description: "failed build - muted logs", builder: fakeFailingBuilder, - suppress: []string{"build"}, + muted: muted(true), shouldErr: true, expectedDigest: "", - logsFound: []string{" - writing logs to ", filepath.Join("skaffold", "img.log"), "failed to build img with tag img:123"}, + logsFound: []string{logFilename, logBuildFailed}, }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { var out bytes.Buffer - builder := WithLogFile(test.builder, test.suppress) + builder := WithLogFile(test.builder, test.muted) digest, err := builder(context.Background(), &out, &latest.Artifact{ImageName: "img"}, "img:123") t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expectedDigest, digest) @@ -121,3 +108,9 @@ func fakeFailingBuilder(_ context.Context, out io.Writer, a *latest.Artifact, ta fmt.Fprintln(out, "failed to build", a.ImageName, "with tag", tag) return "", errors.New("bug") } + +type muted bool + +func (m muted) MuteBuild() bool { + return bool(m) +} diff --git a/pkg/skaffold/config/options.go b/pkg/skaffold/config/options.go index 97b42d02154..4e05b41b24f 100644 --- a/pkg/skaffold/config/options.go +++ b/pkg/skaffold/config/options.go @@ -81,6 +81,7 @@ type SkaffoldOptions struct { TargetImages []string Profiles []string InsecureRegistries []string + Muted Muted Command string RPCPort int RPCHTTPPort int @@ -89,7 +90,6 @@ type SkaffoldOptions struct { // remove minikubeProfile from here and instead detect it by matching the // kubecontext API Server to minikube profiles MinikubeProfile string - SuppressLogs []string WaitForDeletions WaitForDeletions } diff --git a/pkg/skaffold/config/types.go b/pkg/skaffold/config/types.go index 039147f910f..1a27e908b28 100644 --- a/pkg/skaffold/config/types.go +++ b/pkg/skaffold/config/types.go @@ -16,6 +16,8 @@ limitations under the License. package config +import "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" + // StringOrUndefined holds the value of a flag of type `string`, // that's by default `undefined`. // We use this instead of just `string` to differentiate `undefined` @@ -43,3 +45,16 @@ func (s *StringOrUndefined) String() string { } return *s.value } + +// Muted lists phases for which logs are muted. +type Muted struct { + Phases []string +} + +func (m Muted) MuteBuild() bool { return m.mute("build") } +func (m Muted) MuteTest() bool { return m.mute("test") } +func (m Muted) MuteStatusCheck() bool { return m.mute("status-check") } +func (m Muted) MuteDeploy() bool { return m.mute("deploy") } +func (m Muted) mute(phase string) bool { + return util.StrSliceContains(m.Phases, phase) || util.StrSliceContains(m.Phases, "all") +} diff --git a/pkg/skaffold/config/types_test.go b/pkg/skaffold/config/types_test.go index 28eebd3b52c..a3e3da47cca 100644 --- a/pkg/skaffold/config/types_test.go +++ b/pkg/skaffold/config/types_test.go @@ -18,6 +18,7 @@ package config import ( "bytes" + "strings" "testing" "github.com/spf13/cobra" @@ -72,3 +73,84 @@ func TestStringOrUndefined(t *testing.T) { }) } } + +func TestMuted(t *testing.T) { + tests := []struct { + phases []string + expectedMuteBuild bool + expectedMuteTest bool + expectedMuteStatusCheck bool + expectedMuteDeploy bool + }{ + { + phases: nil, + expectedMuteBuild: false, + expectedMuteTest: false, + expectedMuteStatusCheck: false, + expectedMuteDeploy: false, + }, + { + phases: []string{"build"}, + expectedMuteBuild: true, + expectedMuteTest: false, + expectedMuteStatusCheck: false, + expectedMuteDeploy: false, + }, + { + phases: []string{"test"}, + expectedMuteBuild: false, + expectedMuteTest: true, + expectedMuteStatusCheck: false, + expectedMuteDeploy: false, + }, + { + phases: []string{"status-check"}, + expectedMuteBuild: false, + expectedMuteTest: false, + expectedMuteStatusCheck: true, + expectedMuteDeploy: false, + }, + { + phases: []string{"deploy"}, + expectedMuteBuild: false, + expectedMuteTest: false, + expectedMuteStatusCheck: false, + expectedMuteDeploy: true, + }, + { + phases: []string{"build", "test", "status-check", "deploy"}, + expectedMuteBuild: true, + expectedMuteTest: true, + expectedMuteStatusCheck: true, + expectedMuteDeploy: true, + }, + { + phases: []string{"all"}, + expectedMuteBuild: true, + expectedMuteTest: true, + expectedMuteStatusCheck: true, + expectedMuteDeploy: true, + }, + { + phases: []string{"none"}, + expectedMuteBuild: false, + expectedMuteTest: false, + expectedMuteStatusCheck: false, + expectedMuteDeploy: false, + }, + } + for _, test := range tests { + description := strings.Join(test.phases, ",") + + testutil.Run(t, description, func(t *testutil.T) { + m := Muted{ + Phases: test.phases, + } + + t.CheckDeepEqual(test.expectedMuteBuild, m.MuteBuild()) + t.CheckDeepEqual(test.expectedMuteTest, m.MuteTest()) + t.CheckDeepEqual(test.expectedMuteStatusCheck, m.MuteStatusCheck()) + t.CheckDeepEqual(test.expectedMuteDeploy, m.MuteDeploy()) + }) + } +} diff --git a/pkg/skaffold/logfile/logfile.go b/pkg/skaffold/logfile/logfile.go index 8e644e6049d..becd03e224c 100644 --- a/pkg/skaffold/logfile/logfile.go +++ b/pkg/skaffold/logfile/logfile.go @@ -24,14 +24,18 @@ import ( ) // Create creates or truncates a file to be used to output logs. -func Create(name string) (*os.File, error) { - root := filepath.Join(os.TempDir(), "skaffold") - if err := os.MkdirAll(root, 0700); err != nil { - return nil, fmt.Errorf("unable to create temp directory %q: %w", root, err) +func Create(path ...string) (*os.File, error) { + logfile := filepath.Join(os.TempDir(), "skaffold") + for _, p := range path { + logfile = filepath.Join(logfile, escape(p)) } - path := filepath.Join(root, escape(name)) - return os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) + dir := filepath.Dir(logfile) + if err := os.MkdirAll(dir, 0700); err != nil { + return nil, fmt.Errorf("unable to create temp directory %q: %w", dir, err) + } + + return os.OpenFile(logfile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) } var escapeRegexp = regexp.MustCompile(`[^a-zA-Z0-9-_.]`) diff --git a/pkg/skaffold/logfile/logfile_test.go b/pkg/skaffold/logfile/logfile_test.go index 32d731a5684..0b71f58ecc6 100644 --- a/pkg/skaffold/logfile/logfile_test.go +++ b/pkg/skaffold/logfile/logfile_test.go @@ -27,24 +27,29 @@ import ( func TestCreate(t *testing.T) { var tests = []struct { description string - name string + path []string expectedName string }{ { description: "create file", - name: "logs.txt", + path: []string{"logs.txt"}, expectedName: "logs.txt", }, + { + description: "create file in folder", + path: []string{"build", "logs.txt"}, + expectedName: filepath.Join("build", "logs.txt"), + }, { description: "escape name", - name: "a/name.txt", + path: []string{"a*name.txt"}, expectedName: "a-name.txt", }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - file, err := Create(test.name) + file, err := Create(test.path...) defer func() { file.Close() os.Remove(file.Name()) From 2bc007942ffbf998e11ecf658510be36decb377d Mon Sep 17 00:00:00 2001 From: David Gageot Date: Thu, 30 Jul 2020 18:38:19 +0200 Subject: [PATCH 022/138] Mute test phase (#4595) Signed-off-by: David Gageot --- pkg/skaffold/test/test.go | 37 ++++++++++++++++++++++++++++++++++ pkg/skaffold/test/test_test.go | 35 ++++++++++++++++++++++++++++++++ pkg/skaffold/test/types.go | 5 +++++ 3 files changed, 77 insertions(+) diff --git a/pkg/skaffold/test/test.go b/pkg/skaffold/test/test.go index 432ebf555c1..b487156b736 100644 --- a/pkg/skaffold/test/test.go +++ b/pkg/skaffold/test/test.go @@ -17,6 +17,7 @@ limitations under the License. package test import ( + "bytes" "context" "fmt" "io" @@ -24,7 +25,9 @@ import ( "github.com/sirupsen/logrus" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/color" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/logfile" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/test/structure" @@ -42,6 +45,7 @@ func NewTester(runCtx *runcontext.RunContext, imagesAreLocal bool) Tester { return FullTester{ testCases: runCtx.Cfg.Test, + muted: runCtx.Opts.Muted, workingDir: runCtx.WorkingDir, localDaemon: localDaemon, imagesAreLocal: imagesAreLocal, @@ -67,6 +71,39 @@ func (t FullTester) TestDependencies() ([]string, error) { // Test is the top level testing execution call. It serves as the // entrypoint to all individual tests. func (t FullTester) Test(ctx context.Context, out io.Writer, bRes []build.Artifact) error { + if len(t.testCases) == 0 { + return nil + } + + color.Default.Fprintln(out, "Testing images...") + + if t.muted.MuteTest() { + file, err := logfile.Create("test.log") + if err != nil { + return fmt.Errorf("unable to create log file for tests: %w", err) + } + fmt.Fprintln(out, " - writing logs to", file.Name()) + + // Print logs to a memory buffer and to a file. + var buf bytes.Buffer + w := io.MultiWriter(file, &buf) + + // Run the tests. + err = t.runTests(ctx, w, bRes) + + // After the test finish, close the log file. If the tests failed, print the full log to the console. + file.Close() + if err != nil { + buf.WriteTo(out) + } + + return err + } + + return t.runTests(ctx, out, bRes) +} + +func (t FullTester) runTests(ctx context.Context, out io.Writer, bRes []build.Artifact) error { for _, test := range t.testCases { if err := t.runStructureTests(ctx, out, bRes, test); err != nil { return fmt.Errorf("running structure tests: %w", err) diff --git a/pkg/skaffold/test/test_test.go b/pkg/skaffold/test/test_test.go index bf012faf879..d1be269dad5 100644 --- a/pkg/skaffold/test/test_test.go +++ b/pkg/skaffold/test/test_test.go @@ -17,12 +17,16 @@ limitations under the License. package test import ( + "bytes" "context" "errors" "io/ioutil" + "os" + "path/filepath" "testing" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" @@ -223,3 +227,34 @@ func TestTestFailure(t *testing.T) { t.CheckError(true, err) }) } + +func TestTestMuted(t *testing.T) { + testutil.Run(t, "", func(t *testutil.T) { + tmpDir := t.NewTempDir().Touch("test.yaml") + t.Override(&util.DefaultExecCommand, testutil.CmdRun("container-structure-test test -v warn --image image:tag --config "+tmpDir.Path("test.yaml"))) + + runCtx := &runcontext.RunContext{ + WorkingDir: tmpDir.Root(), + Cfg: latest.Pipeline{ + Test: []*latest.TestCase{{ + ImageName: "image", + StructureTests: []string{"test.yaml"}, + }}, + }, + Opts: config.SkaffoldOptions{ + Muted: config.Muted{ + Phases: []string{"test"}, + }, + }, + } + + var buf bytes.Buffer + err := NewTester(runCtx, true).Test(context.Background(), &buf, []build.Artifact{{ + ImageName: "image", + Tag: "image:tag", + }}) + + t.CheckNoError(err) + t.CheckContains("- writing logs to "+filepath.Join(os.TempDir(), "skaffold", "test.log"), buf.String()) + }) +} diff --git a/pkg/skaffold/test/types.go b/pkg/skaffold/test/types.go index 8fcf0ef3ac3..a1903ced2ec 100644 --- a/pkg/skaffold/test/types.go +++ b/pkg/skaffold/test/types.go @@ -35,6 +35,10 @@ type Tester interface { TestDependencies() ([]string, error) } +type Muted interface { + MuteTest() bool +} + // FullTester serves as a holder for the individual artifact-specific // testers. It exists so that the Tester interface can mimic the Builder/Deployer // interface, so it can be called in a similar fashion from the Runner, while @@ -45,6 +49,7 @@ type Tester interface { type FullTester struct { testCases []*latest.TestCase localDaemon docker.LocalDaemon + muted Muted workingDir string imagesAreLocal bool } From dedd545dbeff9f19ea18ad362b89309f21aa863d Mon Sep 17 00:00:00 2001 From: David Gageot Date: Thu, 30 Jul 2020 19:20:55 +0200 Subject: [PATCH 023/138] Use read-only getters on RunContext (#4587) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a first step towards refactoring the Runcontext. What’s nice about this step is that it’s not dangerous. Signed-off-by: David Gageot --- pkg/skaffold/build/cache/cache.go | 8 ++-- pkg/skaffold/build/cluster/types.go | 10 ++-- pkg/skaffold/build/gcb/types.go | 8 ++-- pkg/skaffold/build/local/types.go | 22 ++++----- pkg/skaffold/deploy/helm.go | 10 ++-- pkg/skaffold/deploy/kubectl.go | 14 +++--- pkg/skaffold/deploy/kubectl/cli.go | 4 +- pkg/skaffold/deploy/kustomize.go | 8 ++-- pkg/skaffold/deploy/status_check.go | 8 ++-- pkg/skaffold/diagnose/diagnose.go | 12 ++--- pkg/skaffold/docker/client.go | 4 +- .../generate_pipeline/generate_pipeline.go | 4 +- pkg/skaffold/generate_pipeline/profile.go | 2 +- pkg/skaffold/kubectl/cli.go | 6 +-- pkg/skaffold/runner/build_deploy.go | 10 ++-- pkg/skaffold/runner/debugging.go | 4 +- pkg/skaffold/runner/deploy.go | 14 +++--- pkg/skaffold/runner/deploy_test.go | 2 +- pkg/skaffold/runner/dev.go | 8 ++-- pkg/skaffold/runner/generate_pipeline.go | 2 +- pkg/skaffold/runner/logger.go | 4 +- pkg/skaffold/runner/new.go | 36 +++++++------- pkg/skaffold/runner/portforwarder.go | 6 +-- pkg/skaffold/runner/render.go | 6 +-- pkg/skaffold/runner/runcontext/context.go | 48 +++++++++++++++++-- pkg/skaffold/sync/types.go | 2 +- pkg/skaffold/test/test.go | 6 +-- pkg/skaffold/trigger/triggers.go | 16 +++---- 28 files changed, 164 insertions(+), 120 deletions(-) diff --git a/pkg/skaffold/build/cache/cache.go b/pkg/skaffold/build/cache/cache.go index 42973a9f682..4e67085c1ae 100644 --- a/pkg/skaffold/build/cache/cache.go +++ b/pkg/skaffold/build/cache/cache.go @@ -57,11 +57,11 @@ type DependencyLister func(ctx context.Context, artifact *latest.Artifact) ([]st // NewCache returns the current state of the cache func NewCache(runCtx *runcontext.RunContext, imagesAreLocal bool, dependencies DependencyLister) (Cache, error) { - if !runCtx.Opts.CacheArtifacts { + if !runCtx.CacheArtifacts() { return &noCache{}, nil } - cacheFile, err := resolveCacheFile(runCtx.Opts.CacheFile) + cacheFile, err := resolveCacheFile(runCtx.CacheFile()) if err != nil { logrus.Warnf("Error resolving cache file, not using skaffold cache: %v", err) return &noCache{}, nil @@ -81,11 +81,11 @@ func NewCache(runCtx *runcontext.RunContext, imagesAreLocal bool, dependencies D return &cache{ artifactCache: artifactCache, client: client, - insecureRegistries: runCtx.InsecureRegistries, + insecureRegistries: runCtx.GetInsecureRegistries(), cacheFile: cacheFile, imagesAreLocal: imagesAreLocal, hashForArtifact: func(ctx context.Context, a *latest.Artifact) (string, error) { - return getHashForArtifact(ctx, dependencies, a, runCtx.Opts.IsDevMode()) + return getHashForArtifact(ctx, dependencies, a, runCtx.DevMode()) }, }, nil } diff --git a/pkg/skaffold/build/cluster/types.go b/pkg/skaffold/build/cluster/types.go index 3448b4be8d7..4da8acb809f 100644 --- a/pkg/skaffold/build/cluster/types.go +++ b/pkg/skaffold/build/cluster/types.go @@ -41,18 +41,18 @@ type Builder struct { // NewBuilder creates a new Builder that builds artifacts on cluster. func NewBuilder(runCtx *runcontext.RunContext) (*Builder, error) { - timeout, err := time.ParseDuration(runCtx.Cfg.Build.Cluster.Timeout) + timeout, err := time.ParseDuration(runCtx.Pipeline().Build.Cluster.Timeout) if err != nil { return nil, fmt.Errorf("parsing timeout: %w", err) } return &Builder{ - ClusterDetails: runCtx.Cfg.Build.Cluster, + ClusterDetails: runCtx.Pipeline().Build.Cluster, kubectlcli: kubectl.NewFromRunContext(runCtx), timeout: timeout, - kubeContext: runCtx.KubeContext, - insecureRegistries: runCtx.InsecureRegistries, - muted: runCtx.Opts.Muted, + kubeContext: runCtx.GetKubeContext(), + insecureRegistries: runCtx.GetInsecureRegistries(), + muted: runCtx.Muted(), }, nil } diff --git a/pkg/skaffold/build/gcb/types.go b/pkg/skaffold/build/gcb/types.go index dea1a7eeb0d..96baa99bc18 100644 --- a/pkg/skaffold/build/gcb/types.go +++ b/pkg/skaffold/build/gcb/types.go @@ -86,10 +86,10 @@ type Builder struct { // NewBuilder creates a new Builder that builds artifacts with Google Cloud Build. func NewBuilder(runCtx *runcontext.RunContext) *Builder { return &Builder{ - GoogleCloudBuild: runCtx.Cfg.Build.GoogleCloudBuild, - skipTests: runCtx.Opts.SkipTests, - insecureRegistries: runCtx.InsecureRegistries, - muted: runCtx.Opts.Muted, + GoogleCloudBuild: runCtx.Pipeline().Build.GoogleCloudBuild, + skipTests: runCtx.SkipTests(), + insecureRegistries: runCtx.GetInsecureRegistries(), + muted: runCtx.Muted(), } } diff --git a/pkg/skaffold/build/local/types.go b/pkg/skaffold/build/local/types.go index ac1e6d09103..d5d72a114e2 100644 --- a/pkg/skaffold/build/local/types.go +++ b/pkg/skaffold/build/local/types.go @@ -63,31 +63,31 @@ func NewBuilder(runCtx *runcontext.RunContext) (*Builder, error) { // remove minikubeProfile from here and instead detect it by matching the // kubecontext API Server to minikube profiles - localCluster, err := getLocalCluster(runCtx.Opts.GlobalConfig, runCtx.Opts.MinikubeProfile) + localCluster, err := getLocalCluster(runCtx.GlobalConfig(), runCtx.MinikubeProfile()) if err != nil { return nil, fmt.Errorf("getting localCluster: %w", err) } var pushImages bool - if runCtx.Cfg.Build.LocalBuild.Push == nil { + if runCtx.Pipeline().Build.LocalBuild.Push == nil { pushImages = !localCluster logrus.Debugf("push value not present, defaulting to %t because localCluster is %t", pushImages, localCluster) } else { - pushImages = *runCtx.Cfg.Build.LocalBuild.Push + pushImages = *runCtx.Pipeline().Build.LocalBuild.Push } return &Builder{ - cfg: *runCtx.Cfg.Build.LocalBuild, - kubeContext: runCtx.KubeContext, + cfg: *runCtx.Pipeline().Build.LocalBuild, + kubeContext: runCtx.GetKubeContext(), localDocker: localDocker, localCluster: localCluster, pushImages: pushImages, - skipTests: runCtx.Opts.SkipTests, - devMode: runCtx.Opts.IsDevMode(), - prune: runCtx.Opts.Prune(), - pruneChildren: !runCtx.Opts.NoPruneChildren, - insecureRegistries: runCtx.InsecureRegistries, - muted: runCtx.Opts.Muted, + skipTests: runCtx.SkipTests(), + devMode: runCtx.DevMode(), + prune: runCtx.Prune(), + pruneChildren: !runCtx.NoPruneChildren(), + insecureRegistries: runCtx.GetInsecureRegistries(), + muted: runCtx.Muted(), }, nil } diff --git a/pkg/skaffold/deploy/helm.go b/pkg/skaffold/deploy/helm.go index 39f58ef2188..927efc30f14 100644 --- a/pkg/skaffold/deploy/helm.go +++ b/pkg/skaffold/deploy/helm.go @@ -82,11 +82,11 @@ type HelmDeployer struct { // NewHelmDeployer returns a configured HelmDeployer func NewHelmDeployer(runCtx *runcontext.RunContext, labels map[string]string) *HelmDeployer { return &HelmDeployer{ - HelmDeploy: runCtx.Cfg.Deploy.HelmDeploy, - kubeContext: runCtx.KubeContext, - kubeConfig: runCtx.Opts.KubeConfig, - namespace: runCtx.Opts.Namespace, - forceDeploy: runCtx.Opts.Force, + HelmDeploy: runCtx.Pipeline().Deploy.HelmDeploy, + kubeContext: runCtx.GetKubeContext(), + kubeConfig: runCtx.GetKubeConfig(), + namespace: runCtx.GetKubeNamespace(), + forceDeploy: runCtx.ForceDeploy(), labels: labels, } } diff --git a/pkg/skaffold/deploy/kubectl.go b/pkg/skaffold/deploy/kubectl.go index 9c3db5277cd..ae619ac24f1 100644 --- a/pkg/skaffold/deploy/kubectl.go +++ b/pkg/skaffold/deploy/kubectl.go @@ -58,13 +58,13 @@ type KubectlDeployer struct { // with the needed configuration for `kubectl apply` func NewKubectlDeployer(runCtx *runcontext.RunContext, labels map[string]string) *KubectlDeployer { return &KubectlDeployer{ - KubectlDeploy: runCtx.Cfg.Deploy.KubectlDeploy, - workingDir: runCtx.WorkingDir, - globalConfig: runCtx.Opts.GlobalConfig, - defaultRepo: runCtx.Opts.DefaultRepo.Value(), - kubectl: deploy.NewCLI(runCtx, runCtx.Cfg.Deploy.KubectlDeploy.Flags), - insecureRegistries: runCtx.InsecureRegistries, - skipRender: runCtx.Opts.SkipRender, + KubectlDeploy: runCtx.Pipeline().Deploy.KubectlDeploy, + workingDir: runCtx.GetWorkingDir(), + globalConfig: runCtx.GlobalConfig(), + defaultRepo: runCtx.DefaultRepo(), + kubectl: deploy.NewCLI(runCtx, runCtx.Pipeline().Deploy.KubectlDeploy.Flags), + insecureRegistries: runCtx.GetInsecureRegistries(), + skipRender: runCtx.SkipRender(), labels: labels, } } diff --git a/pkg/skaffold/deploy/kubectl/cli.go b/pkg/skaffold/deploy/kubectl/cli.go index 50a9bf5a90a..87d235771d6 100644 --- a/pkg/skaffold/deploy/kubectl/cli.go +++ b/pkg/skaffold/deploy/kubectl/cli.go @@ -46,8 +46,8 @@ func NewCLI(runCtx *runcontext.RunContext, flags latest.KubectlFlags) CLI { return CLI{ CLI: pkgkubectl.NewFromRunContext(runCtx), Flags: flags, - forceDeploy: runCtx.Opts.Force, - waitForDeletions: runCtx.Opts.WaitForDeletions, + forceDeploy: runCtx.ForceDeploy(), + waitForDeletions: runCtx.WaitForDeletions(), } } diff --git a/pkg/skaffold/deploy/kustomize.go b/pkg/skaffold/deploy/kustomize.go index 786af181873..d0d93c87b18 100644 --- a/pkg/skaffold/deploy/kustomize.go +++ b/pkg/skaffold/deploy/kustomize.go @@ -101,10 +101,10 @@ type KustomizeDeployer struct { func NewKustomizeDeployer(runCtx *runcontext.RunContext, labels map[string]string) *KustomizeDeployer { return &KustomizeDeployer{ - KustomizeDeploy: runCtx.Cfg.Deploy.KustomizeDeploy, - kubectl: deploy.NewCLI(runCtx, runCtx.Cfg.Deploy.KustomizeDeploy.Flags), - insecureRegistries: runCtx.InsecureRegistries, - globalConfig: runCtx.Opts.GlobalConfig, + KustomizeDeploy: runCtx.Pipeline().Deploy.KustomizeDeploy, + kubectl: deploy.NewCLI(runCtx, runCtx.Pipeline().Deploy.KustomizeDeploy.Flags), + insecureRegistries: runCtx.GetInsecureRegistries(), + globalConfig: runCtx.GlobalConfig(), labels: labels, } } diff --git a/pkg/skaffold/deploy/status_check.go b/pkg/skaffold/deploy/status_check.go index c5de81a0c01..45a1f792d96 100644 --- a/pkg/skaffold/deploy/status_check.go +++ b/pkg/skaffold/deploy/status_check.go @@ -77,20 +77,20 @@ func statusCheck(ctx context.Context, defaultLabeller *DefaultLabeller, runCtx * } deployContext = map[string]string{ - "clusterName": runCtx.KubeContext, + "clusterName": runCtx.GetKubeContext(), } deployments := make([]*resource.Deployment, 0) - for _, n := range runCtx.Namespaces { + for _, n := range runCtx.GetNamespaces() { newDeployments, err := getDeployments(client, n, defaultLabeller, - getDeadline(runCtx.Cfg.Deploy.StatusCheckDeadlineSeconds)) + getDeadline(runCtx.Pipeline().Deploy.StatusCheckDeadlineSeconds)) if err != nil { return proto.StatusCode_STATUSCHECK_DEPLOYMENT_FETCH_ERR, fmt.Errorf("could not fetch deployments: %w", err) } deployments = append(deployments, newDeployments...) } - deadline := statusCheckMaxDeadline(runCtx.Cfg.Deploy.StatusCheckDeadlineSeconds, deployments) + deadline := statusCheckMaxDeadline(runCtx.Pipeline().Deploy.StatusCheckDeadlineSeconds, deployments) var wg sync.WaitGroup diff --git a/pkg/skaffold/diagnose/diagnose.go b/pkg/skaffold/diagnose/diagnose.go index 93d6c472d87..761dbc49c34 100644 --- a/pkg/skaffold/diagnose/diagnose.go +++ b/pkg/skaffold/diagnose/diagnose.go @@ -33,11 +33,11 @@ import ( ) func CheckArtifacts(ctx context.Context, runCtx *runcontext.RunContext, out io.Writer) error { - for _, artifact := range runCtx.Cfg.Build.Artifacts { + for _, artifact := range runCtx.Pipeline().Build.Artifacts { color.Default.Fprintf(out, "\n%s: %s\n", typeOfArtifact(artifact), artifact.ImageName) if artifact.DockerArtifact != nil { - size, err := sizeOfDockerContext(ctx, artifact, runCtx.InsecureRegistries) + size, err := sizeOfDockerContext(ctx, artifact, runCtx.GetInsecureRegistries()) if err != nil { return fmt.Errorf("computing the size of the Docker context: %w", err) } @@ -45,11 +45,11 @@ func CheckArtifacts(ctx context.Context, runCtx *runcontext.RunContext, out io.W fmt.Fprintf(out, " - Size of the context: %vbytes\n", size) } - timeDeps1, deps, err := timeToListDependencies(ctx, artifact, runCtx.InsecureRegistries) + timeDeps1, deps, err := timeToListDependencies(ctx, artifact, runCtx.GetInsecureRegistries()) if err != nil { return fmt.Errorf("listing artifact dependencies: %w", err) } - timeDeps2, _, err := timeToListDependencies(ctx, artifact, runCtx.InsecureRegistries) + timeDeps2, _, err := timeToListDependencies(ctx, artifact, runCtx.GetInsecureRegistries()) if err != nil { return fmt.Errorf("listing artifact dependencies: %w", err) } @@ -57,13 +57,13 @@ func CheckArtifacts(ctx context.Context, runCtx *runcontext.RunContext, out io.W fmt.Fprintln(out, " - Dependencies:", len(deps), "files") fmt.Fprintf(out, " - Time to list dependencies: %v (2nd time: %v)\n", timeDeps1, timeDeps2) - timeSyncMap1, err := timeToConstructSyncMap(artifact, runCtx.InsecureRegistries) + timeSyncMap1, err := timeToConstructSyncMap(artifact, runCtx.GetInsecureRegistries()) if err != nil { if _, isNotSupported := err.(build.ErrSyncMapNotSupported); !isNotSupported { return fmt.Errorf("construct artifact dependencies: %w", err) } } - timeSyncMap2, err := timeToConstructSyncMap(artifact, runCtx.InsecureRegistries) + timeSyncMap2, err := timeToConstructSyncMap(artifact, runCtx.GetInsecureRegistries()) if err != nil { if _, isNotSupported := err.(build.ErrSyncMapNotSupported); !isNotSupported { return fmt.Errorf("construct artifact dependencies: %w", err) diff --git a/pkg/skaffold/docker/client.go b/pkg/skaffold/docker/client.go index 877a01caae8..b6a8d8f997b 100644 --- a/pkg/skaffold/docker/client.go +++ b/pkg/skaffold/docker/client.go @@ -56,8 +56,8 @@ var ( // NewAPIClientImpl guesses the docker client to use based on current Kubernetes context. func NewAPIClientImpl(runCtx *runcontext.RunContext) (LocalDaemon, error) { dockerAPIClientOnce.Do(func() { - env, apiClient, err := newAPIClient(runCtx.KubeContext, runCtx.Opts.MinikubeProfile) - dockerAPIClient = NewLocalDaemon(apiClient, env, runCtx.Opts.Prune(), runCtx.InsecureRegistries) + env, apiClient, err := newAPIClient(runCtx.GetKubeContext(), runCtx.MinikubeProfile()) + dockerAPIClient = NewLocalDaemon(apiClient, env, runCtx.Prune(), runCtx.GetInsecureRegistries()) dockerAPIClientErr = err }) diff --git a/pkg/skaffold/generate_pipeline/generate_pipeline.go b/pkg/skaffold/generate_pipeline/generate_pipeline.go index 504efd361b9..94d93142d71 100644 --- a/pkg/skaffold/generate_pipeline/generate_pipeline.go +++ b/pkg/skaffold/generate_pipeline/generate_pipeline.go @@ -50,14 +50,14 @@ func Yaml(out io.Writer, runCtx *runcontext.RunContext, configFiles []*ConfigFil // Generate build task for pipeline var tasks []*tekton.Task - buildTasks, err := generateBuildTasks(runCtx.Opts.Namespace, configFiles) + buildTasks, err := generateBuildTasks(runCtx.GetKubeNamespace(), configFiles) if err != nil { return nil, fmt.Errorf("generating build task: %w", err) } tasks = append(tasks, buildTasks...) // Generate deploy task for pipeline - deployTasks, err := generateDeployTasks(runCtx.Opts.Namespace, configFiles) + deployTasks, err := generateDeployTasks(runCtx.GetKubeNamespace(), configFiles) if err != nil { return nil, fmt.Errorf("generating deploy task: %w", err) } diff --git a/pkg/skaffold/generate_pipeline/profile.go b/pkg/skaffold/generate_pipeline/profile.go index 3a0d6200b4f..7fb11c4f50c 100644 --- a/pkg/skaffold/generate_pipeline/profile.go +++ b/pkg/skaffold/generate_pipeline/profile.go @@ -63,7 +63,7 @@ confirmLoop: } color.Default.Fprintln(out, "Creating skaffold profile \"oncluster\"...") - profile, err := generateProfile(out, runCtx.Opts.Namespace, configFile.Config) + profile, err := generateProfile(out, runCtx.GetKubeNamespace(), configFile.Config) if err != nil { return fmt.Errorf("generating profile \"oncluster\": %w", err) } diff --git a/pkg/skaffold/kubectl/cli.go b/pkg/skaffold/kubectl/cli.go index f344b3748e1..3eed7424fa5 100644 --- a/pkg/skaffold/kubectl/cli.go +++ b/pkg/skaffold/kubectl/cli.go @@ -38,9 +38,9 @@ type CLI struct { func NewFromRunContext(runCtx *runcontext.RunContext) *CLI { return &CLI{ - KubeContext: runCtx.KubeContext, - KubeConfig: runCtx.Opts.KubeConfig, - Namespace: runCtx.Opts.Namespace, + KubeContext: runCtx.GetKubeContext(), + KubeConfig: runCtx.GetKubeConfig(), + Namespace: runCtx.GetKubeNamespace(), } } diff --git a/pkg/skaffold/runner/build_deploy.go b/pkg/skaffold/runner/build_deploy.go index 061e5abc785..03a9d35bea3 100644 --- a/pkg/skaffold/runner/build_deploy.go +++ b/pkg/skaffold/runner/build_deploy.go @@ -34,7 +34,7 @@ import ( // BuildAndTest builds and tests a list of artifacts. func (r *SkaffoldRunner) BuildAndTest(ctx context.Context, out io.Writer, artifacts []*latest.Artifact) ([]build.Artifact, error) { // Use tags directly from the Kubernetes manifests. - if r.runCtx.Opts.DigestSource == noneDigestSource { + if r.runCtx.DigestSource() == noneDigestSource { return []build.Artifact{}, nil } @@ -44,7 +44,7 @@ func (r *SkaffoldRunner) BuildAndTest(ctx context.Context, out io.Writer, artifa } // In dry-run mode or with --digest-source set to 'remote', we don't build anything, just return the tag for each artifact. - if r.runCtx.Opts.DryRun || (r.runCtx.Opts.DigestSource == remoteDigestSource) { + if r.runCtx.DryRun() || (r.runCtx.DigestSource() == remoteDigestSource) { var bRes []build.Artifact for _, artifact := range artifacts { bRes = append(bRes, build.Artifact{ @@ -68,7 +68,7 @@ func (r *SkaffoldRunner) BuildAndTest(ctx context.Context, out io.Writer, artifa return nil, err } - if !r.runCtx.Opts.SkipTests { + if !r.runCtx.SkipTests() { if err = r.tester.Test(ctx, out, bRes); err != nil { return nil, err } @@ -117,7 +117,7 @@ func (r *SkaffoldRunner) DeployAndLog(ctx context.Context, out io.Writer, artifa return fmt.Errorf("starting logger: %w", err) } - if r.runCtx.Opts.Tail || r.runCtx.Opts.PortForward.Enabled { + if r.runCtx.Tail() || r.runCtx.PortForward() { color.Yellow.Fprintln(out, "Press Ctrl+C to exit") <-ctx.Done() } @@ -139,7 +139,7 @@ type tagErr struct { // ApplyDefaultRepo applies the default repo to a given image tag. func (r *SkaffoldRunner) ApplyDefaultRepo(tag string) (string, error) { - return deploy.ApplyDefaultRepo(r.runCtx.Opts.GlobalConfig, r.runCtx.Opts.DefaultRepo.Value(), tag) + return deploy.ApplyDefaultRepo(r.runCtx.GlobalConfig(), r.runCtx.DefaultRepo(), tag) } // imageTags generates tags for a list of artifacts diff --git a/pkg/skaffold/runner/debugging.go b/pkg/skaffold/runner/debugging.go index 76e969bd805..f8d309faf36 100644 --- a/pkg/skaffold/runner/debugging.go +++ b/pkg/skaffold/runner/debugging.go @@ -21,9 +21,9 @@ import ( ) func (r *SkaffoldRunner) createContainerManager() *debugging.ContainerManager { - if !r.runCtx.Opts.IsDebugMode() { + if !r.runCtx.DebugMode() { return nil } - return debugging.NewContainerManager(r.podSelector, r.runCtx.Namespaces) + return debugging.NewContainerManager(r.podSelector, r.runCtx.GetNamespaces()) } diff --git a/pkg/skaffold/runner/deploy.go b/pkg/skaffold/runner/deploy.go index 70465bccfea..a6093a52f8a 100644 --- a/pkg/skaffold/runner/deploy.go +++ b/pkg/skaffold/runner/deploy.go @@ -34,8 +34,8 @@ import ( ) func (r *SkaffoldRunner) Deploy(ctx context.Context, out io.Writer, artifacts []build.Artifact) error { - if r.runCtx.Opts.RenderOnly { - return r.Render(ctx, out, artifacts, false, r.runCtx.Opts.RenderOutput) + if r.runCtx.RenderOnly() { + return r.Render(ctx, out, artifacts, false, r.runCtx.RenderOutput()) } color.Default.Fprintln(out, "Tags used in deployment:") @@ -58,7 +58,7 @@ See https://skaffold.dev/docs/pipeline-stages/taggers/#how-tagging-works`) return fmt.Errorf("unable to connect to Kubernetes: %w", err) } - if config.IsImageLoadingRequired(r.runCtx.KubeContext) { + if config.IsImageLoadingRequired(r.runCtx.GetKubeContext()) { err := r.loadImagesIntoCluster(ctx, out, artifacts) if err != nil { return err @@ -84,7 +84,7 @@ func (r *SkaffoldRunner) loadImagesIntoCluster(ctx context.Context, out io.Write return err } - if config.IsKindCluster(r.runCtx.KubeContext) { + if config.IsKindCluster(r.runCtx.GetKubeContext()) { kindCluster := config.KindClusterName(currentContext.Cluster) // With `kind`, docker images have to be loaded with the `kind` CLI. @@ -93,7 +93,7 @@ func (r *SkaffoldRunner) loadImagesIntoCluster(ctx context.Context, out io.Write } } - if config.IsK3dCluster(r.runCtx.KubeContext) { + if config.IsK3dCluster(r.runCtx.GetKubeContext()) { k3dCluster := config.K3dClusterName(currentContext.Cluster) // With `k3d`, docker images have to be loaded with the `k3d` CLI. @@ -111,7 +111,7 @@ func (r *SkaffoldRunner) getCurrentContext() (*api.Context, error) { return nil, fmt.Errorf("unable to get kubernetes config: %w", err) } - currentContext, present := currentCfg.Contexts[r.runCtx.KubeContext] + currentContext, present := currentCfg.Contexts[r.runCtx.GetKubeContext()] if !present { return nil, fmt.Errorf("unable to get current kubernetes context: %w", err) } @@ -132,7 +132,7 @@ func failIfClusterIsNotReachable() error { func (r *SkaffoldRunner) performStatusCheck(ctx context.Context, out io.Writer) error { // Check if we need to perform deploy status - if !r.runCtx.Opts.StatusCheck { + if !r.runCtx.StatusCheck() { return nil } diff --git a/pkg/skaffold/runner/deploy_test.go b/pkg/skaffold/runner/deploy_test.go index b2395bf7399..673d620ec77 100644 --- a/pkg/skaffold/runner/deploy_test.go +++ b/pkg/skaffold/runner/deploy_test.go @@ -131,7 +131,7 @@ func TestDeployNamespace(t *testing.T) { {ImageName: "img2", Tag: "img2:tag2"}, }) - t.CheckDeepEqual(test.expected, runner.runCtx.Namespaces) + t.CheckDeepEqual(test.expected, runner.runCtx.GetNamespaces()) }) } } diff --git a/pkg/skaffold/runner/dev.go b/pkg/skaffold/runner/dev.go index 2d8a722a8b9..e0a906b2366 100644 --- a/pkg/skaffold/runner/dev.go +++ b/pkg/skaffold/runner/dev.go @@ -148,10 +148,10 @@ func (r *SkaffoldRunner) Dev(ctx context.Context, out io.Writer, artifacts []*la default: if err := r.monitor.Register( func() ([]string, error) { - return build.DependenciesForArtifact(ctx, artifact, r.runCtx.InsecureRegistries) + return build.DependenciesForArtifact(ctx, artifact, r.runCtx.GetInsecureRegistries()) }, func(e filemon.Events) { - s, err := sync.NewItem(ctx, artifact, e, r.builds, r.runCtx.InsecureRegistries) + s, err := sync.NewItem(ctx, artifact, e, r.builds, r.runCtx.GetInsecureRegistries()) switch { case err != nil: logrus.Warnf("error adding dirty artifact to changeset: %s", err.Error()) @@ -188,11 +188,11 @@ func (r *SkaffoldRunner) Dev(ctx context.Context, out io.Writer, artifacts []*la // Watch Skaffold configuration if err := r.monitor.Register( - func() ([]string, error) { return []string{r.runCtx.Opts.ConfigurationFile}, nil }, + func() ([]string, error) { return []string{r.runCtx.ConfigurationFile()}, nil }, func(filemon.Events) { r.changeSet.needsReload = true }, ); err != nil { event.DevLoopFailedWithErrorCode(r.devIteration, proto.StatusCode_DEVINIT_REGISTER_CONFIG_DEP, err) - return fmt.Errorf("watching skaffold configuration %q: %w", r.runCtx.Opts.ConfigurationFile, err) + return fmt.Errorf("watching skaffold configuration %q: %w", r.runCtx.ConfigurationFile(), err) } logrus.Infoln("List generated in", time.Since(start)) diff --git a/pkg/skaffold/runner/generate_pipeline.go b/pkg/skaffold/runner/generate_pipeline.go index f2bfdd5b03f..97dbd4682a4 100644 --- a/pkg/skaffold/runner/generate_pipeline.go +++ b/pkg/skaffold/runner/generate_pipeline.go @@ -34,7 +34,7 @@ func (r *SkaffoldRunner) GeneratePipeline(ctx context.Context, out io.Writer, co // profiles to and what flags to add to task commands baseConfig := []*pipeline.ConfigFile{ { - Path: r.runCtx.Opts.ConfigurationFile, + Path: r.runCtx.ConfigurationFile(), Config: config, Profile: nil, }, diff --git a/pkg/skaffold/runner/logger.go b/pkg/skaffold/runner/logger.go index f9e86994598..716a661a120 100644 --- a/pkg/skaffold/runner/logger.go +++ b/pkg/skaffold/runner/logger.go @@ -24,7 +24,7 @@ import ( ) func (r *SkaffoldRunner) createLogger(out io.Writer, artifacts []build.Artifact) *kubernetes.LogAggregator { - if !r.runCtx.Opts.Tail { + if !r.runCtx.Tail() { return nil } @@ -33,5 +33,5 @@ func (r *SkaffoldRunner) createLogger(out io.Writer, artifacts []build.Artifact) imageNames = append(imageNames, artifact.Tag) } - return kubernetes.NewLogAggregator(out, r.kubectlCLI, imageNames, r.podSelector, r.runCtx.Namespaces, r.runCtx.Cfg.Deploy.Logs) + return kubernetes.NewLogAggregator(out, r.kubectlCLI, imageNames, r.podSelector, r.runCtx.GetNamespaces(), r.runCtx.Pipeline().Deploy.Logs) } diff --git a/pkg/skaffold/runner/new.go b/pkg/skaffold/runner/new.go index 2c336917f74..4a25cb98a5b 100644 --- a/pkg/skaffold/runner/new.go +++ b/pkg/skaffold/runner/new.go @@ -55,13 +55,13 @@ func NewForConfig(runCtx *runcontext.RunContext) (*SkaffoldRunner, error) { return nil, fmt.Errorf("creating builder: %w", err) } - labeller := deploy.NewLabeller(runCtx.Opts.AddSkaffoldLabels, runCtx.Opts.CustomLabels) + labeller := deploy.NewLabeller(runCtx.AddSkaffoldLabels(), runCtx.CustomLabels()) tester := getTester(runCtx, imagesAreLocal) syncer := getSyncer(runCtx) deployer := getDeployer(runCtx, labeller.Labels()) depLister := func(ctx context.Context, artifact *latest.Artifact) ([]string, error) { - buildDependencies, err := build.DependenciesForArtifact(ctx, artifact, runCtx.InsecureRegistries) + buildDependencies, err := build.DependenciesForArtifact(ctx, artifact, runCtx.GetInsecureRegistries()) if err != nil { return nil, err } @@ -79,8 +79,8 @@ func NewForConfig(runCtx *runcontext.RunContext) (*SkaffoldRunner, error) { return nil, fmt.Errorf("initializing cache: %w", err) } - builder, tester, deployer = WithTimings(builder, tester, deployer, runCtx.Opts.CacheArtifacts) - if runCtx.Opts.Notification { + builder, tester, deployer = WithTimings(builder, tester, deployer, runCtx.CacheArtifacts()) + if runCtx.Notification() { deployer = WithNotification(deployer) } @@ -89,7 +89,7 @@ func NewForConfig(runCtx *runcontext.RunContext) (*SkaffoldRunner, error) { return nil, fmt.Errorf("creating watch trigger: %w", err) } - event.InitializeState(runCtx.Cfg, runCtx.KubeContext, runCtx.Opts.AutoBuild, runCtx.Opts.AutoDeploy, runCtx.Opts.AutoSync) + event.InitializeState(runCtx.Pipeline(), runCtx.GetKubeContext(), runCtx.AutoBuild(), runCtx.AutoDeploy(), runCtx.AutoSync()) event.LogMetaEvent() monitor := filemon.NewMonitor() @@ -118,7 +118,7 @@ func NewForConfig(runCtx *runcontext.RunContext) (*SkaffoldRunner, error) { } func setupIntents(runCtx *runcontext.RunContext) (*intents, chan bool) { - intents := newIntents(runCtx.Opts.AutoBuild, runCtx.Opts.AutoSync, runCtx.Opts.AutoDeploy) + intents := newIntents(runCtx.AutoBuild(), runCtx.AutoSync(), runCtx.AutoDeploy()) intentChan := make(chan bool, 1) setupTrigger("build", intents.setBuild, intents.setAutoBuild, intents.getAutoBuild, server.SetBuildCallback, server.SetAutoBuildCallback, intentChan) @@ -155,8 +155,10 @@ func setupTrigger(triggerName string, setIntent func(bool), setAutoTrigger func( // Returns that builder, a bool to indicate that images are local // (ie don't need to be pushed) and an error. func getBuilder(runCtx *runcontext.RunContext) (build.Builder, bool, error) { + b := runCtx.Pipeline().Build + switch { - case runCtx.Cfg.Build.LocalBuild != nil: + case b.LocalBuild != nil: logrus.Debugln("Using builder: local") builder, err := local.NewBuilder(runCtx) if err != nil { @@ -164,17 +166,17 @@ func getBuilder(runCtx *runcontext.RunContext) (build.Builder, bool, error) { } return builder, !builder.PushImages(), nil - case runCtx.Cfg.Build.GoogleCloudBuild != nil: + case b.GoogleCloudBuild != nil: logrus.Debugln("Using builder: google cloud") return gcb.NewBuilder(runCtx), false, nil - case runCtx.Cfg.Build.Cluster != nil: + case b.Cluster != nil: logrus.Debugln("Using builder: cluster") builder, err := cluster.NewBuilder(runCtx) return builder, false, err default: - return nil, false, fmt.Errorf("unknown builder for config %+v", runCtx.Cfg.Build) + return nil, false, fmt.Errorf("unknown builder for config %+v", b) } } @@ -187,17 +189,19 @@ func getSyncer(runCtx *runcontext.RunContext) sync.Syncer { } func getDeployer(runCtx *runcontext.RunContext, labels map[string]string) deploy.Deployer { + d := runCtx.Pipeline().Deploy + var deployers deploy.DeployerMux - if runCtx.Cfg.Deploy.HelmDeploy != nil { + if d.HelmDeploy != nil { deployers = append(deployers, deploy.NewHelmDeployer(runCtx, labels)) } - if runCtx.Cfg.Deploy.KubectlDeploy != nil { + if d.KubectlDeploy != nil { deployers = append(deployers, deploy.NewKubectlDeployer(runCtx, labels)) } - if runCtx.Cfg.Deploy.KustomizeDeploy != nil { + if d.KustomizeDeploy != nil { deployers = append(deployers, deploy.NewKustomizeDeployer(runCtx, labels)) } @@ -210,12 +214,12 @@ func getDeployer(runCtx *runcontext.RunContext, labels map[string]string) deploy } func getTagger(runCtx *runcontext.RunContext) (tag.Tagger, error) { - t := runCtx.Cfg.Build.TagPolicy + t := runCtx.Pipeline().Build.TagPolicy switch { - case runCtx.Opts.CustomTag != "": + case runCtx.CustomTag() != "": return &tag.CustomTag{ - Tag: runCtx.Opts.CustomTag, + Tag: runCtx.CustomTag(), }, nil case t.EnvTemplateTagger != nil: diff --git a/pkg/skaffold/runner/portforwarder.go b/pkg/skaffold/runner/portforwarder.go index 939fb096c2c..c6fd637b389 100644 --- a/pkg/skaffold/runner/portforwarder.go +++ b/pkg/skaffold/runner/portforwarder.go @@ -23,15 +23,15 @@ import ( ) func (r *SkaffoldRunner) createForwarder(out io.Writer) *portforward.ForwarderManager { - if !r.runCtx.Opts.PortForward.Enabled { + if !r.runCtx.PortForward() { return nil } return portforward.NewForwarderManager(out, r.kubectlCLI, r.podSelector, - r.runCtx.Namespaces, + r.runCtx.GetNamespaces(), r.labeller.RunIDSelector(), r.runCtx.Opts.PortForward, - r.runCtx.Cfg.PortForward) + r.runCtx.Pipeline().PortForward) } diff --git a/pkg/skaffold/runner/render.go b/pkg/skaffold/runner/render.go index 294bcb6aeea..7e16dd9cc64 100644 --- a/pkg/skaffold/runner/render.go +++ b/pkg/skaffold/runner/render.go @@ -28,16 +28,16 @@ import ( func (r *SkaffoldRunner) Render(ctx context.Context, out io.Writer, builds []build.Artifact, offline bool, filepath string) error { //Fetch the digest and append it to the tag with the format of "tag@digest" - if r.runCtx.Opts.DigestSource == remoteDigestSource { + if r.runCtx.DigestSource() == remoteDigestSource { for i, a := range builds { - digest, err := docker.RemoteDigest(a.Tag, r.runCtx.InsecureRegistries) + digest, err := docker.RemoteDigest(a.Tag, r.runCtx.GetInsecureRegistries()) if err != nil { return fmt.Errorf("failed to resolve the digest of %s, render aborted", a.Tag) } builds[i].Tag = build.TagWithDigest(a.Tag, digest) } } - if r.runCtx.Opts.DigestSource == noneDigestSource { + if r.runCtx.DigestSource() == noneDigestSource { color.Default.Fprintln(out, "--digest-source set to 'none', tags listed in Kubernetes manifests will be used for render") } return r.deployer.Render(ctx, out, builds, offline, filepath) diff --git a/pkg/skaffold/runner/runcontext/context.go b/pkg/skaffold/runner/runcontext/context.go index 18631d8ff88..c96416d92b7 100644 --- a/pkg/skaffold/runner/runcontext/context.go +++ b/pkg/skaffold/runner/runcontext/context.go @@ -34,11 +34,51 @@ type RunContext struct { Cfg latest.Pipeline KubeContext string - WorkingDir string Namespaces []string + WorkingDir string InsecureRegistries map[string]bool } +func (rc *RunContext) GetKubeContext() string { return rc.KubeContext } +func (rc *RunContext) GetNamespaces() []string { return rc.Namespaces } +func (rc *RunContext) Pipeline() latest.Pipeline { return rc.Cfg } +func (rc *RunContext) GetInsecureRegistries() map[string]bool { return rc.InsecureRegistries } +func (rc *RunContext) GetWorkingDir() string { return rc.WorkingDir } + +func (rc *RunContext) AddSkaffoldLabels() bool { return rc.Opts.AddSkaffoldLabels } +func (rc *RunContext) AutoBuild() bool { return rc.Opts.AutoBuild } +func (rc *RunContext) AutoDeploy() bool { return rc.Opts.AutoDeploy } +func (rc *RunContext) AutoSync() bool { return rc.Opts.AutoSync } +func (rc *RunContext) CacheArtifacts() bool { return rc.Opts.CacheArtifacts } +func (rc *RunContext) CacheFile() string { return rc.Opts.CacheFile } +func (rc *RunContext) ConfigurationFile() string { return rc.Opts.ConfigurationFile } +func (rc *RunContext) CustomLabels() []string { return rc.Opts.CustomLabels } +func (rc *RunContext) CustomTag() string { return rc.Opts.CustomTag } +func (rc *RunContext) DebugMode() bool { return rc.Opts.IsDebugMode() } +func (rc *RunContext) DefaultRepo() *string { return rc.Opts.DefaultRepo.Value() } +func (rc *RunContext) DevMode() bool { return rc.Opts.IsDevMode() } +func (rc *RunContext) DigestSource() string { return rc.Opts.DigestSource } +func (rc *RunContext) DryRun() bool { return rc.Opts.DryRun } +func (rc *RunContext) ForceDeploy() bool { return rc.Opts.Force } +func (rc *RunContext) GetKubeConfig() string { return rc.Opts.KubeConfig } +func (rc *RunContext) GetKubeNamespace() string { return rc.Opts.Namespace } +func (rc *RunContext) GlobalConfig() string { return rc.Opts.GlobalConfig } +func (rc *RunContext) MinikubeProfile() string { return rc.Opts.MinikubeProfile } +func (rc *RunContext) Muted() config.Muted { return rc.Opts.Muted } +func (rc *RunContext) NoPruneChildren() bool { return rc.Opts.NoPruneChildren } +func (rc *RunContext) Notification() bool { return rc.Opts.Notification } +func (rc *RunContext) PortForward() bool { return rc.Opts.PortForward.Enabled } +func (rc *RunContext) Prune() bool { return rc.Opts.Prune() } +func (rc *RunContext) RenderOnly() bool { return rc.Opts.RenderOnly } +func (rc *RunContext) RenderOutput() string { return rc.Opts.RenderOutput } +func (rc *RunContext) SkipRender() bool { return rc.Opts.SkipRender } +func (rc *RunContext) SkipTests() bool { return rc.Opts.SkipTests } +func (rc *RunContext) StatusCheck() bool { return rc.Opts.StatusCheck } +func (rc *RunContext) Tail() bool { return rc.Opts.Tail } +func (rc *RunContext) Trigger() string { return rc.Opts.Trigger } +func (rc *RunContext) WaitForDeletions() config.WaitForDeletions { return rc.Opts.WaitForDeletions } +func (rc *RunContext) WatchPollInterval() int { return rc.Opts.WatchPollInterval } + func GetRunContext(opts config.SkaffoldOptions, cfg latest.Pipeline) (*RunContext, error) { kubeConfig, err := kubectx.CurrentConfig() if err != nil { @@ -80,13 +120,13 @@ func GetRunContext(opts config.SkaffoldOptions, cfg latest.Pipeline) (*RunContex }, nil } -func (r *RunContext) UpdateNamespaces(ns []string) { +func (rc *RunContext) UpdateNamespaces(ns []string) { if len(ns) == 0 { return } nsMap := map[string]bool{} - for _, ns := range append(ns, r.Namespaces...) { + for _, ns := range append(ns, rc.Namespaces...) { nsMap[ns] = true } @@ -96,5 +136,5 @@ func (r *RunContext) UpdateNamespaces(ns []string) { updated = append(updated, k) } sort.Strings(updated) - r.Namespaces = updated + rc.Namespaces = updated } diff --git a/pkg/skaffold/sync/types.go b/pkg/skaffold/sync/types.go index 1f462de8928..219399dbd7f 100644 --- a/pkg/skaffold/sync/types.go +++ b/pkg/skaffold/sync/types.go @@ -43,6 +43,6 @@ type podSyncer struct { func NewSyncer(runCtx *runcontext.RunContext) Syncer { return &podSyncer{ kubectl: kubectl.NewFromRunContext(runCtx), - namespaces: runCtx.Namespaces, + namespaces: runCtx.GetNamespaces(), } } diff --git a/pkg/skaffold/test/test.go b/pkg/skaffold/test/test.go index b487156b736..511404a48dc 100644 --- a/pkg/skaffold/test/test.go +++ b/pkg/skaffold/test/test.go @@ -44,9 +44,9 @@ func NewTester(runCtx *runcontext.RunContext, imagesAreLocal bool) Tester { } return FullTester{ - testCases: runCtx.Cfg.Test, - muted: runCtx.Opts.Muted, - workingDir: runCtx.WorkingDir, + testCases: runCtx.Pipeline().Test, + workingDir: runCtx.GetWorkingDir(), + muted: runCtx.Muted(), localDaemon: localDaemon, imagesAreLocal: imagesAreLocal, } diff --git a/pkg/skaffold/trigger/triggers.go b/pkg/skaffold/trigger/triggers.go index 1659417d6d1..9c9cb6eea25 100644 --- a/pkg/skaffold/trigger/triggers.go +++ b/pkg/skaffold/trigger/triggers.go @@ -42,28 +42,28 @@ type Trigger interface { } // NewTrigger creates a new trigger. -func NewTrigger(runctx *runcontext.RunContext) (Trigger, error) { - switch strings.ToLower(runctx.Opts.Trigger) { +func NewTrigger(runCtx *runcontext.RunContext) (Trigger, error) { + switch strings.ToLower(runCtx.Trigger()) { case "polling": return &pollTrigger{ - Interval: time.Duration(runctx.Opts.WatchPollInterval) * time.Millisecond, + Interval: time.Duration(runCtx.WatchPollInterval()) * time.Millisecond, }, nil case "notify": - return newFSNotifyTrigger(runctx), nil + return newFSNotifyTrigger(runCtx), nil case "manual": return &manualTrigger{}, nil default: - return nil, fmt.Errorf("unsupported trigger: %s", runctx.Opts.Trigger) + return nil, fmt.Errorf("unsupported trigger: %s", runCtx.Trigger()) } } -func newFSNotifyTrigger(runctx *runcontext.RunContext) *fsNotifyTrigger { +func newFSNotifyTrigger(runCtx *runcontext.RunContext) *fsNotifyTrigger { workspaces := map[string]struct{}{} - for _, a := range runctx.Cfg.Build.Artifacts { + for _, a := range runCtx.Pipeline().Build.Artifacts { workspaces[a.Workspace] = struct{}{} } return &fsNotifyTrigger{ - Interval: time.Duration(runctx.Opts.WatchPollInterval) * time.Millisecond, + Interval: time.Duration(runCtx.WatchPollInterval()) * time.Millisecond, workspaces: workspaces, } } From d78813387d0d4ea0a3658eba3ec0e100be37ccfa Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Thu, 30 Jul 2020 14:26:27 -0400 Subject: [PATCH 024/138] Update docs/content/en/docs/pipeline-stages/taggers.md Co-authored-by: Nick Kubala --- docs/content/en/docs/pipeline-stages/taggers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/en/docs/pipeline-stages/taggers.md b/docs/content/en/docs/pipeline-stages/taggers.md index 4578caa0fbc..4089e395ae6 100644 --- a/docs/content/en/docs/pipeline-stages/taggers.md +++ b/docs/content/en/docs/pipeline-stages/taggers.md @@ -12,7 +12,7 @@ Skaffold supports multiple taggers or tag policies for tagging images: + the `sha256` tagger uses `latest` to tag images. + the `envTemplate` tagger uses environment variables to tag images. + the `datetime` tagger uses current date and time, with a configurable pattern. - + the `tagTemplate` tagger uses a combination of the previous taggers to tag images. + + the `tagTemplate` tagger uses a combination of the existing taggers as components in a template. The default tagger, if none is specified in the `skaffold.yaml`, is the `gitCommit` tagger. @@ -176,4 +176,4 @@ Suppose current time is `15:04:09.999 January 2nd, 2006` and the abbreviated com The tag template uses the [Go Programming Language Syntax](https://golang.org/pkg/text/template/). As showcased in the example, `tagTemplate` tag policy features one -**required** parameter, `template`, which is the tag template to use. To learn more about templating support in Skaffold.yaml see [Templated fields]({{< relref "../environment/templating.md" >}}) \ No newline at end of file +**required** parameter, `template`, which is the tag template to use. To learn more about templating support in Skaffold.yaml see [Templated fields]({{< relref "../environment/templating.md" >}}) From 227177d171545ef806d03e489e239d04ceac9f8f Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Thu, 30 Jul 2020 14:26:37 -0400 Subject: [PATCH 025/138] Update docs/content/en/docs/pipeline-stages/taggers.md Co-authored-by: Nick Kubala --- docs/content/en/docs/pipeline-stages/taggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/en/docs/pipeline-stages/taggers.md b/docs/content/en/docs/pipeline-stages/taggers.md index 4089e395ae6..a9b59647c9f 100644 --- a/docs/content/en/docs/pipeline-stages/taggers.md +++ b/docs/content/en/docs/pipeline-stages/taggers.md @@ -145,7 +145,7 @@ You can learn more about what time format and time zone you can use in example, `dateTime` tag policy features two optional parameters: `format` and `timezone`. -## `tagTemplate`: uses a combination of the previous taggers as tags +## `tagTemplate`: uses a combination of the existing taggers as components in a template `tagTemplate` allows you to combine all previous taggers to create your own tagging policy. This policy requires that you specify a tag template, From 5d09d9255eaa2a8812d3ef3769446c63b4dcefce Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Thu, 30 Jul 2020 14:26:44 -0400 Subject: [PATCH 026/138] Update docs/content/en/docs/pipeline-stages/taggers.md Co-authored-by: Nick Kubala --- docs/content/en/docs/pipeline-stages/taggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/en/docs/pipeline-stages/taggers.md b/docs/content/en/docs/pipeline-stages/taggers.md index a9b59647c9f..33a2b71368a 100644 --- a/docs/content/en/docs/pipeline-stages/taggers.md +++ b/docs/content/en/docs/pipeline-stages/taggers.md @@ -147,7 +147,7 @@ tag policy features two optional parameters: `format` and `timezone`. ## `tagTemplate`: uses a combination of the existing taggers as components in a template -`tagTemplate` allows you to combine all previous taggers to create your own tagging policy. +`tagTemplate` allows you to combine all existing taggers to create a custom tagging policy. This policy requires that you specify a tag template, where part of template can be replaced with the result of other tagging strategies during the tagging process. These other tagging strategies are called components, which can be From 9089f5594f2523df8ce14bca35ab6f207e868fe9 Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Thu, 30 Jul 2020 14:26:48 -0400 Subject: [PATCH 027/138] Update docs/content/en/docs/pipeline-stages/taggers.md Co-authored-by: Nick Kubala --- docs/content/en/docs/pipeline-stages/taggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/en/docs/pipeline-stages/taggers.md b/docs/content/en/docs/pipeline-stages/taggers.md index 33a2b71368a..b1c63ff3727 100644 --- a/docs/content/en/docs/pipeline-stages/taggers.md +++ b/docs/content/en/docs/pipeline-stages/taggers.md @@ -149,7 +149,7 @@ tag policy features two optional parameters: `format` and `timezone`. `tagTemplate` allows you to combine all existing taggers to create a custom tagging policy. This policy requires that you specify a tag template, -where part of template can be replaced with the result of other tagging strategies during the tagging process. +using a combination of plaintext and references to other tagging strategies which will be evaluated at runtime. These other tagging strategies are called components, which can be a `gitCommit`, `sha256`, `envTemplate`, or `dateTime` tagger. From fe2a768459bf989bca6a3208122ba46544509988 Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Thu, 30 Jul 2020 14:26:54 -0400 Subject: [PATCH 028/138] Update docs/content/en/docs/pipeline-stages/taggers.md Co-authored-by: Nick Kubala --- docs/content/en/docs/pipeline-stages/taggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/en/docs/pipeline-stages/taggers.md b/docs/content/en/docs/pipeline-stages/taggers.md index b1c63ff3727..9b470130ad5 100644 --- a/docs/content/en/docs/pipeline-stages/taggers.md +++ b/docs/content/en/docs/pipeline-stages/taggers.md @@ -150,7 +150,7 @@ tag policy features two optional parameters: `format` and `timezone`. `tagTemplate` allows you to combine all existing taggers to create a custom tagging policy. This policy requires that you specify a tag template, using a combination of plaintext and references to other tagging strategies which will be evaluated at runtime. -These other tagging strategies are called components, which can be +We refer to these individual parts as "components", which can be a `gitCommit`, `sha256`, `envTemplate`, or `dateTime` tagger. The following `build` section, for example, instructs Skaffold to build a Docker image From 99aedd57a1b9ca8feaf4fe7c6d358fc3deebd7fc Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Thu, 30 Jul 2020 14:27:26 -0400 Subject: [PATCH 029/138] Update docs/content/en/docs/pipeline-stages/taggers.md Co-authored-by: Nick Kubala --- docs/content/en/docs/pipeline-stages/taggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/en/docs/pipeline-stages/taggers.md b/docs/content/en/docs/pipeline-stages/taggers.md index 9b470130ad5..49325fd3453 100644 --- a/docs/content/en/docs/pipeline-stages/taggers.md +++ b/docs/content/en/docs/pipeline-stages/taggers.md @@ -151,7 +151,7 @@ tag policy features two optional parameters: `format` and `timezone`. This policy requires that you specify a tag template, using a combination of plaintext and references to other tagging strategies which will be evaluated at runtime. We refer to these individual parts as "components", which can be -a `gitCommit`, `sha256`, `envTemplate`, or `dateTime` tagger. +any of the other existing supported tagging strategies. Nested `tagTemplate` components are not supported. The following `build` section, for example, instructs Skaffold to build a Docker image `gcr.io/k8s-skaffold/example` with the `tagTemplate` tag policy. From 00c7277c6c17d92558753b3fcd97f51163e8c20b Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Thu, 30 Jul 2020 14:27:33 -0400 Subject: [PATCH 030/138] Update docs/content/en/docs/pipeline-stages/taggers.md Co-authored-by: Nick Kubala --- docs/content/en/docs/pipeline-stages/taggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/en/docs/pipeline-stages/taggers.md b/docs/content/en/docs/pipeline-stages/taggers.md index 49325fd3453..f9bde19bc83 100644 --- a/docs/content/en/docs/pipeline-stages/taggers.md +++ b/docs/content/en/docs/pipeline-stages/taggers.md @@ -162,7 +162,7 @@ it will evaluate `FOO` and `BAR` and use their values to tag the image. {{< alert >}} Note
-`GIT`, `DATE`, and `SHA` are built-in component values that will evaluate to the default gitCommit, dateTime, and sha256 taggers, respectively. +`GIT`, `DATE`, and `SHA` are special built-in component references that will evaluate to the default gitCommit, dateTime, and sha256 taggers, respectively. User can overwrite these values by defining a component with one of these names. {{< /alert >}} From 7d36aebd27d071c76ad53b2de21184120463dfac Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Thu, 30 Jul 2020 14:27:39 -0400 Subject: [PATCH 031/138] Update docs/content/en/docs/pipeline-stages/taggers.md Co-authored-by: Nick Kubala --- docs/content/en/docs/pipeline-stages/taggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/en/docs/pipeline-stages/taggers.md b/docs/content/en/docs/pipeline-stages/taggers.md index f9bde19bc83..404bff5712d 100644 --- a/docs/content/en/docs/pipeline-stages/taggers.md +++ b/docs/content/en/docs/pipeline-stages/taggers.md @@ -170,7 +170,7 @@ User can overwrite these values by defining a component with one of these names. {{% readfile file="samples/taggers/tagTemplate.yaml" %}} -Suppose current time is `15:04:09.999 January 2nd, 2006` and the abbreviated commit sha was `25c65e0`, the image built will be `gcr.io/k8s-skaffold/example:2006-01-02_25c65e0`. +Suppose the current time is `15:04:09.999 January 2nd, 2006` and the abbreviated commit sha is `25c65e0`, the image built will be `gcr.io/k8s-skaffold/example:2006-01-02_25c65e0`. ### Configuration From 41baa279f324406f999019ae1a28331c529be84a Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Thu, 30 Jul 2020 14:27:45 -0400 Subject: [PATCH 032/138] Update docs/content/en/docs/pipeline-stages/taggers.md Co-authored-by: Nick Kubala --- docs/content/en/docs/pipeline-stages/taggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/en/docs/pipeline-stages/taggers.md b/docs/content/en/docs/pipeline-stages/taggers.md index 404bff5712d..9540834f9b6 100644 --- a/docs/content/en/docs/pipeline-stages/taggers.md +++ b/docs/content/en/docs/pipeline-stages/taggers.md @@ -174,6 +174,6 @@ Suppose the current time is `15:04:09.999 January 2nd, 2006` and the abbreviated ### Configuration -The tag template uses the [Go Programming Language Syntax](https://golang.org/pkg/text/template/). +The tag template uses the [Golang Templating Syntax](https://golang.org/pkg/text/template/). As showcased in the example, `tagTemplate` tag policy features one **required** parameter, `template`, which is the tag template to use. To learn more about templating support in Skaffold.yaml see [Templated fields]({{< relref "../environment/templating.md" >}}) From 3aa3c03d89c436ed50765ba53e69a9a421fc01fe Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Thu, 30 Jul 2020 14:27:58 -0400 Subject: [PATCH 033/138] Update docs/content/en/samples/taggers/tagTemplate.yaml Co-authored-by: Nick Kubala --- docs/content/en/samples/taggers/tagTemplate.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/content/en/samples/taggers/tagTemplate.yaml b/docs/content/en/samples/taggers/tagTemplate.yaml index d879717931e..d03491f4f41 100644 --- a/docs/content/en/samples/taggers/tagTemplate.yaml +++ b/docs/content/en/samples/taggers/tagTemplate.yaml @@ -11,4 +11,5 @@ build: gitCommit: variant: AbbrevCommitSha artifacts: - - image: gcr.io/k8s-skaffold/example \ No newline at end of file + - image: gcr.io/k8s-skaffold/example + From 3c75beb210e933d7999f2bf4dadecec3f65470d6 Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Thu, 30 Jul 2020 14:28:06 -0400 Subject: [PATCH 034/138] Update pkg/skaffold/runner/new.go Co-authored-by: Nick Kubala --- pkg/skaffold/runner/new.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/skaffold/runner/new.go b/pkg/skaffold/runner/new.go index b8182a14013..b2ac5903e08 100644 --- a/pkg/skaffold/runner/new.go +++ b/pkg/skaffold/runner/new.go @@ -269,7 +269,7 @@ func CreateComponents(t *latest.TemplateTagger) (map[string]tag.Tagger, error) { components[name] = tag.NewDateTimeTagger(c.DateTimeTagger.Format, c.DateTimeTagger.TimeZone) case c.TemplateTagger != nil: - return nil, fmt.Errorf("cannot use tagTemplate as a component: %s %+v", name, c) + return nil, fmt.Errorf("nested tagTemplate components are not supported in skaffold (%s)", name) default: return nil, fmt.Errorf("unknown component for tagTemplate: %s %+v", name, c) From 48c611fc28ee7d71f1e308fd6b5cdde69177c2ae Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Thu, 30 Jul 2020 14:28:26 -0400 Subject: [PATCH 035/138] Update docs/content/en/docs/pipeline-stages/taggers.md Co-authored-by: Nick Kubala --- docs/content/en/docs/pipeline-stages/taggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/en/docs/pipeline-stages/taggers.md b/docs/content/en/docs/pipeline-stages/taggers.md index 9540834f9b6..f358e46a817 100644 --- a/docs/content/en/docs/pipeline-stages/taggers.md +++ b/docs/content/en/docs/pipeline-stages/taggers.md @@ -163,7 +163,7 @@ it will evaluate `FOO` and `BAR` and use their values to tag the image. Note
`GIT`, `DATE`, and `SHA` are special built-in component references that will evaluate to the default gitCommit, dateTime, and sha256 taggers, respectively. -User can overwrite these values by defining a component with one of these names. +Users can overwrite these values by defining a component with one of these names. {{< /alert >}} ### Example From ba9ce78d1925a9b53c04a951e20949648206cc23 Mon Sep 17 00:00:00 2001 From: Felix Date: Thu, 30 Jul 2020 14:32:07 -0400 Subject: [PATCH 036/138] resolve comments --- docs/content/en/docs/pipeline-stages/taggers.md | 2 +- pkg/skaffold/schema/v2beta5/upgrade.go | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/content/en/docs/pipeline-stages/taggers.md b/docs/content/en/docs/pipeline-stages/taggers.md index f358e46a817..30a4b48ec95 100644 --- a/docs/content/en/docs/pipeline-stages/taggers.md +++ b/docs/content/en/docs/pipeline-stages/taggers.md @@ -176,4 +176,4 @@ Suppose the current time is `15:04:09.999 January 2nd, 2006` and the abbreviated The tag template uses the [Golang Templating Syntax](https://golang.org/pkg/text/template/). As showcased in the example, `tagTemplate` tag policy features one -**required** parameter, `template`, which is the tag template to use. To learn more about templating support in Skaffold.yaml see [Templated fields]({{< relref "../environment/templating.md" >}}) +**required** parameter, `template`, which is the tag template to use. To learn more about templating support in the skaffold.yaml, see [Templated fields]({{< relref "../environment/templating.md" >}}) \ No newline at end of file diff --git a/pkg/skaffold/schema/v2beta5/upgrade.go b/pkg/skaffold/schema/v2beta5/upgrade.go index 4c2f52ed194..de97f3093d3 100755 --- a/pkg/skaffold/schema/v2beta5/upgrade.go +++ b/pkg/skaffold/schema/v2beta5/upgrade.go @@ -26,8 +26,6 @@ import ( // Config changes from v2beta5 to v2beta6 // 1. Additions: // New structs TemplateTagger and TaggerComponent used by TagPolicy. -// 2. Removals: -// 3. Updates: func (c *SkaffoldConfig) Upgrade() (util.VersionedConfig, error) { var newConfig next.SkaffoldConfig pkgutil.CloneThroughJSON(c, &newConfig) From 2ca444caebf5c18add0282fd8762eaeb8fa025a6 Mon Sep 17 00:00:00 2001 From: tejal29 Date: Thu, 30 Jul 2020 11:39:11 -0700 Subject: [PATCH 037/138] code review comments and add tests --- pkg/diag/validator/pod.go | 20 ++-- pkg/skaffold/deploy/resource/deployment.go | 3 +- pkg/skaffold/deploy/status_check.go | 13 ++- pkg/skaffold/deploy/status_check_test.go | 125 +++++++++++++++++++++ pkg/skaffold/runner/deploy_test.go | 4 +- proto/skaffold.proto | 2 +- 6 files changed, 148 insertions(+), 19 deletions(-) diff --git a/pkg/diag/validator/pod.go b/pkg/diag/validator/pod.go index b0a6d959f1f..2c2f9626b88 100644 --- a/pkg/diag/validator/pod.go +++ b/pkg/diag/validator/pod.go @@ -116,25 +116,25 @@ func (p *PodValidator) getPodStatus(pod *v1.Pod) *podStatus { case v1.PodSucceeded: return ps default: - return ps.withErrAndLogs(getContainerStatus(pod)) + return ps.withErrAndLogs(getPodStatus(pod)) } } -func getContainerStatus(pod *v1.Pod) (proto.StatusCode, []string, error) { +func getPodStatus(pod *v1.Pod) (proto.StatusCode, []string, error) { // See https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-conditions for _, c := range pod.Status.Conditions { if c.Type == v1.PodScheduled { switch c.Status { case v1.ConditionFalse: - logrus.Debugf("Getting tolerations for pod %s", pod.Name) + logrus.Debugf("Pod %q not scheduled: checking tolerations", pod.Name) sc, err := getUntoleratedTaints(c.Reason, c.Message) return sc, nil, err case v1.ConditionTrue: - logrus.Debugf("Fetching container statuses for pod %s", pod.Name) + logrus.Debugf("Pod %q scheduled: checking container statuses", pod.Name) // TODO(dgageot): Add EphemeralContainerStatuses cs := append(pod.Status.InitContainerStatuses, pod.Status.ContainerStatuses...) // See https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-states - return getWaitingContainerStatus(pod, cs) + return getContainerStatus(pod, cs) case v1.ConditionUnknown: logrus.Debugf("Could not determine scheduling condition for pod %s", pod.Name) return proto.StatusCode_STATUSCHECK_UNKNOWN, nil, fmt.Errorf(c.Message) @@ -144,17 +144,15 @@ func getContainerStatus(pod *v1.Pod) (proto.StatusCode, []string, error) { return proto.StatusCode_STATUSCHECK_SUCCESS, nil, nil } -func getWaitingContainerStatus(po *v1.Pod, cs []v1.ContainerStatus) (proto.StatusCode, []string, error) { +func getContainerStatus(po *v1.Pod, cs []v1.ContainerStatus) (proto.StatusCode, []string, error) { // See https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-states for _, c := range cs { switch { case c.State.Waiting != nil: return extractErrorMessageFromWaitingContainerStatus(po, c) - case c.State.Terminated != nil: - if c.State.Terminated.ExitCode != 0 { - l := getPodLogs(po, c.Name) - return proto.StatusCode_STATUSCHECK_CONTAINER_TERMINATED, l, fmt.Errorf("container %s terminated with exit code %d", c.Name, c.State.Terminated.ExitCode) - } + case c.State.Terminated != nil && c.State.Terminated.ExitCode != 0: + l := getPodLogs(po, c.Name) + return proto.StatusCode_STATUSCHECK_CONTAINER_TERMINATED, l, fmt.Errorf("container %s terminated with exit code %d", c.Name, c.State.Terminated.ExitCode) } } // No waiting or terminated containers, pod should be in good health. diff --git a/pkg/skaffold/deploy/resource/deployment.go b/pkg/skaffold/deploy/resource/deployment.go index da98264dca8..36fefd0ba31 100644 --- a/pkg/skaffold/deploy/resource/deployment.go +++ b/pkg/skaffold/deploy/resource/deployment.go @@ -220,8 +220,9 @@ func isErrAndNotRetryAble(statusCode proto.StatusCode) bool { statusCode != proto.StatusCode_STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING } +// AreContainersNotRetryAble goes through all pod statuses and return true +// if any cannot be retried func (d *Deployment) AreContainersNotRetryAble() bool { - // Go through all pod statuses and make sure they are not retriable. for _, p := range d.pods { if _, ok := nonRetryContainerErrors[p.ActionableError().ErrCode]; ok { return true diff --git a/pkg/skaffold/deploy/status_check.go b/pkg/skaffold/deploy/status_check.go index d1e8fa20c40..5726791b249 100644 --- a/pkg/skaffold/deploy/status_check.go +++ b/pkg/skaffold/deploy/status_check.go @@ -65,8 +65,8 @@ type counter struct { func StatusCheck(ctx context.Context, defaultLabeller *DefaultLabeller, runCtx *runcontext.RunContext, out io.Writer, iteration int) error { event.StatusCheckEventStarted() - s := &checker{ - iteration: iteration, + s := checker{ + devIteration: iteration, } errCode, err := s.statusCheck(ctx, defaultLabeller, runCtx, out) event.StatusCheckEventEnded(errCode, err) @@ -74,7 +74,7 @@ func StatusCheck(ctx context.Context, defaultLabeller *DefaultLabeller, runCtx * } type checker struct { - iteration int + devIteration int } func (s checker) statusCheck(ctx context.Context, defaultLabeller *DefaultLabeller, runCtx *runcontext.RunContext, out io.Writer) (proto.StatusCode, error) { @@ -171,7 +171,12 @@ func (s checker) pollDeploymentStatus(ctx context.Context, runCtx *runcontext.Ru if r.IsStatusCheckComplete() { return } - if s.iteration == 0 && r.AreContainersNotRetryAble() { + // If this is the first skaffold dev devIteration, then fail immediately if + // any pod container errors cannot be recovered. + // Runner starts watching for file changes only after first deploy is successful. + // As any changes to build or deploy dependencies are not triggered, exit + // immediately rather than waiting for for statusCheckDeadlineSeconds + if s.devIteration == 0 && r.AreContainersNotRetryAble() { r.MarkComplete() return } diff --git a/pkg/skaffold/deploy/status_check_test.go b/pkg/skaffold/deploy/status_check_test.go index acf8904d797..d4f4216c1ce 100644 --- a/pkg/skaffold/deploy/status_check_test.go +++ b/pkg/skaffold/deploy/status_check_test.go @@ -18,10 +18,14 @@ package deploy import ( "bytes" + "context" "errors" "testing" "time" + "github.com/GoogleContainerTools/skaffold/pkg/diag/validator" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" appsv1 "k8s.io/api/apps/v1" @@ -520,3 +524,124 @@ func TestGetStatusCheckDeadline(t *testing.T) { }) } } + +func TestPollDeployment(t *testing.T) { + rolloutCmd := "kubectl --context kubecontext rollout status deployment dep --namespace test --watch=false" + tests := []struct { + description string + iteration int + dep *resource.Deployment + runs [][]validator.Resource + command util.Command + expected proto.StatusCode + }{ + { + description: "0 devIteration errors out immediately when container error can't recover", + dep: resource.NewDeployment("dep", "test", time.Second), + command: testutil.CmdRunOut(rolloutCmd, "Waiting for replicas to be available"), + runs: [][]validator.Resource{ + {validator.NewResource( + "test", + "pod", + "dep-pod", + "Pending", + proto.ActionableErr{ErrCode: proto.StatusCode_STATUSCHECK_CONTAINER_TERMINATED}, + []string{"err"})}, + }, + expected: proto.StatusCode_STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING, + }, + { + description: "0 devIteration waits when a container can recover and eventually succeeds", + dep: resource.NewDeployment("dep", "test", time.Second), + command: testutil.CmdRunOutErr( + rolloutCmd, "", errors.New("Unable to connect to the server"), + ).AndRunOut(rolloutCmd, "successfully rolled out"), + runs: [][]validator.Resource{ + {validator.NewResource( + "test", + "pod", + "dep-pod", + "Pending", + proto.ActionableErr{ErrCode: proto.StatusCode_STATUSCHECK_NODE_DISK_PRESSURE}, + []string{"err"})}, + {validator.NewResource( + "test", + "pod", + "dep-pod", + "Running", + proto.ActionableErr{ErrCode: proto.StatusCode_STATUSCHECK_SUCCESS}, + nil)}, + }, + expected: proto.StatusCode_STATUSCHECK_SUCCESS, + }, + { + description: "1 devIteration waits for container error to recover and eventually succeeds", + iteration: 1, + dep: resource.NewDeployment("dep", "test", time.Second), + command: testutil.CmdRunOut(rolloutCmd, "Waiting for replicas to be available"). + AndRunOut(rolloutCmd, "Waiting for replicas to be available"). + AndRunOut(rolloutCmd, "successfully rolled out"), + runs: [][]validator.Resource{ + {validator.NewResource( + "test", + "pod", + "dep-pod", + "Pending", + proto.ActionableErr{ErrCode: proto.StatusCode_STATUSCHECK_CONTAINER_TERMINATED}, + []string{"err"})}, + {validator.NewResource( + "test", + "pod", + "dep-pod", + "Pending", + proto.ActionableErr{ErrCode: proto.StatusCode_STATUSCHECK_IMAGE_PULL_ERR}, + nil)}, + {validator.NewResource( + "test", + "pod", + "dep-pod", + "Running", + proto.ActionableErr{ErrCode: proto.StatusCode_STATUSCHECK_SUCCESS}, + nil)}, + }, + expected: proto.StatusCode_STATUSCHECK_SUCCESS, + }, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + t.Override(&util.DefaultExecCommand, test.command) + t.Override(&defaultPollPeriodInMilliseconds, 100) + event.InitializeState(latest.Pipeline{}, "test", true, true, true) + mockVal := mockValidator{runs: test.runs} + dep := test.dep.WithValidator(mockVal) + s := checker{devIteration: test.iteration} + runCtx := &runcontext.RunContext{ + KubeContext: "kubecontext", + } + s.pollDeploymentStatus(context.Background(), runCtx, dep) + t.CheckDeepEqual(test.expected, test.dep.Status().ActionableError().ErrCode) + }) + } +} + +type mockValidator struct { + runs [][]validator.Resource + iteration int +} + +func (m mockValidator) Run(context.Context) ([]validator.Resource, error) { + if m.iteration < len(m.runs) { + return m.runs[m.iteration], nil + m.iteration++ + } + // keep replaying the last result. + return m.runs[m.iteration-1], nil +} + +func (m mockValidator) WithLabel(string, string) diag.Diagnose { + return m +} + +func (m mockValidator) WithValidators([]validator.Validator) diag.Diagnose { + return m +} diff --git a/pkg/skaffold/runner/deploy_test.go b/pkg/skaffold/runner/deploy_test.go index b2395bf7399..598f7358c80 100644 --- a/pkg/skaffold/runner/deploy_test.go +++ b/pkg/skaffold/runner/deploy_test.go @@ -63,7 +63,7 @@ func TestDeploy(t *testing.T) { }, } - dummyStatusCheck := func(context.Context, *deploy.DefaultLabeller, *runcontext.RunContext, io.Writer) error { + dummyStatusCheck := func(context.Context, *deploy.DefaultLabeller, *runcontext.RunContext, io.Writer, int) error { return nil } for _, test := range tests { @@ -114,7 +114,7 @@ func TestDeployNamespace(t *testing.T) { }, } - dummyStatusCheck := func(context.Context, *deploy.DefaultLabeller, *runcontext.RunContext, io.Writer) error { + dummyStatusCheck := func(context.Context, *deploy.DefaultLabeller, *runcontext.RunContext, io.Writer, int) error { return nil } for _, test := range tests { diff --git a/proto/skaffold.proto b/proto/skaffold.proto index 410d5c8eaea..e79277309c1 100644 --- a/proto/skaffold.proto +++ b/proto/skaffold.proto @@ -446,7 +446,7 @@ enum StatusCode { // Pod States // Pod Initializing - STATUSCHECK_POD_INITIALIZING= 451; + STATUSCHECK_POD_INITIALIZING = 451; // Unknown Error Codes From 02a6817c3bee48ce728c49471441cff7dad2b1bf Mon Sep 17 00:00:00 2001 From: Brian de Alwis Date: Thu, 30 Jul 2020 14:53:41 -0400 Subject: [PATCH 038/138] Log when values are taken from global config file (#4566) --- pkg/skaffold/config/util.go | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/pkg/skaffold/config/util.go b/pkg/skaffold/config/util.go index 42292254f48..49008d8a137 100644 --- a/pkg/skaffold/config/util.go +++ b/pkg/skaffold/config/util.go @@ -73,6 +73,9 @@ func readConfigFileCached(filename string) (*GlobalConfig, error) { return } configFile, configFileErr = ReadConfigFileNoCache(filenameOrDefault) + if configFileErr == nil { + logrus.Infof("Loaded Skaffold defaults from %q", filenameOrDefault) + } }) return configFile, configFileErr } @@ -161,7 +164,9 @@ func GetDefaultRepo(configFile string, cliValue *string) (string, error) { if err != nil { return "", err } - + if cfg.DefaultRepo != "" { + logrus.Infof("Using default-repo=%s from config", cfg.DefaultRepo) + } return cfg.DefaultRepo, nil } @@ -175,6 +180,7 @@ func GetLocalCluster(configFile string, minikubeProfile string) (bool, error) { } // when set, the local-cluster config takes precedence if cfg.LocalCluster != nil { + logrus.Infof("Using local-cluster=%v from config", *cfg.LocalCluster) return *cfg.LocalCluster, nil } @@ -190,7 +196,9 @@ func GetInsecureRegistries(configFile string) ([]string, error) { if err != nil { return nil, err } - + if len(cfg.InsecureRegistries) > 0 { + logrus.Infof("Using insecure-registries=%v from config", cfg.InsecureRegistries) + } return cfg.InsecureRegistries, nil } @@ -200,11 +208,11 @@ func GetDebugHelpersRegistry(configFile string) (string, error) { return "", err } - if cfg.DebugHelpersRegistry == "" { - return constants.DefaultDebugHelpersRegistry, nil + if cfg.DebugHelpersRegistry != "" { + logrus.Infof("Using debug-helpers-registry=%s from config", cfg.DebugHelpersRegistry) + return cfg.DebugHelpersRegistry, nil } - - return cfg.DebugHelpersRegistry, nil + return constants.DefaultDebugHelpersRegistry, nil } func isDefaultLocal(kubeContext string) bool { From 8c60696db44ebfac5e5ab4afca7d29c50e9aac7e Mon Sep 17 00:00:00 2001 From: tejal29 Date: Thu, 30 Jul 2020 11:56:32 -0700 Subject: [PATCH 039/138] remove first iteration cause --- pkg/skaffold/deploy/status_check.go | 25 ++++++--------- pkg/skaffold/deploy/status_check_test.go | 40 ++---------------------- pkg/skaffold/runner/deploy.go | 2 +- pkg/skaffold/runner/deploy_test.go | 4 +-- 4 files changed, 15 insertions(+), 56 deletions(-) diff --git a/pkg/skaffold/deploy/status_check.go b/pkg/skaffold/deploy/status_check.go index 5726791b249..b611753fa23 100644 --- a/pkg/skaffold/deploy/status_check.go +++ b/pkg/skaffold/deploy/status_check.go @@ -63,21 +63,14 @@ type counter struct { failed int32 } -func StatusCheck(ctx context.Context, defaultLabeller *DefaultLabeller, runCtx *runcontext.RunContext, out io.Writer, iteration int) error { +func StatusCheck(ctx context.Context, defaultLabeller *DefaultLabeller, runCtx *runcontext.RunContext, out io.Writer) error { event.StatusCheckEventStarted() - s := checker{ - devIteration: iteration, - } - errCode, err := s.statusCheck(ctx, defaultLabeller, runCtx, out) + errCode, err := statusCheck(ctx, defaultLabeller, runCtx, out) event.StatusCheckEventEnded(errCode, err) return err } -type checker struct { - devIteration int -} - -func (s checker) statusCheck(ctx context.Context, defaultLabeller *DefaultLabeller, runCtx *runcontext.RunContext, out io.Writer) (proto.StatusCode, error) { +func statusCheck(ctx context.Context, defaultLabeller *DefaultLabeller, runCtx *runcontext.RunContext, out io.Writer) (proto.StatusCode, error) { client, err := pkgkubernetes.Client() if err != nil { return proto.StatusCode_STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR, fmt.Errorf("getting Kubernetes client: %w", err) @@ -108,7 +101,7 @@ func (s checker) statusCheck(ctx context.Context, defaultLabeller *DefaultLabell go func(r *resource.Deployment) { defer wg.Done() // keep updating the resource status until it fails/succeeds/times out - s.pollDeploymentStatus(ctx, runCtx, r) + pollDeploymentStatus(ctx, runCtx, r) rcCopy := c.markProcessed(r.Status().Error()) printStatusCheckSummary(out, r, rcCopy) }(d) @@ -153,7 +146,7 @@ func getDeployments(client kubernetes.Interface, ns string, l *DefaultLabeller, return deployments, nil } -func (s checker) pollDeploymentStatus(ctx context.Context, runCtx *runcontext.RunContext, r *resource.Deployment) { +func pollDeploymentStatus(ctx context.Context, runCtx *runcontext.RunContext, r *resource.Deployment) { pollDuration := time.Duration(defaultPollPeriodInMilliseconds) * time.Millisecond // Add poll duration to account for one last attempt after progressDeadlineSeconds. timeoutContext, cancel := context.WithTimeout(ctx, r.Deadline()+pollDuration) @@ -171,12 +164,12 @@ func (s checker) pollDeploymentStatus(ctx context.Context, runCtx *runcontext.Ru if r.IsStatusCheckComplete() { return } - // If this is the first skaffold dev devIteration, then fail immediately if - // any pod container errors cannot be recovered. - // Runner starts watching for file changes only after first deploy is successful. + // Fail immediately if any pod container errors cannot be recovered. + // StatusCheck is not interruptable. // As any changes to build or deploy dependencies are not triggered, exit // immediately rather than waiting for for statusCheckDeadlineSeconds - if s.devIteration == 0 && r.AreContainersNotRetryAble() { + // TODO: https://github.com/GoogleContainerTools/skaffold/pull/4591 + if r.AreContainersNotRetryAble() { r.MarkComplete() return } diff --git a/pkg/skaffold/deploy/status_check_test.go b/pkg/skaffold/deploy/status_check_test.go index d4f4216c1ce..fec6083139d 100644 --- a/pkg/skaffold/deploy/status_check_test.go +++ b/pkg/skaffold/deploy/status_check_test.go @@ -529,14 +529,13 @@ func TestPollDeployment(t *testing.T) { rolloutCmd := "kubectl --context kubecontext rollout status deployment dep --namespace test --watch=false" tests := []struct { description string - iteration int dep *resource.Deployment runs [][]validator.Resource command util.Command expected proto.StatusCode }{ { - description: "0 devIteration errors out immediately when container error can't recover", + description: "pollDeploymentStatus errors out immediately when container error can't recover", dep: resource.NewDeployment("dep", "test", time.Second), command: testutil.CmdRunOut(rolloutCmd, "Waiting for replicas to be available"), runs: [][]validator.Resource{ @@ -551,7 +550,7 @@ func TestPollDeployment(t *testing.T) { expected: proto.StatusCode_STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING, }, { - description: "0 devIteration waits when a container can recover and eventually succeeds", + description: "pollDeploymentStatus waits when a container can recover and eventually succeeds", dep: resource.NewDeployment("dep", "test", time.Second), command: testutil.CmdRunOutErr( rolloutCmd, "", errors.New("Unable to connect to the server"), @@ -574,38 +573,6 @@ func TestPollDeployment(t *testing.T) { }, expected: proto.StatusCode_STATUSCHECK_SUCCESS, }, - { - description: "1 devIteration waits for container error to recover and eventually succeeds", - iteration: 1, - dep: resource.NewDeployment("dep", "test", time.Second), - command: testutil.CmdRunOut(rolloutCmd, "Waiting for replicas to be available"). - AndRunOut(rolloutCmd, "Waiting for replicas to be available"). - AndRunOut(rolloutCmd, "successfully rolled out"), - runs: [][]validator.Resource{ - {validator.NewResource( - "test", - "pod", - "dep-pod", - "Pending", - proto.ActionableErr{ErrCode: proto.StatusCode_STATUSCHECK_CONTAINER_TERMINATED}, - []string{"err"})}, - {validator.NewResource( - "test", - "pod", - "dep-pod", - "Pending", - proto.ActionableErr{ErrCode: proto.StatusCode_STATUSCHECK_IMAGE_PULL_ERR}, - nil)}, - {validator.NewResource( - "test", - "pod", - "dep-pod", - "Running", - proto.ActionableErr{ErrCode: proto.StatusCode_STATUSCHECK_SUCCESS}, - nil)}, - }, - expected: proto.StatusCode_STATUSCHECK_SUCCESS, - }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { @@ -614,11 +581,10 @@ func TestPollDeployment(t *testing.T) { event.InitializeState(latest.Pipeline{}, "test", true, true, true) mockVal := mockValidator{runs: test.runs} dep := test.dep.WithValidator(mockVal) - s := checker{devIteration: test.iteration} runCtx := &runcontext.RunContext{ KubeContext: "kubecontext", } - s.pollDeploymentStatus(context.Background(), runCtx, dep) + pollDeploymentStatus(context.Background(), runCtx, dep) t.CheckDeepEqual(test.expected, test.dep.Status().ActionableError().ErrCode) }) } diff --git a/pkg/skaffold/runner/deploy.go b/pkg/skaffold/runner/deploy.go index 5b9ab4144b4..70465bccfea 100644 --- a/pkg/skaffold/runner/deploy.go +++ b/pkg/skaffold/runner/deploy.go @@ -139,7 +139,7 @@ func (r *SkaffoldRunner) performStatusCheck(ctx context.Context, out io.Writer) start := time.Now() color.Default.Fprintln(out, "Waiting for deployments to stabilize...") - err := statusCheck(ctx, r.labeller, r.runCtx, out, r.devIteration) + err := statusCheck(ctx, r.labeller, r.runCtx, out) if err != nil { return err } diff --git a/pkg/skaffold/runner/deploy_test.go b/pkg/skaffold/runner/deploy_test.go index 598f7358c80..b2395bf7399 100644 --- a/pkg/skaffold/runner/deploy_test.go +++ b/pkg/skaffold/runner/deploy_test.go @@ -63,7 +63,7 @@ func TestDeploy(t *testing.T) { }, } - dummyStatusCheck := func(context.Context, *deploy.DefaultLabeller, *runcontext.RunContext, io.Writer, int) error { + dummyStatusCheck := func(context.Context, *deploy.DefaultLabeller, *runcontext.RunContext, io.Writer) error { return nil } for _, test := range tests { @@ -114,7 +114,7 @@ func TestDeployNamespace(t *testing.T) { }, } - dummyStatusCheck := func(context.Context, *deploy.DefaultLabeller, *runcontext.RunContext, io.Writer, int) error { + dummyStatusCheck := func(context.Context, *deploy.DefaultLabeller, *runcontext.RunContext, io.Writer) error { return nil } for _, test := range tests { From dfc9e5f3aa4187c06630052232609125608c346e Mon Sep 17 00:00:00 2001 From: tejal29 Date: Thu, 30 Jul 2020 14:09:35 -0700 Subject: [PATCH 040/138] fix linters --- pkg/skaffold/deploy/status_check_test.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pkg/skaffold/deploy/status_check_test.go b/pkg/skaffold/deploy/status_check_test.go index fec6083139d..adee222d84c 100644 --- a/pkg/skaffold/deploy/status_check_test.go +++ b/pkg/skaffold/deploy/status_check_test.go @@ -23,9 +23,6 @@ import ( "testing" "time" - "github.com/GoogleContainerTools/skaffold/pkg/diag/validator" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" appsv1 "k8s.io/api/apps/v1" @@ -35,9 +32,12 @@ import ( utilpointer "k8s.io/utils/pointer" "github.com/GoogleContainerTools/skaffold/pkg/diag" + "github.com/GoogleContainerTools/skaffold/pkg/diag/validator" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/resource" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/proto" "github.com/GoogleContainerTools/skaffold/testutil" ) @@ -553,9 +553,12 @@ func TestPollDeployment(t *testing.T) { description: "pollDeploymentStatus waits when a container can recover and eventually succeeds", dep: resource.NewDeployment("dep", "test", time.Second), command: testutil.CmdRunOutErr( - rolloutCmd, "", errors.New("Unable to connect to the server"), - ).AndRunOut(rolloutCmd, "successfully rolled out"), + // pending due to recoverable error + rolloutCmd, "", errors.New("Unable to connect to the server")). + // successfully rolled out run + AndRunOut(rolloutCmd, "successfully rolled out"), runs: [][]validator.Resource{ + // pod pending due to some k8 infra related recoverable error. {validator.NewResource( "test", "pod", @@ -563,6 +566,7 @@ func TestPollDeployment(t *testing.T) { "Pending", proto.ActionableErr{ErrCode: proto.StatusCode_STATUSCHECK_NODE_DISK_PRESSURE}, []string{"err"})}, + // pod recovered {validator.NewResource( "test", "pod", @@ -597,8 +601,8 @@ type mockValidator struct { func (m mockValidator) Run(context.Context) ([]validator.Resource, error) { if m.iteration < len(m.runs) { - return m.runs[m.iteration], nil m.iteration++ + return m.runs[m.iteration], nil } // keep replaying the last result. return m.runs[m.iteration-1], nil From 0d2531b632bd2bebbbfedd824c60df1755b15327 Mon Sep 17 00:00:00 2001 From: Tejal Desai Date: Thu, 30 Jul 2020 14:14:41 -0700 Subject: [PATCH 041/138] Apply suggestions from code review Co-authored-by: Brian de Alwis --- pkg/diag/validator/pod.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/diag/validator/pod.go b/pkg/diag/validator/pod.go index 2c2f9626b88..845ddcaa98a 100644 --- a/pkg/diag/validator/pod.go +++ b/pkg/diag/validator/pod.go @@ -136,7 +136,7 @@ func getPodStatus(pod *v1.Pod) (proto.StatusCode, []string, error) { // See https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-states return getContainerStatus(pod, cs) case v1.ConditionUnknown: - logrus.Debugf("Could not determine scheduling condition for pod %s", pod.Name) + logrus.Debugf("Pod %q scheduling condition is unknown", pod.Name) return proto.StatusCode_STATUSCHECK_UNKNOWN, nil, fmt.Errorf(c.Message) } } @@ -211,13 +211,13 @@ func processPodEvents(e corev1.EventInterface, pod v1.Pod, ps *podStatus) { if _, ok := unknownConditionsOrSuccess[ps.ae.ErrCode]; !ok { return } - logrus.Debugf("Fetching events for pod %s", pod.Name) + logrus.Debugf("Fetching events for pod %q", pod.Name) // Get pod events. scheme := runtime.NewScheme() scheme.AddKnownTypes(v1.SchemeGroupVersion, &pod) events, err := e.Search(scheme, &pod) if err != nil { - logrus.Debugf("Could not fetch events for resource %s due to %v", pod.Name, err) + logrus.Debugf("Could not fetch events for resource %q due to %v", pod.Name, err) return } // find the latest failed event. @@ -307,7 +307,7 @@ func extractErrorMessageFromWaitingContainerStatus(po *v1.Pod, c v1.ContainerSta return proto.StatusCode_STATUSCHECK_RUN_CONTAINER_ERR, nil, fmt.Errorf("container %s in error: %s", c.Name, trimSpace(match[3])) } } - logrus.Debugf("Failed to extract error message for waiting container %q: %v", c.Name, c.State) + logrus.Debugf("Unknown waiting reason for container %q: %v", c.Name, c.State) return proto.StatusCode_STATUSCHECK_CONTAINER_WAITING_UNKNOWN, nil, fmt.Errorf("container %s in error: %v", c.Name, c.State.Waiting) } @@ -327,7 +327,7 @@ func trimSpace(msg string) string { } func getPodLogs(po *v1.Pod, c string) []string { - logrus.Debugf("Fetching logs for container %v", c) + logrus.Debugf("Fetching logs for container %q/%q", po.Name, c) logCommand := []string{"kubectl", "logs", po.Name, "-n", po.Namespace, "-c", c} logs, err := runCli(logCommand[0], logCommand[1:]) if err != nil { From c7b42b2e85d6eb72c5f330e981bdca9625062603 Mon Sep 17 00:00:00 2001 From: Brian de Alwis Date: Thu, 30 Jul 2020 17:16:19 -0400 Subject: [PATCH 042/138] Skip logging first error from parsing Helm release info --- pkg/skaffold/deploy/util.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/skaffold/deploy/util.go b/pkg/skaffold/deploy/util.go index 9a4a4798277..db78d03e198 100644 --- a/pkg/skaffold/deploy/util.go +++ b/pkg/skaffold/deploy/util.go @@ -61,7 +61,7 @@ func parseReleaseInfo(namespace string, b *bufio.Reader) []Artifact { var results []Artifact r := k8syaml.NewYAMLReader(b) - for { + for i := 0; ; i++ { doc, err := r.Read() if err == io.EOF { break @@ -77,7 +77,9 @@ func parseReleaseInfo(namespace string, b *bufio.Reader) []Artifact { } obj, err := parseRuntimeObject(objNamespace, doc) if err != nil { - logrus.Infof(err.Error()) + if i > 0 { + logrus.Infof(err.Error()) + } } else { results = append(results, *obj) logrus.Debugf("found deployed object: %+v", obj.Obj) From 750455e624ee2a96e9ee7d8d6bdaa0901dc451c6 Mon Sep 17 00:00:00 2001 From: Brian de Alwis Date: Thu, 30 Jul 2020 17:29:01 -0400 Subject: [PATCH 043/138] Use alternative service port-forwarding scheme (#4590) When Skaffold redeploys new workload resources, it tears down and re-establishes port-forwards for services. But `kubectl port-forward service/xxx` actually selects and forwards to a matching pod, in a way that favours older pods -- pods that are often about to be replaced during the rollout. We select a pod matching the service but favour newer pods which are more likely to be from the rollout. --- .../portforward/kubectl_forwarder.go | 137 +++++++- .../portforward/kubectl_forwarder_test.go | 303 +++++++++++++++--- 2 files changed, 376 insertions(+), 64 deletions(-) diff --git a/pkg/skaffold/kubernetes/portforward/kubectl_forwarder.go b/pkg/skaffold/kubernetes/portforward/kubectl_forwarder.go index 93be4e6fcf7..94d112ab557 100644 --- a/pkg/skaffold/kubernetes/portforward/kubectl_forwarder.go +++ b/pkg/skaffold/kubernetes/portforward/kubectl_forwarder.go @@ -22,13 +22,20 @@ import ( "context" "fmt" "io" + "os" + "sort" "strings" "time" "github.com/sirupsen/logrus" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/intstr" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/color" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" ) @@ -52,11 +59,11 @@ func NewKubectlForwarder(out io.Writer, cli *kubectl.CLI) *KubectlForwarder { // For testing var ( - isPortFree = util.IsPortFree - portForwardCmd = portForwardCommand - deferFunc = func() {} - waitPortNotFree = 5 * time.Second - waitErrorLogs = 1 * time.Second + isPortFree = util.IsPortFree + findNewestPodForSvc = findNewestPodForService + deferFunc = func() {} + waitPortNotFree = 5 * time.Second + waitErrorLogs = 1 * time.Second ) // Forward port-forwards a pod using kubectl port-forward in the background @@ -97,8 +104,11 @@ func (k *KubectlForwarder) forward(parentCtx context.Context, pfe *portForwardEn ctx, cancel := context.WithCancel(parentCtx) pfe.cancel = cancel + args := portForwardArgs(ctx, pfe) var buf bytes.Buffer - cmd := portForwardCmd(ctx, k.kubectl, pfe, &buf) + cmd := k.kubectl.CommandWithStrictCancellation(ctx, "port-forward", args...) + cmd.Stdout = &buf + cmd.Stderr = &buf logrus.Debugf("Running command: %s", cmd.Args) if err := cmd.Start(); err != nil { @@ -127,20 +137,29 @@ func (k *KubectlForwarder) forward(parentCtx context.Context, pfe *portForwardEn } } -func portForwardCommand(ctx context.Context, k *kubectl.CLI, pfe *portForwardEntry, buf io.Writer) *kubectl.Cmd { - args := []string{ - "--pod-running-timeout", "1s", - fmt.Sprintf("%s/%s", pfe.resource.Type, pfe.resource.Name), - fmt.Sprintf("%d:%d", pfe.localPort, pfe.resource.Port), - "--namespace", pfe.resource.Namespace, +func portForwardArgs(ctx context.Context, pfe *portForwardEntry) []string { + args := []string{"--pod-running-timeout", "1s", "--namespace", pfe.resource.Namespace} + + _, disableServiceForwarding := os.LookupEnv("SKAFFOLD_DISABLE_SERVICE_FORWARDING") + switch { + case pfe.resource.Type == "service" && !disableServiceForwarding: + // Services need special handling: https://github.com/GoogleContainerTools/skaffold/issues/4522 + podName, remotePort, err := findNewestPodForSvc(ctx, pfe.resource.Namespace, pfe.resource.Name, pfe.resource.Port) + if err == nil { + args = append(args, fmt.Sprintf("pod/%s", podName), fmt.Sprintf("%d:%d", pfe.localPort, remotePort)) + break + } + logrus.Warnf("could not map pods to service %s/%s/%d: %v", pfe.resource.Namespace, pfe.resource.Name, pfe.resource.Port, err) + fallthrough // and let kubectl try to handle it + + default: + args = append(args, fmt.Sprintf("%s/%s", pfe.resource.Type, pfe.resource.Name), fmt.Sprintf("%d:%d", pfe.localPort, pfe.resource.Port)) } + if pfe.resource.Address != "" && pfe.resource.Address != util.Loopback { args = append(args, []string{"--address", pfe.resource.Address}...) } - cmd := k.CommandWithStrictCancellation(ctx, "port-forward", args...) - cmd.Stdout = buf - cmd.Stderr = buf - return cmd + return args } // Terminate terminates an existing kubectl port-forward command using SIGTERM @@ -189,3 +208,89 @@ func (*KubectlForwarder) monitorErrorLogs(ctx context.Context, logs io.Reader, c } } } + +// findNewestPodForService queries the cluster to find a pod that fulfills the given service, giving +// preference to pods that were most recently created. This is in contrast to the selection algorithm +// used by kubectl (see https://github.com/GoogleContainerTools/skaffold/issues/4522 for details). +func findNewestPodForService(ctx context.Context, ns, serviceName string, servicePort int) (string, int, error) { + client, err := kubernetes.Client() + if err != nil { + return "", -1, fmt.Errorf("getting Kubernetes client: %w", err) + } + svc, err := client.CoreV1().Services(ns).Get(serviceName, metav1.GetOptions{}) + if err != nil { + return "", -1, fmt.Errorf("getting service %s/%s: %w", ns, serviceName, err) + } + svcPort, err := findServicePort(*svc, servicePort) + if err != nil { + return "", -1, err + } + + // Look for pods with matching selectors and that are not terminated. + // We cannot use field selectors as they are only supported in 1.16 + // https://github.com/flant/shell-operator/blob/8fa3c3b8cfeb1ddb37b070b7a871561fdffe788b/HOOKS.md#fieldselector + set := labels.Set(svc.Spec.Selector) + listOptions := metav1.ListOptions{ + LabelSelector: set.AsSelector().String(), + } + podsList, err := client.CoreV1().Pods(ns).List(listOptions) + if err != nil { + return "", -1, fmt.Errorf("listing pods: %w", err) + } + var pods []corev1.Pod + for _, pod := range podsList.Items { + if pod.Status.Phase == corev1.PodPending || pod.Status.Phase == corev1.PodRunning { + pods = append(pods, pod) + } + } + sort.Slice(pods, newestPodsFirst(pods)) + + if logrus.IsLevelEnabled((logrus.TraceLevel)) { + var names []string + for _, p := range pods { + names = append(names, fmt.Sprintf("(pod:%q phase:%v created:%v)", p.Name, p.Status.Phase, p.CreationTimestamp)) + } + logrus.Tracef("service %s/%d maps to %d pods: %v", serviceName, servicePort, len(pods), names) + } + + for _, p := range pods { + if targetPort := findTargetPort(svcPort, p); targetPort > 0 { + logrus.Debugf("Forwarding service %s/%d to pod %s/%d", serviceName, servicePort, p.Name, targetPort) + return p.Name, targetPort, nil + } + } + + return "", -1, fmt.Errorf("no pods match service %s/%d", serviceName, servicePort) +} + +// newestPodsFirst sorts pods by their creation time +func newestPodsFirst(pods []corev1.Pod) func(int, int) bool { + return func(i, j int) bool { + ti := pods[i].CreationTimestamp.Time + tj := pods[j].CreationTimestamp.Time + return ti.After(tj) + } +} + +func findServicePort(svc corev1.Service, servicePort int) (corev1.ServicePort, error) { + for _, s := range svc.Spec.Ports { + if int(s.Port) == servicePort { + return s, nil + } + } + return corev1.ServicePort{}, fmt.Errorf("service %q does not expose port %d", svc.Name, servicePort) +} + +func findTargetPort(svcPort corev1.ServicePort, pod corev1.Pod) int { + if svcPort.TargetPort.Type == intstr.Int { + return svcPort.TargetPort.IntValue() + } + for _, c := range pod.Spec.Containers { + for _, p := range c.Ports { + if svcPort.TargetPort.StrVal == p.Name { + return int(p.ContainerPort) + } + } + } + return -1 +} diff --git a/pkg/skaffold/kubernetes/portforward/kubectl_forwarder_test.go b/pkg/skaffold/kubernetes/portforward/kubectl_forwarder_test.go index 77acddc6352..132a261e1ee 100644 --- a/pkg/skaffold/kubernetes/portforward/kubectl_forwarder_test.go +++ b/pkg/skaffold/kubernetes/portforward/kubectl_forwarder_test.go @@ -19,14 +19,23 @@ package portforward import ( "bytes" "context" + "errors" "runtime" + "sort" "strings" "sync" "testing" "time" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + pkgruntime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/fake" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" + pkgkubernetes "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/testutil" ) @@ -171,61 +180,259 @@ func assertCmdWasKilled(t *testutil.T, cmd *kubectl.Cmd) { } } -func TestAddressArg(t *testing.T) { - ctx := context.Background() - pfe := newPortForwardEntry(0, latest.PortForwardResource{Address: "0.0.0.0"}, "", "", "", "", 8080, false) - cli := kubectl.NewFromRunContext(&runcontext.RunContext{}) - cmd := portForwardCommand(ctx, cli, pfe, nil) - assertCmdContainsArgs(t, cmd, true, "--address", "0.0.0.0") +func TestPortForwardArgs(t *testing.T) { + tests := []struct { + description string + input *portForwardEntry + servicePod string + servicePort int + serviceErr error + result []string + }{ + { + description: "non-default address", + input: newPortForwardEntry(0, latest.PortForwardResource{Type: "pod", Name: "p", Namespace: "ns", Port: 9, Address: "0.0.0.0"}, "", "", "", "", 8080, false), + result: []string{"--pod-running-timeout", "1s", "--namespace", "ns", "pod/p", "8080:9", "--address", "0.0.0.0"}, + }, + { + description: "localhost is the default", + input: newPortForwardEntry(0, latest.PortForwardResource{Type: "pod", Name: "p", Namespace: "ns", Port: 9, Address: "127.0.0.1"}, "", "", "", "", 8080, false), + result: []string{"--pod-running-timeout", "1s", "--namespace", "ns", "pod/p", "8080:9"}, + }, + { + description: "no address", + input: newPortForwardEntry(0, latest.PortForwardResource{Type: "pod", Name: "p", Namespace: "ns", Port: 9}, "", "", "", "", 8080, false), + result: []string{"--pod-running-timeout", "1s", "--namespace", "ns", "pod/p", "8080:9"}, + }, + { + description: "service to pod", + input: newPortForwardEntry(0, latest.PortForwardResource{Type: "service", Name: "svc", Namespace: "ns", Port: 9}, "", "", "", "", 8080, false), + servicePod: "servicePod", + servicePort: 9999, + result: []string{"--pod-running-timeout", "1s", "--namespace", "ns", "pod/servicePod", "8080:9999"}, + }, + { + description: "service could not be mapped to pod", + input: newPortForwardEntry(0, latest.PortForwardResource{Type: "service", Name: "svc", Namespace: "ns", Port: 9}, "", "", "", "", 8080, false), + serviceErr: errors.New("error"), + result: []string{"--pod-running-timeout", "1s", "--namespace", "ns", "service/svc", "8080:9"}, + }, + } + + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) + defer cancel() + + t.Override(&findNewestPodForSvc, func(ctx context.Context, ns, serviceName string, servicePort int) (string, int, error) { + return test.servicePod, test.servicePort, test.serviceErr + }) + + args := portForwardArgs(ctx, test.input) + t.CheckDeepEqual(test.result, args) + }) + } } -func TestNoAddressArg(t *testing.T) { - ctx := context.Background() - pfe := newPortForwardEntry(0, latest.PortForwardResource{}, "", "", "", "", 8080, false) - cli := kubectl.NewFromRunContext(&runcontext.RunContext{}) - cmd := portForwardCommand(ctx, cli, pfe, nil) - assertCmdContainsArgs(t, cmd, false, "--address") +func TestNewestPodFirst(t *testing.T) { + starting := mockPod("starting", nil, time.Now()) + starting.Status.Phase = corev1.PodPending + new := mockPod("new", nil, time.Now().Add(-time.Minute)) + old := mockPod("old", nil, time.Now().Add(-time.Hour)) + + pods := []corev1.Pod{*old, *new, *starting} + sort.Slice(pods, newestPodsFirst(pods)) + + expected := []corev1.Pod{*starting, *new, *old} + testutil.CheckDeepEqual(t, expected, pods) } -func TestDefaultAddressArg(t *testing.T) { - ctx := context.Background() - pfe := newPortForwardEntry(0, latest.PortForwardResource{Address: "127.0.0.1"}, "", "", "", "", 8080, false) - cli := kubectl.NewFromRunContext(&runcontext.RunContext{}) - cmd := portForwardCommand(ctx, cli, pfe, nil) - assertCmdContainsArgs(t, cmd, false, "--address") +func TestFindServicePort(t *testing.T) { + tests := []struct { + description string + service *corev1.Service + port int + shouldErr bool + expected corev1.ServicePort + }{ + { + description: "simple case", + service: mockService("svc1", corev1.ServiceTypeLoadBalancer, []corev1.ServicePort{{Port: 90, TargetPort: intstr.FromInt(80)}, {Port: 80, TargetPort: intstr.FromInt(8080)}}), + port: 80, + expected: corev1.ServicePort{Port: 80, TargetPort: intstr.FromInt(8080)}, + }, + { + description: "no ports", + service: mockService("svc2", corev1.ServiceTypeLoadBalancer, nil), + port: 80, + shouldErr: true, + }, + { + description: "no matching ports", + service: mockService("svc3", corev1.ServiceTypeLoadBalancer, []corev1.ServicePort{{Port: 90, TargetPort: intstr.FromInt(80)}, {Port: 80, TargetPort: intstr.FromInt(8080)}}), + port: 100, + shouldErr: true, + }, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + result, err := findServicePort(*test.service, test.port) + t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expected, result) + }) + } } -func assertCmdContainsArgs(t *testing.T, cmd *kubectl.Cmd, expected bool, args ...string) { - if len(args) == 0 { - return - } - contains := false - cmdArgs := cmd.Args - var start int - var cmdArg string - for start, cmdArg = range cmdArgs { - if cmdArg == args[0] { - contains = true - break - } +func TestFindTargetPort(t *testing.T) { + tests := []struct { + description string + servicePort corev1.ServicePort + pod corev1.Pod + expected int + }{ + { + description: "integer port", + servicePort: corev1.ServicePort{TargetPort: intstr.FromInt(8080)}, + pod: *mockPod("new", []corev1.ContainerPort{{Name: "http", ContainerPort: 8080}}, time.Time{}), + expected: 8080, + }, + { + description: "named port", + servicePort: corev1.ServicePort{TargetPort: intstr.FromString("http")}, + pod: *mockPod("new", []corev1.ContainerPort{{Name: "http", ContainerPort: 8080}}, time.Time{}), + expected: 8080, + }, + { + description: "no port found", + servicePort: corev1.ServicePort{TargetPort: intstr.FromString("http")}, + pod: *mockPod("new", nil, time.Time{}), + expected: -1, + }, } - if !contains { - if expected { - t.Fatalf("cmd expected to contain args %v but args are %v", args, cmdArgs) - } - return + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + result := findTargetPort(test.servicePort, test.pod) + t.CheckDeepEqual(test.expected, result) + }) } - for i, arg := range args[1:] { - if arg != cmdArgs[start+i+1] { - contains = false - break - } +} + +func TestFindNewestPodForService(t *testing.T) { + tests := []struct { + description string + clientResources []pkgruntime.Object + clientErr error + serviceName string + servicePort int + shouldErr bool + chosenPod string + chosenPort int + }{ + { + description: "chooses new with port 8080 via int targetport", + clientResources: []pkgruntime.Object{ + mockService("svc", corev1.ServiceTypeLoadBalancer, []corev1.ServicePort{{Port: 80, TargetPort: intstr.FromInt(8080)}}), + mockPod("new", []corev1.ContainerPort{{Name: "http", ContainerPort: 8080}}, time.Now().Add(-time.Minute)), + mockPod("old", []corev1.ContainerPort{{Name: "http", ContainerPort: 8080}}, time.Now().Add(-time.Hour)), + }, + serviceName: "svc", + servicePort: 80, + chosenPod: "new", + chosenPort: 8080, + }, + { + description: "chooses new with port 8080 via string targetport", + clientResources: []pkgruntime.Object{ + mockService("svc", corev1.ServiceTypeLoadBalancer, []corev1.ServicePort{{Port: 80, TargetPort: intstr.FromString("http")}}), + mockPod("new", []corev1.ContainerPort{{Name: "http", ContainerPort: 8080}}, time.Now().Add(-time.Minute)), + mockPod("old", []corev1.ContainerPort{{Name: "http", ContainerPort: 8080}}, time.Now().Add(-time.Hour)), + }, + serviceName: "svc", + servicePort: 80, + chosenPod: "new", + chosenPort: 8080, + }, + { + description: "service not found", + clientResources: []pkgruntime.Object{ + mockService("svc", corev1.ServiceTypeClusterIP, []corev1.ServicePort{{Port: 80, TargetPort: intstr.FromInt(8080)}}), + mockPod("new", []corev1.ContainerPort{{Name: "http", ContainerPort: 8080}}, time.Now().Add(-time.Minute)), + mockPod("old", []corev1.ContainerPort{{Name: "http", ContainerPort: 8080}}, time.Now().Add(-time.Hour)), + }, + serviceName: "notfound", + servicePort: 80, + shouldErr: true, + chosenPort: -1, + }, + { + description: "port not found", + clientResources: []pkgruntime.Object{ + mockService("svc", corev1.ServiceTypeLoadBalancer, []corev1.ServicePort{{Port: 80, TargetPort: intstr.FromInt(8080)}}), + mockPod("new", []corev1.ContainerPort{{Name: "http", ContainerPort: 8080}}, time.Now().Add(-time.Minute)), + mockPod("old", []corev1.ContainerPort{{Name: "http", ContainerPort: 8080}}, time.Now().Add(-time.Hour)), + }, + serviceName: "svc", + servicePort: 90, + shouldErr: true, + chosenPort: -1, + }, + { + description: "no matching pods", + clientResources: []pkgruntime.Object{ + mockService("service", corev1.ServiceTypeLoadBalancer, []corev1.ServicePort{{Port: 80, TargetPort: intstr.FromInt(8080)}}), + }, + serviceName: "svc", + servicePort: 90, + shouldErr: true, + chosenPort: -1, + }, + { + description: "port not found", + clientErr: errors.New("injected failure"), + serviceName: "svc", + servicePort: 90, + shouldErr: true, + chosenPort: -1, + }, } - if contains != expected { - if expected { - t.Fatalf("cmd expected to contain args %v but args are %v", args, cmdArgs) - } else { - t.Fatalf("cmd expected not to contain args %v but args are %v", args, cmdArgs) - } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) + defer cancel() + + t.Override(&pkgkubernetes.Client, func() (kubernetes.Interface, error) { + return fake.NewSimpleClientset(test.clientResources...), test.clientErr + }) + + pod, port, err := findNewestPodForService(ctx, "", test.serviceName, test.servicePort) + t.CheckErrorAndDeepEqual(test.shouldErr, err, test.chosenPod, pod) + t.CheckErrorAndDeepEqual(test.shouldErr, err, test.chosenPort, port) + }) + } +} + +func mockService(name string, serviceType corev1.ServiceType, ports []corev1.ServicePort) *corev1.Service { + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{Name: name}, + Spec: corev1.ServiceSpec{ + Type: serviceType, + Ports: ports, + }} +} + +func mockPod(name string, ports []corev1.ContainerPort, creationTime time.Time) *corev1.Pod { + return &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + CreationTimestamp: metav1.NewTime(creationTime), + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Name: "container", + Ports: ports, + }}, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + }, } } From 21afd5cc60d1c5403dbe040a6b089cf515a68cd6 Mon Sep 17 00:00:00 2001 From: Marlon Gamez Date: Thu, 30 Jul 2020 16:19:36 -0700 Subject: [PATCH 044/138] add support for muting deploy logs --- pkg/skaffold/deploy/logfile.go | 60 ++++++++++++++ pkg/skaffold/deploy/logfile_test.go | 123 ++++++++++++++++++++++++++++ pkg/skaffold/runner/deploy.go | 9 +- 3 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 pkg/skaffold/deploy/logfile.go create mode 100644 pkg/skaffold/deploy/logfile_test.go diff --git a/pkg/skaffold/deploy/logfile.go b/pkg/skaffold/deploy/logfile.go new file mode 100644 index 00000000000..f5e7576d0a3 --- /dev/null +++ b/pkg/skaffold/deploy/logfile.go @@ -0,0 +1,60 @@ +/* +Copyright 2020 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 deploy + +import ( + "bytes" + "fmt" + "io" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/color" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/logfile" +) + +// TimeFormat is used to name log files generated by deploy step +const TimeFormat = "2006-01-02_15-04-05" + +type Muted interface { + MuteDeploy() bool +} + +// WithLogFile returns a multiwriter that writes both to a files and a buffer, with the buffer being written to out in case of error +func WithLogFile(filename string, out io.Writer, muted Muted) (io.Writer, func(error), error) { + if !muted.MuteDeploy() { + return out, func(error) {}, nil + } + + file, err := logfile.Create("deploy", filename) + if err != nil { + return out, func(error) {}, fmt.Errorf("unable to create log file for deploy step: %w", err) + } + + color.Default.Fprintln(out, "Writing deploy logs to", file.Name()) + + // Print logs to a memory buffer and to a file. + var buf bytes.Buffer + w := io.MultiWriter(file, &buf) + + // After the build finishes, close the log file. If the build failed, print the full log to the console. + + return w, func(deployErr error) { + file.Close() + if deployErr != nil { + buf.WriteTo(out) + } + }, err +} diff --git a/pkg/skaffold/deploy/logfile_test.go b/pkg/skaffold/deploy/logfile_test.go new file mode 100644 index 00000000000..1683a188353 --- /dev/null +++ b/pkg/skaffold/deploy/logfile_test.go @@ -0,0 +1,123 @@ +/* +Copyright 2020 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 deploy + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" + "github.com/GoogleContainerTools/skaffold/testutil" +) + +func TestWithLogFile(t *testing.T) { + logDeploySucceeded := " - fake/deployment created" + logDeployFailed := " - failed to deploy" + logFilename := " - writing logs to " + filepath.Join(os.TempDir(), "skaffold", "deploy", "deploy.log") + + tests := []struct { + description string + muted Muted + shouldErr bool + expectedNamespaces []string + logsFound []string + logsNotFound []string + }{ + { + description: "all logs", + muted: muted(false), + shouldErr: false, + expectedNamespaces: []string{"ns"}, + logsFound: []string{logDeploySucceeded}, + logsNotFound: []string{logFilename}, + }, + { + description: "mute build logs", + muted: muted(true), + shouldErr: false, + expectedNamespaces: []string{"ns"}, + logsFound: []string{logFilename}, + logsNotFound: []string{logDeploySucceeded}, + }, + { + description: "failed deploy - all logs", + muted: muted(false), + shouldErr: true, + expectedNamespaces: nil, + logsFound: []string{logDeployFailed}, + logsNotFound: []string{logFilename}, + }, + { + description: "failed deploy - muted logs", + muted: muted(true), + shouldErr: true, + expectedNamespaces: nil, + logsFound: []string{logFilename, logDeployFailed}, + }, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + var mockOut bytes.Buffer + + var deployer = mockDeployer{ + muted: test.muted, + shouldErr: test.shouldErr, + } + + deployOut, postDeployFn, _ := WithLogFile("deploy.log", &mockOut, test.muted) + namespaces, err := deployer.Deploy(context.Background(), deployOut, nil) + postDeployFn(err) + + t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expectedNamespaces, namespaces) + for _, found := range test.logsFound { + t.CheckContains(found, mockOut.String()) + } + for _, notFound := range test.logsNotFound { + t.CheckFalse(strings.Contains(mockOut.String(), notFound)) + } + }) + } +} + +// Used just to show how output gets routed to different writers with the log file +type mockDeployer struct { + muted Muted + shouldErr bool +} + +func (fd *mockDeployer) Deploy(ctx context.Context, out io.Writer, _ []build.Artifact) ([]string, error) { + if fd.shouldErr { + fmt.Fprintln(out, " - failed to deploy") + return nil, errors.New("failed to deploy") + } + + fmt.Fprintln(out, " - fake/deployment created") + return []string{"ns"}, nil +} + +type muted bool + +func (m muted) MuteDeploy() bool { + return bool(m) +} diff --git a/pkg/skaffold/runner/deploy.go b/pkg/skaffold/runner/deploy.go index a6093a52f8a..fb087b1102d 100644 --- a/pkg/skaffold/runner/deploy.go +++ b/pkg/skaffold/runner/deploy.go @@ -28,6 +28,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/color" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" kubectx "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/context" @@ -65,9 +66,15 @@ See https://skaffold.dev/docs/pipeline-stages/taggers/#how-tagging-works`) } } + deployOut, postDeployFn, err := deploy.WithLogFile(time.Now().Format(deploy.TimeFormat)+".log", out, r.runCtx.Muted()) + if err != nil { + return err + } + event.DeployInProgress() - namespaces, err := r.deployer.Deploy(ctx, out, artifacts) + namespaces, err := r.deployer.Deploy(ctx, deployOut, artifacts) r.hasDeployed = true + postDeployFn(err) if err != nil { event.DeployFailed(err) return err From 59ad3d0533c0f39c987a631fe7e53b2807f97304 Mon Sep 17 00:00:00 2001 From: tejal29 Date: Fri, 31 Jul 2020 09:33:00 -0700 Subject: [PATCH 045/138] fix tests --- pkg/skaffold/deploy/status_check_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/skaffold/deploy/status_check_test.go b/pkg/skaffold/deploy/status_check_test.go index adee222d84c..34deb54ce84 100644 --- a/pkg/skaffold/deploy/status_check_test.go +++ b/pkg/skaffold/deploy/status_check_test.go @@ -602,7 +602,6 @@ type mockValidator struct { func (m mockValidator) Run(context.Context) ([]validator.Resource, error) { if m.iteration < len(m.runs) { m.iteration++ - return m.runs[m.iteration], nil } // keep replaying the last result. return m.runs[m.iteration-1], nil From 4b3d8db47c49adf62d47ac7d4395431e0bd73dc3 Mon Sep 17 00:00:00 2001 From: Marlon Gamez Date: Fri, 31 Jul 2020 10:12:41 -0700 Subject: [PATCH 046/138] fix expected logs in test file --- pkg/skaffold/deploy/logfile_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/skaffold/deploy/logfile_test.go b/pkg/skaffold/deploy/logfile_test.go index 1683a188353..6dae2a3e814 100644 --- a/pkg/skaffold/deploy/logfile_test.go +++ b/pkg/skaffold/deploy/logfile_test.go @@ -34,7 +34,7 @@ import ( func TestWithLogFile(t *testing.T) { logDeploySucceeded := " - fake/deployment created" logDeployFailed := " - failed to deploy" - logFilename := " - writing logs to " + filepath.Join(os.TempDir(), "skaffold", "deploy", "deploy.log") + logFilename := "Writing deploy logs to " + filepath.Join(os.TempDir(), "skaffold", "deploy", "deploy.log") tests := []struct { description string From 91974f3e5d27638f3b2c60f9ae02481478f44666 Mon Sep 17 00:00:00 2001 From: tejal29 Date: Fri, 31 Jul 2020 10:34:52 -0700 Subject: [PATCH 047/138] code review --- pkg/diag/validator/pod.go | 2 +- pkg/skaffold/deploy/resource/deployment.go | 6 +++--- pkg/skaffold/deploy/status_check.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/diag/validator/pod.go b/pkg/diag/validator/pod.go index 845ddcaa98a..5e49e69452b 100644 --- a/pkg/diag/validator/pod.go +++ b/pkg/diag/validator/pod.go @@ -327,7 +327,7 @@ func trimSpace(msg string) string { } func getPodLogs(po *v1.Pod, c string) []string { - logrus.Debugf("Fetching logs for container %q/%q", po.Name, c) + logrus.Debugf("Fetching logs for container %s/%s", po.Name, c) logCommand := []string{"kubectl", "logs", po.Name, "-n", po.Namespace, "-c", c} logs, err := runCli(logCommand[0], logCommand[1:]) if err != nil { diff --git a/pkg/skaffold/deploy/resource/deployment.go b/pkg/skaffold/deploy/resource/deployment.go index 36fefd0ba31..6440056b9ad 100644 --- a/pkg/skaffold/deploy/resource/deployment.go +++ b/pkg/skaffold/deploy/resource/deployment.go @@ -220,9 +220,9 @@ func isErrAndNotRetryAble(statusCode proto.StatusCode) bool { statusCode != proto.StatusCode_STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING } -// AreContainersNotRetryAble goes through all pod statuses and return true -// if any cannot be retried -func (d *Deployment) AreContainersNotRetryAble() bool { +// HasEncounteredUnrecoverableError goes through all pod statuses and return true +// if any cannot be recovered +func (d *Deployment) HasEncounteredUnrecoverableError() bool { for _, p := range d.pods { if _, ok := nonRetryContainerErrors[p.ActionableError().ErrCode]; ok { return true diff --git a/pkg/skaffold/deploy/status_check.go b/pkg/skaffold/deploy/status_check.go index b611753fa23..ce8b6b571cf 100644 --- a/pkg/skaffold/deploy/status_check.go +++ b/pkg/skaffold/deploy/status_check.go @@ -169,7 +169,7 @@ func pollDeploymentStatus(ctx context.Context, runCtx *runcontext.RunContext, r // As any changes to build or deploy dependencies are not triggered, exit // immediately rather than waiting for for statusCheckDeadlineSeconds // TODO: https://github.com/GoogleContainerTools/skaffold/pull/4591 - if r.AreContainersNotRetryAble() { + if r.HasEncounteredUnrecoverableError() { r.MarkComplete() return } From 2afe386cb641c14e7b0bc56ff48ee429d04e8b20 Mon Sep 17 00:00:00 2001 From: Marlon Gamez Date: Fri, 31 Jul 2020 12:47:38 -0700 Subject: [PATCH 048/138] fix comments and change output --- pkg/skaffold/deploy/logfile.go | 8 ++++---- pkg/skaffold/deploy/logfile_test.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/skaffold/deploy/logfile.go b/pkg/skaffold/deploy/logfile.go index f5e7576d0a3..5013c12ffd0 100644 --- a/pkg/skaffold/deploy/logfile.go +++ b/pkg/skaffold/deploy/logfile.go @@ -32,7 +32,7 @@ type Muted interface { MuteDeploy() bool } -// WithLogFile returns a multiwriter that writes both to a files and a buffer, with the buffer being written to out in case of error +// WithLogFile returns a multiwriter that writes both to a file and a buffer, with the buffer being written to the provided output buffer in case of error func WithLogFile(filename string, out io.Writer, muted Muted) (io.Writer, func(error), error) { if !muted.MuteDeploy() { return out, func(error) {}, nil @@ -43,14 +43,14 @@ func WithLogFile(filename string, out io.Writer, muted Muted) (io.Writer, func(e return out, func(error) {}, fmt.Errorf("unable to create log file for deploy step: %w", err) } - color.Default.Fprintln(out, "Writing deploy logs to", file.Name()) + color.Default.Fprintln(out, "Starting deploy...") + fmt.Fprintln(out, "- writing logs to", file.Name()) // Print logs to a memory buffer and to a file. var buf bytes.Buffer w := io.MultiWriter(file, &buf) - // After the build finishes, close the log file. If the build failed, print the full log to the console. - + // After the deploy finishes, close the log file. If the deploy failed, print the full log to output buffer. return w, func(deployErr error) { file.Close() if deployErr != nil { diff --git a/pkg/skaffold/deploy/logfile_test.go b/pkg/skaffold/deploy/logfile_test.go index 6dae2a3e814..9af6c90321b 100644 --- a/pkg/skaffold/deploy/logfile_test.go +++ b/pkg/skaffold/deploy/logfile_test.go @@ -34,7 +34,7 @@ import ( func TestWithLogFile(t *testing.T) { logDeploySucceeded := " - fake/deployment created" logDeployFailed := " - failed to deploy" - logFilename := "Writing deploy logs to " + filepath.Join(os.TempDir(), "skaffold", "deploy", "deploy.log") + logFilename := "- writing logs to " + filepath.Join(os.TempDir(), "skaffold", "deploy", "deploy.log") tests := []struct { description string From 27ae52cd29cf78537ca18325bd7066385574d0a3 Mon Sep 17 00:00:00 2001 From: Felix Date: Fri, 31 Jul 2020 16:24:03 -0400 Subject: [PATCH 049/138] changed naming convention --- .../en/docs/pipeline-stages/taggers.md | 14 +++---- .../{tagTemplate.yaml => customTemplate.yaml} | 2 +- .../{tag_template.go => custom_template.go} | 38 +++++++++---------- ...mplate_test.go => custom_template_test.go} | 22 +++++------ pkg/skaffold/build/tag/git_commit_test.go | 4 +- pkg/skaffold/build/tag/tag_test.go | 9 +++++ pkg/skaffold/runner/new.go | 16 ++++---- pkg/skaffold/runner/new_test.go | 22 +++++------ pkg/skaffold/schema/latest/config.go | 12 +++--- pkg/skaffold/schema/v2beta5/upgrade.go | 2 +- 10 files changed, 75 insertions(+), 66 deletions(-) rename docs/content/en/samples/taggers/{tagTemplate.yaml => customTemplate.yaml} (93%) rename pkg/skaffold/build/tag/{tag_template.go => custom_template.go} (58%) rename pkg/skaffold/build/tag/{tag_template_test.go => custom_template_test.go} (88%) diff --git a/docs/content/en/docs/pipeline-stages/taggers.md b/docs/content/en/docs/pipeline-stages/taggers.md index 30a4b48ec95..b033b3446f0 100644 --- a/docs/content/en/docs/pipeline-stages/taggers.md +++ b/docs/content/en/docs/pipeline-stages/taggers.md @@ -12,7 +12,7 @@ Skaffold supports multiple taggers or tag policies for tagging images: + the `sha256` tagger uses `latest` to tag images. + the `envTemplate` tagger uses environment variables to tag images. + the `datetime` tagger uses current date and time, with a configurable pattern. - + the `tagTemplate` tagger uses a combination of the existing taggers as components in a template. + + the `customTemplate` tagger uses a combination of the existing taggers as components in a template. The default tagger, if none is specified in the `skaffold.yaml`, is the `gitCommit` tagger. @@ -145,16 +145,16 @@ You can learn more about what time format and time zone you can use in example, `dateTime` tag policy features two optional parameters: `format` and `timezone`. -## `tagTemplate`: uses a combination of the existing taggers as components in a template +## `customTemplate`: uses a combination of the existing taggers as components in a template -`tagTemplate` allows you to combine all existing taggers to create a custom tagging policy. +`customTemplate` allows you to combine all existing taggers to create a custom tagging policy. This policy requires that you specify a tag template, using a combination of plaintext and references to other tagging strategies which will be evaluated at runtime. We refer to these individual parts as "components", which can be -any of the other existing supported tagging strategies. Nested `tagTemplate` components are not supported. +any of the other existing supported tagging strategies. Nested `customTemplate` components are not supported. The following `build` section, for example, instructs Skaffold to build a Docker image -`gcr.io/k8s-skaffold/example` with the `tagTemplate` tag policy. +`gcr.io/k8s-skaffold/example` with the `customTemplate` tag policy. The tag template is `{{.FOO}}_{{.BAR}}`. The components are a `dateTime` tagger named `FOO` and a `gitCommit` tagger named `BAR`. When Skaffold finishes building the image, it will evaluate `FOO` and `BAR` and use their values to tag the image. @@ -168,12 +168,12 @@ Users can overwrite these values by defining a component with one of these names ### Example -{{% readfile file="samples/taggers/tagTemplate.yaml" %}} +{{% readfile file="samples/taggers/customTemplate.yaml" %}} Suppose the current time is `15:04:09.999 January 2nd, 2006` and the abbreviated commit sha is `25c65e0`, the image built will be `gcr.io/k8s-skaffold/example:2006-01-02_25c65e0`. ### Configuration The tag template uses the [Golang Templating Syntax](https://golang.org/pkg/text/template/). -As showcased in the example, `tagTemplate` tag policy features one +As showcased in the example, `customTemplate` tag policy features one **required** parameter, `template`, which is the tag template to use. To learn more about templating support in the skaffold.yaml, see [Templated fields]({{< relref "../environment/templating.md" >}}) \ No newline at end of file diff --git a/docs/content/en/samples/taggers/tagTemplate.yaml b/docs/content/en/samples/taggers/customTemplate.yaml similarity index 93% rename from docs/content/en/samples/taggers/tagTemplate.yaml rename to docs/content/en/samples/taggers/customTemplate.yaml index d03491f4f41..86b691381da 100644 --- a/docs/content/en/samples/taggers/tagTemplate.yaml +++ b/docs/content/en/samples/taggers/customTemplate.yaml @@ -1,6 +1,6 @@ build: tagPolicy: - tagTemplate: + customTemplate: template: "{{.FOO}}_{{.BAR}}" components: - name: FOO diff --git a/pkg/skaffold/build/tag/tag_template.go b/pkg/skaffold/build/tag/custom_template.go similarity index 58% rename from pkg/skaffold/build/tag/tag_template.go rename to pkg/skaffold/build/tag/custom_template.go index 260b3d831cf..9be9049bf59 100644 --- a/pkg/skaffold/build/tag/tag_template.go +++ b/pkg/skaffold/build/tag/custom_template.go @@ -24,34 +24,34 @@ import ( "github.com/sirupsen/logrus" ) -// templateTagger implements Tagger -type templateTagger struct { +// CustomTemplateTagger implements Tagger +type CustomTemplateTagger struct { Template *template.Template Components map[string]Tagger } -// NewTemplateTagger creates a new TemplateTagger -func NewTemplateTagger(t string, components map[string]Tagger) (Tagger, error) { - tmpl, err := ParseTagTemplate(t) +// NewCustomTemplateTagger creates a new CustomTemplateTagger +func NewCustomTemplateTagger(t string, components map[string]Tagger) (Tagger, error) { + tmpl, err := ParseCustomTemplate(t) if err != nil { return nil, fmt.Errorf("parsing template: %w", err) } - return &templateTagger{ + return &CustomTemplateTagger{ Template: tmpl, Components: components, }, nil } // GenerateTag generates a tag from a template referencing tagging strategies. -func (t *templateTagger) GenerateTag(workingDir, imageName string) (string, error) { +func (t *CustomTemplateTagger) GenerateTag(workingDir, imageName string) (string, error) { customMap, err := t.EvaluateComponents(workingDir, imageName) if err != nil { return "", err } // missingkey=error throws error when map is indexed with an undefined key - tag, err := ExecuteTagTemplate(t.Template.Option("missingkey=error"), customMap) + tag, err := ExecuteCustomTemplate(t.Template.Option("missingkey=error"), customMap) if err != nil { return "", err } @@ -60,7 +60,7 @@ func (t *templateTagger) GenerateTag(workingDir, imageName string) (string, erro } // EvaluateComponents creates a custom mapping of component names to their tagger string representation. -func (t *templateTagger) EvaluateComponents(workingDir, imageName string) (map[string]string, error) { +func (t *CustomTemplateTagger) EvaluateComponents(workingDir, imageName string) (map[string]string, error) { customMap := map[string]string{} gitTagger, _ := NewGitCommit("", "") @@ -72,28 +72,28 @@ func (t *templateTagger) EvaluateComponents(workingDir, imageName string) (map[s } for k, v := range t.Components { - if _, ok := v.(*templateTagger); ok { - return nil, fmt.Errorf("invalid component specified in tag template: %v", v) + if _, ok := v.(*CustomTemplateTagger); ok { + return nil, fmt.Errorf("invalid component specified in custom template: %v", v) } tag, err := v.GenerateTag(workingDir, imageName) if err != nil { - return nil, fmt.Errorf("evaluating tag template component: %w", err) + return nil, fmt.Errorf("evaluating custom template component: %w", err) } customMap[k] = tag } return customMap, nil } -// ParseTagTemplate is a simple wrapper to parse an tag template. -func ParseTagTemplate(t string) (*template.Template, error) { - return template.New("tagTemplate").Parse(t) +// ParseCustomTemplate is a simple wrapper to parse an custom template. +func ParseCustomTemplate(t string) (*template.Template, error) { + return template.New("customTemplate").Parse(t) } -// ExecuteTagTemplate executes a tagTemplate against a custom map. -func ExecuteTagTemplate(tagTemplate *template.Template, customMap map[string]string) (string, error) { +// ExecuteCustomTemplate executes a customTemplate against a custom map. +func ExecuteCustomTemplate(customTemplate *template.Template, customMap map[string]string) (string, error) { var buf bytes.Buffer - logrus.Debugf("Executing tag template %v with custom map %v", tagTemplate, customMap) - if err := tagTemplate.Execute(&buf, customMap); err != nil { + logrus.Debugf("Executing custom template %v with custom map %v", customTemplate, customMap) + if err := customTemplate.Execute(&buf, customMap); err != nil { return "", fmt.Errorf("executing template: %w", err) } return buf.String(), nil diff --git a/pkg/skaffold/build/tag/tag_template_test.go b/pkg/skaffold/build/tag/custom_template_test.go similarity index 88% rename from pkg/skaffold/build/tag/tag_template_test.go rename to pkg/skaffold/build/tag/custom_template_test.go index 0e5616fffbf..bffeb0e91ae 100644 --- a/pkg/skaffold/build/tag/tag_template_test.go +++ b/pkg/skaffold/build/tag/custom_template_test.go @@ -37,7 +37,7 @@ func TestTagTemplate_GenerateTag(t *testing.T) { invalidEnvTemplate, _ := NewEnvTemplateTagger("{{.BAR}}") env := []string{"FOO=BAR"} - tagTemplateExample, _ := NewTemplateTagger("", nil) + customTemplateExample, _ := NewCustomTemplateTagger("", nil) tests := []struct { description string @@ -67,9 +67,9 @@ func TestTagTemplate_GenerateTag(t *testing.T) { expected: "foo-BAR-latest", }, { - description: "using tagTemplate as a component", + description: "using customTemplate as a component", template: "{{.FOO}}", - customMap: map[string]Tagger{"FOO": tagTemplateExample}, + customMap: map[string]Tagger{"FOO": customTemplateExample}, shouldErr: true, }, { @@ -99,7 +99,7 @@ func TestTagTemplate_GenerateTag(t *testing.T) { testutil.Run(t, test.description, func(t *testutil.T) { t.Override(&util.OSEnviron, func() []string { return env }) - c, err := NewTemplateTagger(test.template, test.customMap) + c, err := NewCustomTemplateTagger(test.template, test.customMap) t.CheckNoError(err) @@ -110,7 +110,7 @@ func TestTagTemplate_GenerateTag(t *testing.T) { } } -func TestTagTemplate_NewTemplateTagger(t *testing.T) { +func TestCustomTemplate_NewCustomTemplateTagger(t *testing.T) { tests := []struct { description string template string @@ -140,13 +140,13 @@ func TestTagTemplate_NewTemplateTagger(t *testing.T) { } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - _, err := NewTemplateTagger(test.template, test.customMap) + _, err := NewCustomTemplateTagger(test.template, test.customMap) t.CheckError(test.shouldErr, err) }) } } -func TestTagTemplate_ExecuteTagTemplate(t *testing.T) { +func TestCustomTemplate_ExecuteCustomTemplate(t *testing.T) { tests := []struct { description string template string @@ -189,16 +189,16 @@ func TestTagTemplate_ExecuteTagTemplate(t *testing.T) { } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - testTemplate, err := ParseTagTemplate(test.template) + testTemplate, err := ParseCustomTemplate(test.template) t.CheckNoError(err) - got, err := ExecuteTagTemplate(testTemplate.Option("missingkey=error"), test.customMap) + got, err := ExecuteCustomTemplate(testTemplate.Option("missingkey=error"), test.customMap) t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expected, got) }) } } -func TestTagTemplate_ParseTagTemplate(t *testing.T) { +func TestCustomTemplate_ParseCustomTemplate(t *testing.T) { tests := []struct { description string template string @@ -216,7 +216,7 @@ func TestTagTemplate_ParseTagTemplate(t *testing.T) { } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - _, err := ParseTagTemplate(test.template) + _, err := ParseCustomTemplate(test.template) t.CheckError(test.shouldErr, err) }) } diff --git a/pkg/skaffold/build/tag/git_commit_test.go b/pkg/skaffold/build/tag/git_commit_test.go index fc7991056a2..489528654f5 100644 --- a/pkg/skaffold/build/tag/git_commit_test.go +++ b/pkg/skaffold/build/tag/git_commit_test.go @@ -399,7 +399,7 @@ func TestGitCommit_GenerateFullyQualifiedImageName(t *testing.T) { } } -func TestGitCommit_TagTemplate(t *testing.T) { +func TestGitCommit_CustomTemplate(t *testing.T) { gitCommitExample, _ := NewGitCommit("", "CommitSha") tests := []struct { description string @@ -440,7 +440,7 @@ func TestGitCommit_TagTemplate(t *testing.T) { test.createGitRepo(tmpDir.Root()) workspace := tmpDir.Path(test.subDir) - c, err := NewTemplateTagger(test.template, test.customMap) + c, err := NewCustomTemplateTagger(test.template, test.customMap) t.CheckNoError(err) diff --git a/pkg/skaffold/build/tag/tag_test.go b/pkg/skaffold/build/tag/tag_test.go index d7743adec8a..5546cf303bd 100644 --- a/pkg/skaffold/build/tag/tag_test.go +++ b/pkg/skaffold/build/tag/tag_test.go @@ -17,6 +17,7 @@ limitations under the License. package tag import ( + "fmt" "testing" "time" @@ -41,6 +42,8 @@ func TestTagger_GenerateFullyQualifiedImageName(t *testing.T) { } dateTimeExpected := "2015-03-07" + customTemplateExample, _ := NewCustomTemplateTagger("{{.DATE}}_{{.SHA}}", map[string]Tagger{"DATE": dateTimeExample}) + tests := []struct { description string imageName string @@ -96,6 +99,12 @@ func TestTagger_GenerateFullyQualifiedImageName(t *testing.T) { }, shouldErr: true, }, + { + description: "customTemplate", + imageName: "test", + tagger: customTemplateExample, + expected: fmt.Sprintf("test:%s_%s", dateTimeExpected, "latest"), + }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { diff --git a/pkg/skaffold/runner/new.go b/pkg/skaffold/runner/new.go index b2ac5903e08..dc1faac6458 100644 --- a/pkg/skaffold/runner/new.go +++ b/pkg/skaffold/runner/new.go @@ -230,22 +230,22 @@ func getTagger(runCtx *runcontext.RunContext) (tag.Tagger, error) { case t.DateTimeTagger != nil: return tag.NewDateTimeTagger(t.DateTimeTagger.Format, t.DateTimeTagger.TimeZone), nil - case t.TemplateTagger != nil: - components, err := CreateComponents(t.TemplateTagger) + case t.CustomTemplateTagger != nil: + components, err := CreateComponents(t.CustomTemplateTagger) if err != nil { return nil, fmt.Errorf("creating components: %w", err) } - return tag.NewTemplateTagger(t.TemplateTagger.Template, components) + return tag.NewCustomTemplateTagger(t.CustomTemplateTagger.Template, components) default: return nil, fmt.Errorf("unknown tagger for strategy %+v", t) } } -// CreateComponents creates a map of taggers for TemplateTagger -func CreateComponents(t *latest.TemplateTagger) (map[string]tag.Tagger, error) { +// CreateComponents creates a map of taggers for CustomTemplateTagger +func CreateComponents(t *latest.CustomTemplateTagger) (map[string]tag.Tagger, error) { components := map[string]tag.Tagger{} for _, taggerComponent := range t.Components { @@ -268,11 +268,11 @@ func CreateComponents(t *latest.TemplateTagger) (map[string]tag.Tagger, error) { case c.DateTimeTagger != nil: components[name] = tag.NewDateTimeTagger(c.DateTimeTagger.Format, c.DateTimeTagger.TimeZone) - case c.TemplateTagger != nil: - return nil, fmt.Errorf("nested tagTemplate components are not supported in skaffold (%s)", name) + case c.CustomTemplateTagger != nil: + return nil, fmt.Errorf("nested customTemplate components are not supported in skaffold (%s)", name) default: - return nil, fmt.Errorf("unknown component for tagTemplate: %s %+v", name, c) + return nil, fmt.Errorf("unknown component for custom template: %s %+v", name, c) } } diff --git a/pkg/skaffold/runner/new_test.go b/pkg/skaffold/runner/new_test.go index 0a246d9db50..7c6d07cc232 100644 --- a/pkg/skaffold/runner/new_test.go +++ b/pkg/skaffold/runner/new_test.go @@ -29,14 +29,14 @@ func TestCreateComponents(t *testing.T) { envExample, _ := tag.NewEnvTemplateTagger("test") tests := []struct { - description string - templateTagger *latest.TemplateTagger - expected map[string]tag.Tagger - shouldErr bool + description string + customTemplateTagger *latest.CustomTemplateTagger + expected map[string]tag.Tagger + shouldErr bool }{ { description: "correct component types", - templateTagger: &latest.TemplateTagger{ + customTemplateTagger: &latest.CustomTemplateTagger{ Components: []latest.TaggerComponent{ {Name: "FOO", Component: latest.TagPolicy{GitTagger: &latest.GitTagger{}}}, {Name: "FOE", Component: latest.TagPolicy{ShaTagger: &latest.ShaTagger{}}}, @@ -52,17 +52,17 @@ func TestCreateComponents(t *testing.T) { }, }, { - description: "tagTemplate is an invalid component", - templateTagger: &latest.TemplateTagger{ + description: "customTemplate is an invalid component", + customTemplateTagger: &latest.CustomTemplateTagger{ Components: []latest.TaggerComponent{ - {Name: "FOO", Component: latest.TagPolicy{TemplateTagger: &latest.TemplateTagger{Template: "test"}}}, + {Name: "FOO", Component: latest.TagPolicy{CustomTemplateTagger: &latest.CustomTemplateTagger{Template: "test"}}}, }, }, shouldErr: true, }, { description: "recurring names", - templateTagger: &latest.TemplateTagger{ + customTemplateTagger: &latest.CustomTemplateTagger{ Components: []latest.TaggerComponent{ {Name: "FOO", Component: latest.TagPolicy{GitTagger: &latest.GitTagger{}}}, {Name: "FOO", Component: latest.TagPolicy{GitTagger: &latest.GitTagger{}}}, @@ -72,7 +72,7 @@ func TestCreateComponents(t *testing.T) { }, { description: "unknown component", - templateTagger: &latest.TemplateTagger{ + customTemplateTagger: &latest.CustomTemplateTagger{ Components: []latest.TaggerComponent{ {Name: "FOO", Component: latest.TagPolicy{}}, }, @@ -82,7 +82,7 @@ func TestCreateComponents(t *testing.T) { } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - components, err := CreateComponents(test.templateTagger) + components, err := CreateComponents(test.customTemplateTagger) t.CheckErrorAndDeepEqual(test.shouldErr, err, len(test.expected), len(components)) for k, v := range test.expected { t.CheckTypeEquality(v, components[k]) diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 565b8625ccb..10a82a6ef1f 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -129,8 +129,8 @@ type TagPolicy struct { // DateTimeTagger *beta* tags images with the build timestamp. DateTimeTagger *DateTimeTagger `yaml:"dateTime,omitempty" yamltags:"oneOf=tag"` - // TemplateTagger *beta* tags images with a configurable template string *composed of other taggers*. - TemplateTagger *TemplateTagger `yaml:"tagTemplate,omitempty" yamltags:"oneOf=tag"` + // CustomTemplateTagger *beta* tags images with a configurable template string *composed of other taggers*. + CustomTemplateTagger *CustomTemplateTagger `yaml:"customTemplate,omitempty" yamltags:"oneOf=tag"` } // ShaTagger *beta* tags images with their sha256 digest. @@ -173,8 +173,8 @@ type DateTimeTagger struct { TimeZone string `yaml:"timezone,omitempty"` } -// TemplateTagger *beta* tags images with a configurable template string. -type TemplateTagger struct { +// CustomTemplateTagger *beta* tags images with a configurable template string. +type CustomTemplateTagger 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 provided components with those variables injected. @@ -185,12 +185,12 @@ type TemplateTagger struct { Components []TaggerComponent `yaml:"components,omitempty"` } -// TaggerComponent *beta* is a component of TemplateTagger. +// TaggerComponent *beta* is a component of CustomTemplateTagger. type TaggerComponent struct { // Name is an identifier for the component. Name string `yaml:"name,omitempty"` - // Component is a tagging strategy to be used in TemplateTagger. + // Component is a tagging strategy to be used in CustomTemplateTagger. Component TagPolicy `yaml:",inline"` } diff --git a/pkg/skaffold/schema/v2beta5/upgrade.go b/pkg/skaffold/schema/v2beta5/upgrade.go index de97f3093d3..b11e23c2991 100755 --- a/pkg/skaffold/schema/v2beta5/upgrade.go +++ b/pkg/skaffold/schema/v2beta5/upgrade.go @@ -25,7 +25,7 @@ import ( // Upgrade upgrades a configuration to the next version. // Config changes from v2beta5 to v2beta6 // 1. Additions: -// New structs TemplateTagger and TaggerComponent used by TagPolicy. +// New structs CustomTemplateTagger and TaggerComponent used by TagPolicy. func (c *SkaffoldConfig) Upgrade() (util.VersionedConfig, error) { var newConfig next.SkaffoldConfig pkgutil.CloneThroughJSON(c, &newConfig) From cde74c7752305c13e37c41c04252d66369e04618 Mon Sep 17 00:00:00 2001 From: Felix Date: Fri, 31 Jul 2020 16:26:03 -0400 Subject: [PATCH 050/138] generate schemas --- docs/content/en/schemas/v2beta6.json | 76 ++++++++++++++-------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/docs/content/en/schemas/v2beta6.json b/docs/content/en/schemas/v2beta6.json index 38c583d3ad5..74046c58b32 100755 --- a/docs/content/en/schemas/v2beta6.json +++ b/docs/content/en/schemas/v2beta6.json @@ -769,6 +769,36 @@ "description": "*beta* used to specify dependencies for an artifact built by a custom build script. Either `dockerfile` or `paths` should be specified for file watching to work as expected.", "x-intellij-html-description": "beta used to specify dependencies for an artifact built by a custom build script. Either dockerfile or paths should be specified for file watching to work as expected." }, + "CustomTemplateTagger": { + "required": [ + "template" + ], + "properties": { + "components": { + "items": { + "$ref": "#/definitions/TaggerComponent" + }, + "type": "array", + "description": "TaggerComponents that the template (see field above) can be executed against.", + "x-intellij-html-description": "TaggerComponents that the template (see field above) can be executed against." + }, + "template": { + "type": "string", + "description": "used to produce the image name and tag. See golang [text/template](https://golang.org/pkg/text/template/). The template is executed against the provided components with those variables injected.", + "x-intellij-html-description": "used to produce the image name and tag. See golang text/template. The template is executed against the provided components with those variables injected.", + "examples": [ + "{{.DATE}}" + ] + } + }, + "preferredOrder": [ + "template", + "components" + ], + "additionalProperties": false, + "description": "*beta* tags images with a configurable template string.", + "x-intellij-html-description": "beta tags images with a configurable template string." + }, "DateTimeTagger": { "properties": { "format": { @@ -2180,56 +2210,26 @@ }, { "properties": { + "customTemplate": { + "$ref": "#/definitions/CustomTemplateTagger", + "description": "*beta* tags images with a configurable template string *composed of other taggers*.", + "x-intellij-html-description": "beta tags images with a configurable template string composed of other taggers." + }, "name": { "type": "string", "description": "an identifier for the component.", "x-intellij-html-description": "an identifier for the component." - }, - "tagTemplate": { - "$ref": "#/definitions/TemplateTagger", - "description": "*beta* tags images with a configurable template string *composed of other taggers*.", - "x-intellij-html-description": "beta tags images with a configurable template string composed of other taggers." } }, "preferredOrder": [ "name", - "tagTemplate" + "customTemplate" ], "additionalProperties": false } ], - "description": "*beta* a component of TemplateTagger.", - "x-intellij-html-description": "beta a component of TemplateTagger." - }, - "TemplateTagger": { - "required": [ - "template" - ], - "properties": { - "components": { - "items": { - "$ref": "#/definitions/TaggerComponent" - }, - "type": "array", - "description": "TaggerComponents that the template (see field above) can be executed against.", - "x-intellij-html-description": "TaggerComponents that the template (see field above) can be executed against." - }, - "template": { - "type": "string", - "description": "used to produce the image name and tag. See golang [text/template](https://golang.org/pkg/text/template/). The template is executed against the provided components with those variables injected.", - "x-intellij-html-description": "used to produce the image name and tag. See golang text/template. The template is executed against the provided components with those variables injected.", - "examples": [ - "{{.DATE}}" - ] - } - }, - "preferredOrder": [ - "template", - "components" - ], - "additionalProperties": false, - "description": "*beta* tags images with a configurable template string.", - "x-intellij-html-description": "beta tags images with a configurable template string." + "description": "*beta* a component of CustomTemplateTagger.", + "x-intellij-html-description": "beta a component of CustomTemplateTagger." }, "TestCase": { "required": [ From 8a4e968d0ab4997f4bc6bea87b8e6cdfdb495c6e Mon Sep 17 00:00:00 2001 From: Felix Date: Fri, 31 Jul 2020 16:35:21 -0400 Subject: [PATCH 051/138] fix error --- pkg/skaffold/build/tag/tag_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/skaffold/build/tag/tag_test.go b/pkg/skaffold/build/tag/tag_test.go index 5546cf303bd..7d06790a304 100644 --- a/pkg/skaffold/build/tag/tag_test.go +++ b/pkg/skaffold/build/tag/tag_test.go @@ -17,7 +17,6 @@ limitations under the License. package tag import ( - "fmt" "testing" "time" @@ -103,7 +102,7 @@ func TestTagger_GenerateFullyQualifiedImageName(t *testing.T) { description: "customTemplate", imageName: "test", tagger: customTemplateExample, - expected: fmt.Sprintf("test:%s_%s", dateTimeExpected, "latest"), + expected: "test:" + dateTimeExpected + "_latest", }, } for _, test := range tests { From d3ad072ccea55173b9a1641c7f6310e182c0bbd1 Mon Sep 17 00:00:00 2001 From: tejal29 Date: Fri, 31 Jul 2020 13:37:05 -0700 Subject: [PATCH 052/138] purne empty namespaces --- pkg/skaffold/runner/runcontext/context.go | 7 +++++++ pkg/skaffold/runner/runcontext/context_test.go | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/pkg/skaffold/runner/runcontext/context.go b/pkg/skaffold/runner/runcontext/context.go index c96416d92b7..6a281d5c61e 100644 --- a/pkg/skaffold/runner/runcontext/context.go +++ b/pkg/skaffold/runner/runcontext/context.go @@ -29,6 +29,10 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" ) +const ( + emptyNamespace = "" +) + type RunContext struct { Opts config.SkaffoldOptions Cfg latest.Pipeline @@ -127,6 +131,9 @@ func (rc *RunContext) UpdateNamespaces(ns []string) { nsMap := map[string]bool{} for _, ns := range append(ns, rc.Namespaces...) { + if ns == emptyNamespace { + continue + } nsMap[ns] = true } diff --git a/pkg/skaffold/runner/runcontext/context_test.go b/pkg/skaffold/runner/runcontext/context_test.go index 5e1e9b15bbb..b1546202e38 100644 --- a/pkg/skaffold/runner/runcontext/context_test.go +++ b/pkg/skaffold/runner/runcontext/context_test.go @@ -59,6 +59,24 @@ func TestRunContext_UpdateNamespaces(t *testing.T) { namespaces: []string{}, expected: []string{}, }, + { + description: "update namespace when runcontext namespace has an empty string", + runContext: &RunContext{Namespaces: []string{""}}, + namespaces: []string{"another"}, + expected: []string{"another"}, + }, + { + description: "update namespace when namespace is empty string", + runContext: &RunContext{Namespaces: []string{"test"}}, + namespaces: []string{""}, + expected: []string{"test"}, + }, + { + description: "update namespace when namespace is empty string and runContext is empty", + runContext: &RunContext{Namespaces: []string{}}, + namespaces: []string{""}, + expected: []string{}, + }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { From 89774c5abf062f28a6e0e4d5478f763db6ecb7dd Mon Sep 17 00:00:00 2001 From: Felix Date: Fri, 31 Jul 2020 17:01:47 -0400 Subject: [PATCH 053/138] dont export customTemplateTagger --- pkg/skaffold/build/tag/custom_template.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/skaffold/build/tag/custom_template.go b/pkg/skaffold/build/tag/custom_template.go index 9be9049bf59..f18d8887bab 100644 --- a/pkg/skaffold/build/tag/custom_template.go +++ b/pkg/skaffold/build/tag/custom_template.go @@ -24,27 +24,27 @@ import ( "github.com/sirupsen/logrus" ) -// CustomTemplateTagger implements Tagger -type CustomTemplateTagger struct { +// customTemplateTagger implements Tagger +type customTemplateTagger struct { Template *template.Template Components map[string]Tagger } -// NewCustomTemplateTagger creates a new CustomTemplateTagger +// NewCustomTemplateTagger creates a new customTemplateTagger func NewCustomTemplateTagger(t string, components map[string]Tagger) (Tagger, error) { tmpl, err := ParseCustomTemplate(t) if err != nil { return nil, fmt.Errorf("parsing template: %w", err) } - return &CustomTemplateTagger{ + return &customTemplateTagger{ Template: tmpl, Components: components, }, nil } // GenerateTag generates a tag from a template referencing tagging strategies. -func (t *CustomTemplateTagger) GenerateTag(workingDir, imageName string) (string, error) { +func (t *customTemplateTagger) GenerateTag(workingDir, imageName string) (string, error) { customMap, err := t.EvaluateComponents(workingDir, imageName) if err != nil { return "", err @@ -60,7 +60,7 @@ func (t *CustomTemplateTagger) GenerateTag(workingDir, imageName string) (string } // EvaluateComponents creates a custom mapping of component names to their tagger string representation. -func (t *CustomTemplateTagger) EvaluateComponents(workingDir, imageName string) (map[string]string, error) { +func (t *customTemplateTagger) EvaluateComponents(workingDir, imageName string) (map[string]string, error) { customMap := map[string]string{} gitTagger, _ := NewGitCommit("", "") @@ -72,7 +72,7 @@ func (t *CustomTemplateTagger) EvaluateComponents(workingDir, imageName string) } for k, v := range t.Components { - if _, ok := v.(*CustomTemplateTagger); ok { + if _, ok := v.(*customTemplateTagger); ok { return nil, fmt.Errorf("invalid component specified in custom template: %v", v) } tag, err := v.GenerateTag(workingDir, imageName) From 8e6d86d02c30a0aece0f517184270ee07bcd1801 Mon Sep 17 00:00:00 2001 From: Brian de Alwis Date: Fri, 31 Jul 2020 17:26:43 -0400 Subject: [PATCH 054/138] Ask for notification of os.Interrupt --- cmd/skaffold/app/signals.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/skaffold/app/signals.go b/cmd/skaffold/app/signals.go index 876ca2cdcf8..d06c90c8bb3 100644 --- a/cmd/skaffold/app/signals.go +++ b/cmd/skaffold/app/signals.go @@ -26,6 +26,7 @@ import ( func catchCtrlC(cancel context.CancelFunc) { signals := make(chan os.Signal, 1) signal.Notify(signals, + os.Interrupt, syscall.SIGTERM, syscall.SIGINT, syscall.SIGPIPE, From 6efa05e3fc4551f7dcb25d5de0481562e680e5be Mon Sep 17 00:00:00 2001 From: tejal29 Date: Fri, 31 Jul 2020 14:45:53 -0700 Subject: [PATCH 055/138] dont print deployment status if no pod statuses --- pkg/skaffold/deploy/resource/deployment.go | 11 +++- .../deploy/resource/deployment_test.go | 61 ++++++++++++++++--- 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/pkg/skaffold/deploy/resource/deployment.go b/pkg/skaffold/deploy/resource/deployment.go index 6440056b9ad..c7029f07858 100644 --- a/pkg/skaffold/deploy/resource/deployment.go +++ b/pkg/skaffold/deploy/resource/deployment.go @@ -157,7 +157,11 @@ func (d *Deployment) ReportSinceLastUpdated() string { return "" } var result strings.Builder - result.WriteString(fmt.Sprintf("%s %s: %s", tabHeader, d, d.status)) + // Pod container statuses can be empty. + // This can happen when + // 1. No pods have been scheduled for the deployment + // 2. All containers are in running phase with no errors. + // In such case, avoid printing any status update for the deployment. for _, p := range d.pods { if s := p.ActionableError().Message; s != "" { result.WriteString(fmt.Sprintf("%s %s %s: %s\n", tab, tabHeader, p, s)) @@ -166,7 +170,10 @@ func (d *Deployment) ReportSinceLastUpdated() string { } } } - return result.String() + if result.String() == "" { + return "" + } + return fmt.Sprintf("%s %s: %s%s", tabHeader, d, d.status, result.String()) } func (d *Deployment) cleanupStatus(msg string) string { diff --git a/pkg/skaffold/deploy/resource/deployment_test.go b/pkg/skaffold/deploy/resource/deployment_test.go index 9fad404809c..4f904d9c69e 100644 --- a/pkg/skaffold/deploy/resource/deployment_test.go +++ b/pkg/skaffold/deploy/resource/deployment_test.go @@ -21,6 +21,7 @@ import ( "errors" "testing" + "github.com/GoogleContainerTools/skaffold/pkg/diag/validator" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/proto" @@ -193,25 +194,50 @@ func TestIsErrAndNotRetriable(t *testing.T) { } func TestReportSinceLastUpdated(t *testing.T) { + pods := map[string]validator.Resource{ + "foo": validator.NewResource( + "test", + "pod", + "foo", + "Pending", + proto.ActionableErr{ + ErrCode: proto.StatusCode_STATUSCHECK_IMAGE_PULL_ERR, + Message: "image cant be pulled"}, + []string{}, + ), + } var tests = []struct { description string ae proto.ActionableErr + pods map[string]validator.Resource expected string }{ { description: "updating an error status", - ae: proto.ActionableErr{Message: "cannot pull image"}, - expected: " - test-ns:deployment/test: cannot pull image", + ae: proto.ActionableErr{Message: "cannot pull image\n"}, + pods: pods, + expected: ` - test-ns:deployment/test: cannot pull image + - test:pod/foo: image cant be pulled +`, }, { description: "updating a non error status", - ae: proto.ActionableErr{Message: "waiting for container"}, - expected: " - test-ns:deployment/test: waiting for container", + ae: proto.ActionableErr{Message: "waiting for container\n"}, + pods: pods, + expected: ` - test-ns:deployment/test: waiting for container + - test:pod/foo: image cant be pulled +`, + }, + { + description: "updating a non error status", + ae: proto.ActionableErr{Message: "waiting for container\n"}, + expected: "", }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { dep := NewDeployment("test", "test-ns", 1) + dep.pods = test.pods dep.UpdateStatus(test.ae) t.CheckDeepEqual(test.expected, dep.ReportSinceLastUpdated()) t.CheckTrue(dep.status.changed) @@ -220,6 +246,18 @@ func TestReportSinceLastUpdated(t *testing.T) { } func TestReportSinceLastUpdatedMultipleTimes(t *testing.T) { + pods := map[string]validator.Resource{ + "foo": validator.NewResource( + "test", + "pod", + "foo", + "Pending", + proto.ActionableErr{ + ErrCode: proto.StatusCode_STATUSCHECK_IMAGE_PULL_ERR, + Message: "image cant be pulled"}, + []string{}, + ), + } var tests = []struct { description string statuses []string @@ -228,26 +266,31 @@ func TestReportSinceLastUpdatedMultipleTimes(t *testing.T) { }{ { description: "report first time should return status", - statuses: []string{"cannot pull image"}, + statuses: []string{"cannot pull image\n"}, reportStatusSeq: []bool{true}, - expected: " - test-ns:deployment/test: cannot pull image", + expected: ` - test-ns:deployment/test: cannot pull image + - test:pod/foo: image cant be pulled +`, }, { description: "report 2nd time should not return when same status", - statuses: []string{"cannot pull image", "cannot pull image"}, + statuses: []string{"cannot pull image\n", "cannot pull image\n"}, reportStatusSeq: []bool{true, true}, expected: "", }, { description: "report called after multiple changes but last status was not changed.", - statuses: []string{"cannot pull image", "changed but not reported", "changed but not reported", "changed but not reported"}, + statuses: []string{"cannot pull image\n", "changed but not reported\n", "changed but not reported\n", "changed but not reported\n"}, reportStatusSeq: []bool{true, false, false, true}, - expected: " - test-ns:deployment/test: changed but not reported", + expected: ` - test-ns:deployment/test: changed but not reported + - test:pod/foo: image cant be pulled +`, }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { dep := NewDeployment("test", "test-ns", 1) + dep.pods = pods var actual string for i, status := range test.statuses { // update to same status From 9b7dd76174386163786ec1153fd1ca0342923bdb Mon Sep 17 00:00:00 2001 From: tejal29 Date: Fri, 31 Jul 2020 16:06:51 -0700 Subject: [PATCH 056/138] fix tests --- pkg/skaffold/deploy/resource/deployment.go | 4 ++-- pkg/skaffold/deploy/status_check_test.go | 16 +++++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/pkg/skaffold/deploy/resource/deployment.go b/pkg/skaffold/deploy/resource/deployment.go index c7029f07858..c5ec5d40de5 100644 --- a/pkg/skaffold/deploy/resource/deployment.go +++ b/pkg/skaffold/deploy/resource/deployment.go @@ -43,8 +43,8 @@ const ( ) var ( - msgKubectlKilled = "kubectl rollout status command interrupted" - MsgKubectlConnection = "kubectl connection error" + msgKubectlKilled = "kubectl rollout status command interrupted\n" + MsgKubectlConnection = "kubectl connection error\n" nonRetryContainerErrors = map[proto.StatusCode]struct{}{ proto.StatusCode_STATUSCHECK_IMAGE_PULL_ERR: {}, diff --git a/pkg/skaffold/deploy/status_check_test.go b/pkg/skaffold/deploy/status_check_test.go index 34deb54ce84..79ca0fd1549 100644 --- a/pkg/skaffold/deploy/status_check_test.go +++ b/pkg/skaffold/deploy/status_check_test.go @@ -392,12 +392,15 @@ func TestPrintStatus(t *testing.T) { proto.ActionableErr{ErrCode: proto.StatusCode_STATUSCHECK_SUCCESS}, ), withStatus( - resource.NewDeployment("r2", "test", 1), + resource.NewDeployment("r2", "test", 1). + WithPodStatuses([]proto.StatusCode{proto.StatusCode_STATUSCHECK_IMAGE_PULL_ERR}), proto.ActionableErr{ErrCode: proto.StatusCode_STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING, - Message: "pending"}, + Message: "pending\n"}, ), }, - expectedOut: " - test:deployment/r2: pending\n", + expectedOut: ` - test:deployment/r2: pending + - test:pod/foo: pod failed +`, }, { description: "multiple resources 1 not complete and retry-able error", @@ -407,13 +410,16 @@ func TestPrintStatus(t *testing.T) { proto.ActionableErr{ErrCode: proto.StatusCode_STATUSCHECK_SUCCESS}, ), withStatus( - resource.NewDeployment("r2", "test", 1), + resource.NewDeployment("r2", "test", 1). + WithPodStatuses([]proto.StatusCode{proto.StatusCode_STATUSCHECK_IMAGE_PULL_ERR}), proto.ActionableErr{ ErrCode: proto.StatusCode_STATUSCHECK_KUBECTL_CONNECTION_ERR, Message: resource.MsgKubectlConnection}, ), }, - expectedOut: " - test:deployment/r2: kubectl connection error\n", + expectedOut: ` - test:deployment/r2: kubectl connection error + - test:pod/foo: pod failed +`, }, } From dc02ca287efd37502bd17f7a57bc332c3b732878 Mon Sep 17 00:00:00 2001 From: tejal29 Date: Fri, 31 Jul 2020 16:58:44 -0700 Subject: [PATCH 057/138] Release v1.13.0 --- CHANGELOG.md | 103 ++++++++++++++++++ examples/bazel/skaffold.yaml | 2 +- examples/buildpacks-java/skaffold.yaml | 2 +- examples/buildpacks-node/skaffold.yaml | 2 +- examples/buildpacks-python/skaffold.yaml | 2 +- examples/buildpacks/skaffold.yaml | 2 +- examples/custom/skaffold.yaml | 2 +- examples/gcb-kaniko/skaffold.yaml | 2 +- examples/generate-pipeline/skaffold.yaml | 2 +- .../getting-started-kustomize/skaffold.yaml | 2 +- examples/getting-started/skaffold.yaml | 2 +- examples/google-cloud-build/skaffold.yaml | 2 +- .../skaffold.yaml | 8 +- examples/helm-deployment/skaffold.yaml | 2 +- examples/hot-reload/skaffold.yaml | 2 +- examples/jib-gradle/skaffold.yaml | 2 +- examples/jib-multimodule/skaffold.yaml | 2 +- examples/jib-sync/skaffold-gradle.yaml | 2 +- examples/jib-sync/skaffold-maven.yaml | 2 +- examples/jib/skaffold.yaml | 2 +- examples/kaniko/skaffold.yaml | 2 +- .../kustomize/skaffold-kustomize-args.yaml | 2 +- examples/kustomize/skaffold.yaml | 2 +- examples/microservices/skaffold.yaml | 2 +- examples/nodejs/skaffold.yaml | 2 +- examples/profile-patches/skaffold.yaml | 2 +- examples/profiles/skaffold.yaml | 2 +- examples/react-reload/skaffold.yaml | 2 +- examples/ruby/skaffold.yaml | 2 +- examples/structure-tests/skaffold.yaml | 2 +- .../skaffold.yaml | 2 +- examples/templated-fields/skaffold.yaml | 2 +- pkg/skaffold/schema/latest/config.go | 2 +- 33 files changed, 139 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40aacab3434..431c7ac15ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,106 @@ +# v1.13.0 Release - 07/30/2020 + +**Linux** +`curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v1.13.0/skaffold-linux-amd64 && chmod +x skaffold && sudo mv skaffold /usr/local/bin` + +**macOS** +`curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v1.13.0/skaffold-darwin-amd64 && chmod +x skaffold && sudo mv skaffold /usr/local/bin` + +**Windows** + https://storage.googleapis.com/skaffold/releases/v1.13.0/skaffold-windows-amd64.exe + +**Docker image** +`gcr.io/k8s-skaffold/skaffold:v1.13.0` + +Note: This release comes with a new config version, `v2beta6`. To upgrade your skaffold.yaml, use `skaffold fix`. If you choose not to upgrade, skaffold will auto-upgrade as best as it can. + + +Highlights: +* Skaffold now supports muting both build, test, and deploy logs through the `--mute-logs` flag for more succinct output. +* All extraneous labels added to deployed resources are now added as annotations in Kubernetes. +* Skaffold now supports a new tagging strategy, `customTemplate`, allowing combinations of multiple tagging strategies. +* Specification of the {{.IMAGE_NAME}} component of the `envTemplate` tagger has been deprecated. +* Many other usability fixes and updates in this release! + + +New Features: +* Add helper to create log files [#4563](https://github.com/GoogleContainerTools/skaffold/pull/4563) +* Tag template tagger [#4567](https://github.com/GoogleContainerTools/skaffold/pull/4567) +* Add suppress-logs flag [#4530](https://github.com/GoogleContainerTools/skaffold/pull/4530) +* Add `deploy.logs` section to skaffold.yaml [#4509](https://github.com/GoogleContainerTools/skaffold/pull/4509) +* Allow deeply nested property definition for Helm properties [#4511](https://github.com/GoogleContainerTools/skaffold/pull/4511) +* Add Agones custom kinds to the Allow-List [#4488](https://github.com/GoogleContainerTools/skaffold/pull/4488) +* Support deprecated extensions/v1beta1 workload resources [#4478](https://github.com/GoogleContainerTools/skaffold/pull/4478) + + +Fixes: +* Use alternative service port-forwarding scheme [#4590](https://github.com/GoogleContainerTools/skaffold/pull/4590) +* Ignore "" namespaces in collectHelmReleasesNamespaces [#4568](https://github.com/GoogleContainerTools/skaffold/pull/4568) +* Wait for pending deletions to complete before a deploy [#4531](https://github.com/GoogleContainerTools/skaffold/pull/4531) +* SKAFFOLD_UPDATE_CHECK should also be a global flag [#4510](https://github.com/GoogleContainerTools/skaffold/pull/4510) +* fix: remove the dev override of the force flag [#4513](https://github.com/GoogleContainerTools/skaffold/pull/4513) +* Error on invalid artifact workspace [#4492](https://github.com/GoogleContainerTools/skaffold/pull/4492) + + +Updates: +* Log when values are taken from global config file [#4566](https://github.com/GoogleContainerTools/skaffold/pull/4566) +* Muted test logs [#4595](https://github.com/GoogleContainerTools/skaffold/pull/4595) +* Support short build logs [#4528](https://github.com/GoogleContainerTools/skaffold/pull/4528) +* Fail when k8s client can’t be obtained [#4584](https://github.com/GoogleContainerTools/skaffold/pull/4584) +* Deprecating EnvTemplate's use of {{.IMAGE_NAME}} [#4533](https://github.com/GoogleContainerTools/skaffold/pull/4533) +* Get digest of multi-arch images [#4475](https://github.com/GoogleContainerTools/skaffold/pull/4475) +* Reduce volume of debug-level logging [#4552](https://github.com/GoogleContainerTools/skaffold/pull/4552) +* Remove labels from builders and deployers [#4499](https://github.com/GoogleContainerTools/skaffold/pull/4499) +* Update k3d cli 'load image' to 'image import' (#4498) [#4507](https://github.com/GoogleContainerTools/skaffold/pull/4507) +* Disable update check and survey prompt in non-interactive mode [#4508](https://github.com/GoogleContainerTools/skaffold/pull/4508) +* Map container status PodInitializing to STATUSCHECK_SUCCESS [#4471](https://github.com/GoogleContainerTools/skaffold/pull/4471) +* Use runCtx.Namespaces to get deployments for status checks [#4460](https://github.com/GoogleContainerTools/skaffold/pull/4460) + + +Dependency Updates: +* Update pack to v0.12.0 [#4474](https://github.com/GoogleContainerTools/skaffold/pull/4474) +* Include k3d 3.0.0 in Skaffold image [#4545](https://github.com/GoogleContainerTools/skaffold/pull/4545) +* Update cross compilation image [#4543](https://github.com/GoogleContainerTools/skaffold/pull/4543) +* Upgrade go-containerregistry to v0.1.1 [#4476](https://github.com/GoogleContainerTools/skaffold/pull/4476) + + +Docs Updates: +* Fix documentation for Helm `artifactOverride` [#4503](https://github.com/GoogleContainerTools/skaffold/pull/4503) +* Fail fast and point to docs for 'skaffold init' on helm projects [#4396](https://github.com/GoogleContainerTools/skaffold/pull/4396) +* Fix example for generate-pipeline to use "latest" as image tag [#4458](https://github.com/GoogleContainerTools/skaffold/pull/4458) + + +Huge thanks goes out to all of our contributors for this release: + +- Alex Lewis +- Alexander Shirobokov +- Andreas Sommer +- Andrew den Hertog +- Appu Goundan +- Balint Pato +- Brian de Alwis +- Chanseok Oh +- Chris Ge +- Daniel Sel +- David Gageot +- Felix Tran +- Gaurav +- Gergo Tolnai +- Keerthan Jaic +- Kent Hua +- Lennox Stevenson +- Marlon Gamez +- Miklos Kiss +- Nicholas Hawkes +- Nick Kubala +- Nils Breunese +- Oliver Hughes +- Paul Vollmer +- Sarmad Abualkaz +- Stefan Büringer +- Tejal Desai +- Zhou Wenzong + # v1.12.1 Release - 07/14/2020 **Linux** diff --git a/examples/bazel/skaffold.yaml b/examples/bazel/skaffold.yaml index 5e6e6b541f5..a3a28920935 100644 --- a/examples/bazel/skaffold.yaml +++ b/examples/bazel/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/buildpacks-java/skaffold.yaml b/examples/buildpacks-java/skaffold.yaml index 9f0c14f1138..ed374ab601d 100644 --- a/examples/buildpacks-java/skaffold.yaml +++ b/examples/buildpacks-java/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/buildpacks-node/skaffold.yaml b/examples/buildpacks-node/skaffold.yaml index d82594778a2..2b7f0951102 100644 --- a/examples/buildpacks-node/skaffold.yaml +++ b/examples/buildpacks-node/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/buildpacks-python/skaffold.yaml b/examples/buildpacks-python/skaffold.yaml index 8f3b308fd5a..5fbbafb9f54 100644 --- a/examples/buildpacks-python/skaffold.yaml +++ b/examples/buildpacks-python/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/buildpacks/skaffold.yaml b/examples/buildpacks/skaffold.yaml index a53465c2d76..d78c0a32950 100644 --- a/examples/buildpacks/skaffold.yaml +++ b/examples/buildpacks/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/custom/skaffold.yaml b/examples/custom/skaffold.yaml index 39c483f76ce..9fab8ee3963 100644 --- a/examples/custom/skaffold.yaml +++ b/examples/custom/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/gcb-kaniko/skaffold.yaml b/examples/gcb-kaniko/skaffold.yaml index 1bc1ea92ba0..a6fd38405ce 100644 --- a/examples/gcb-kaniko/skaffold.yaml +++ b/examples/gcb-kaniko/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: googleCloudBuild: diff --git a/examples/generate-pipeline/skaffold.yaml b/examples/generate-pipeline/skaffold.yaml index a8c067666f5..5716074437a 100644 --- a/examples/generate-pipeline/skaffold.yaml +++ b/examples/generate-pipeline/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/getting-started-kustomize/skaffold.yaml b/examples/getting-started-kustomize/skaffold.yaml index 0ff31b7edae..14229de67ab 100644 --- a/examples/getting-started-kustomize/skaffold.yaml +++ b/examples/getting-started-kustomize/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config metadata: name: getting-started-kustomize diff --git a/examples/getting-started/skaffold.yaml b/examples/getting-started/skaffold.yaml index cbf95a1a7a6..36a019559aa 100644 --- a/examples/getting-started/skaffold.yaml +++ b/examples/getting-started/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/google-cloud-build/skaffold.yaml b/examples/google-cloud-build/skaffold.yaml index e184470c972..dc50e83541e 100644 --- a/examples/google-cloud-build/skaffold.yaml +++ b/examples/google-cloud-build/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: googleCloudBuild: diff --git a/examples/helm-deployment-dependencies/skaffold.yaml b/examples/helm-deployment-dependencies/skaffold.yaml index bc03bffe500..ffb340c5aa8 100644 --- a/examples/helm-deployment-dependencies/skaffold.yaml +++ b/examples/helm-deployment-dependencies/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: tagPolicy: @@ -16,7 +16,8 @@ deploy: skipBuildDependencies: true # Skip helm dep build artifactOverrides: image: skaffold-helm - "skaffold-helm-subchart.image": skaffold-helm + skaffold-helm-subchart: + image: skaffold-helm #recreatePods will pass --recreate-pods to helm upgrade #recreatePods: true #overrides builds an override values.yaml file to run with the helm deploy @@ -25,4 +26,5 @@ deploy: # key: someValue #setValues get appended to the helm deploy with --set. #setValues: - #some.key: someValue + # some: + # key: someValue diff --git a/examples/helm-deployment/skaffold.yaml b/examples/helm-deployment/skaffold.yaml index 11a765e449e..ee904081d92 100644 --- a/examples/helm-deployment/skaffold.yaml +++ b/examples/helm-deployment/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/hot-reload/skaffold.yaml b/examples/hot-reload/skaffold.yaml index 7f37ce5ca7e..e11cc149ac4 100644 --- a/examples/hot-reload/skaffold.yaml +++ b/examples/hot-reload/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/jib-gradle/skaffold.yaml b/examples/jib-gradle/skaffold.yaml index 9e4044a66f0..11973a4ee48 100644 --- a/examples/jib-gradle/skaffold.yaml +++ b/examples/jib-gradle/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/jib-multimodule/skaffold.yaml b/examples/jib-multimodule/skaffold.yaml index 59a15b369ac..6f4e4284630 100644 --- a/examples/jib-multimodule/skaffold.yaml +++ b/examples/jib-multimodule/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/jib-sync/skaffold-gradle.yaml b/examples/jib-sync/skaffold-gradle.yaml index e618373b131..7250cccc165 100644 --- a/examples/jib-sync/skaffold-gradle.yaml +++ b/examples/jib-sync/skaffold-gradle.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/jib-sync/skaffold-maven.yaml b/examples/jib-sync/skaffold-maven.yaml index 58c5f90b810..a1aff752bc5 100644 --- a/examples/jib-sync/skaffold-maven.yaml +++ b/examples/jib-sync/skaffold-maven.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/jib/skaffold.yaml b/examples/jib/skaffold.yaml index 4d2d0fc9bc6..87419a7533f 100644 --- a/examples/jib/skaffold.yaml +++ b/examples/jib/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/kaniko/skaffold.yaml b/examples/kaniko/skaffold.yaml index 8df9bd8bceb..f7d37aa9627 100644 --- a/examples/kaniko/skaffold.yaml +++ b/examples/kaniko/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/kustomize/skaffold-kustomize-args.yaml b/examples/kustomize/skaffold-kustomize-args.yaml index 1731303f3c4..3c475b101d7 100644 --- a/examples/kustomize/skaffold-kustomize-args.yaml +++ b/examples/kustomize/skaffold-kustomize-args.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config deploy: kustomize: diff --git a/examples/kustomize/skaffold.yaml b/examples/kustomize/skaffold.yaml index 0e6a43e3a59..276e66ab73d 100644 --- a/examples/kustomize/skaffold.yaml +++ b/examples/kustomize/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config deploy: kustomize: {} diff --git a/examples/microservices/skaffold.yaml b/examples/microservices/skaffold.yaml index 7370643afc0..0610fa1e5c6 100644 --- a/examples/microservices/skaffold.yaml +++ b/examples/microservices/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/nodejs/skaffold.yaml b/examples/nodejs/skaffold.yaml index 9d743d24815..880aad983cc 100644 --- a/examples/nodejs/skaffold.yaml +++ b/examples/nodejs/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: diff --git a/examples/profile-patches/skaffold.yaml b/examples/profile-patches/skaffold.yaml index 88a425ad40b..2193fb2909f 100644 --- a/examples/profile-patches/skaffold.yaml +++ b/examples/profile-patches/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: # only build and deploy "base-service" on main profile diff --git a/examples/profiles/skaffold.yaml b/examples/profiles/skaffold.yaml index 10de3653cfc..2d120284cc6 100644 --- a/examples/profiles/skaffold.yaml +++ b/examples/profiles/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: # only build and deploy "world-service" on main profile diff --git a/examples/react-reload/skaffold.yaml b/examples/react-reload/skaffold.yaml index 68ca9cf7c1d..00074bdf8be 100644 --- a/examples/react-reload/skaffold.yaml +++ b/examples/react-reload/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/ruby/skaffold.yaml b/examples/ruby/skaffold.yaml index f4d1439c8f4..cc716d78fb0 100644 --- a/examples/ruby/skaffold.yaml +++ b/examples/ruby/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/structure-tests/skaffold.yaml b/examples/structure-tests/skaffold.yaml index 1e504414ada..5fafb7d386c 100644 --- a/examples/structure-tests/skaffold.yaml +++ b/examples/structure-tests/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/tagging-with-environment-variables/skaffold.yaml b/examples/tagging-with-environment-variables/skaffold.yaml index 699293d5522..edfa4fdee9d 100644 --- a/examples/tagging-with-environment-variables/skaffold.yaml +++ b/examples/tagging-with-environment-variables/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config build: artifacts: diff --git a/examples/templated-fields/skaffold.yaml b/examples/templated-fields/skaffold.yaml index 362fe218132..91c54e99358 100644 --- a/examples/templated-fields/skaffold.yaml +++ b/examples/templated-fields/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta5 +apiVersion: skaffold/v2beta6 kind: Config metadata: name: my-app diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 10a82a6ef1f..9ead532f4dd 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -22,7 +22,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util" ) -// This config version is not yet released, it is SAFE TO MODIFY the structs in this file. +// !!! WARNING !!! This config version is already released, please DO NOT MODIFY the structs in this file. const Version string = "skaffold/v2beta6" // NewSkaffoldConfig creates a SkaffoldConfig From 4ba07bb9878c845c962686e297538c05acd7992d Mon Sep 17 00:00:00 2001 From: gsquared94 Date: Mon, 3 Aug 2020 20:08:03 +0530 Subject: [PATCH 058/138] Install specific Bazel version and Skaffold version for nightly linux and darwin integration tests. --- .github/workflows/integration-darwin.yml | 20 ++++++++++++++++++-- .github/workflows/integration-linux.yml | 14 +++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/.github/workflows/integration-darwin.yml b/.github/workflows/integration-darwin.yml index 15b2a9e6bca..02ad5fb4702 100644 --- a/.github/workflows/integration-darwin.yml +++ b/.github/workflows/integration-darwin.yml @@ -12,12 +12,13 @@ jobs: strategy: matrix: kustomize_version: [3.5.4] + bazel_version: [3.2.0] ko_version: [0.4.0] kompose_version: [1.21.0] gcloud_sdk_version: [276.0.0] kubectl_version: [1.18.0] container_structure_tests_version: [1.8.0] - minikube_version: [1.11.0] + minikube_version: [1.12.1] integration_test_partitions: [0, 1, 2, 3] steps: @@ -30,10 +31,21 @@ jobs: - name: Check out code into the Go module directory uses: actions/checkout@v2 + - name: Skaffold binary version + run: | + echo ::set-env name=SKAFFOLD_VERSION::"$(git log --format="%H" -n 1)" + + - name: Install Bazel + run: | + curl -fLO "https://github.com/bazelbuild/bazel/releases/download/${{ matrix.bazel_version }}/bazel-${{ matrix.bazel_version }}-installer-darwin-x86_64.sh" + chmod +x "bazel-${{ matrix.bazel_version }}-installer-darwin-x86_64.sh" + ./bazel-${{ matrix.bazel_version }}-installer-darwin-x86_64.sh --user + - name: Install Kustomize run: | wget -O kustomize.tar.gz https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v${{ matrix.kustomize_version }}/kustomize_v${{ matrix.kustomize_version }}_darwin_amd64.tar.gz sudo tar -xvf kustomize.tar.gz -C /usr/local/bin/ + echo ::add-path::${HOME}/bin - name: Install Ko run: | @@ -72,6 +84,10 @@ jobs: gcloud auth configure-docker echo '{}' > ${HOME}/.docker/config.json + - name: Check VBoxManage installed + run: | + /usr/local/bin/VBoxManage list hostinfo + - name: Install Minikube and start cluster run: | curl -Lo minikube https://storage.googleapis.com/minikube/releases/v${{ matrix.minikube_version }}/minikube-darwin-amd64 @@ -85,7 +101,7 @@ jobs: - name: Install Skaffold release binary run: | - curl -Lo skaffold https://storage.googleapis.com/skaffold/builds/latest/skaffold-darwin-amd64 + curl -Lo skaffold https://storage.googleapis.com/skaffold/builds/${{ env.SKAFFOLD_VERSION }}/skaffold-darwin-amd64 sudo install skaffold /usr/local/bin/skaffold - name: Run integration tests diff --git a/.github/workflows/integration-linux.yml b/.github/workflows/integration-linux.yml index a209f813f38..03874b3eaa1 100644 --- a/.github/workflows/integration-linux.yml +++ b/.github/workflows/integration-linux.yml @@ -13,6 +13,7 @@ jobs: matrix: kustomize_version: [3.5.4] ko_version: [0.4.0] + bazel_version: [3.2.0] kompose_version: [1.21.0] gcloud_sdk_version: [276.0.0] container_structure_tests_version: [1.8.0] @@ -29,6 +30,17 @@ jobs: - name: Check out code into the Go module directory uses: actions/checkout@v2 + - name: Skaffold binary version + run: | + echo ::set-env name=SKAFFOLD_VERSION::"$(git log --format="%H" -n 1)" + + - name: Install Bazel + run: | + curl -fLO "https://github.com/bazelbuild/bazel/releases/download/${{ matrix.bazel_version }}/bazel-${{ matrix.bazel_version }}-installer-linux-x86_64.sh" + chmod +x bazel-${{ matrix.bazel_version }}-installer-linux-x86_64.sh + ./bazel-${{ matrix.bazel_version }}-installer-linux-x86_64.sh --user + echo ::add-path::${HOME}/bin + - name: Install Kustomize run: | wget -O kustomize.tar.gz https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v${{ matrix.kustomize_version }}/kustomize_v${{ matrix.kustomize_version }}_linux_amd64.tar.gz @@ -76,7 +88,7 @@ jobs: - name: Install Skaffold release binary run: | - curl -Lo skaffold https://storage.googleapis.com/skaffold/builds/latest/skaffold-linux-amd64 + curl -Lo skaffold https://storage.googleapis.com/skaffold/builds/${{ env.SKAFFOLD_VERSION }}/skaffold-linux-amd64 sudo install skaffold /usr/local/bin/skaffold - name: Run integration tests From 3593d4b4f9f68516884f65cefbde926b86ee25c7 Mon Sep 17 00:00:00 2001 From: gsquared94 Date: Mon, 3 Aug 2020 20:09:38 +0530 Subject: [PATCH 059/138] Install specific Bazel version and Skaffold version for nightly linux and darwin integration tests. --- .github/workflows/integration-darwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-darwin.yml b/.github/workflows/integration-darwin.yml index 02ad5fb4702..5d9dea38c52 100644 --- a/.github/workflows/integration-darwin.yml +++ b/.github/workflows/integration-darwin.yml @@ -40,12 +40,12 @@ jobs: curl -fLO "https://github.com/bazelbuild/bazel/releases/download/${{ matrix.bazel_version }}/bazel-${{ matrix.bazel_version }}-installer-darwin-x86_64.sh" chmod +x "bazel-${{ matrix.bazel_version }}-installer-darwin-x86_64.sh" ./bazel-${{ matrix.bazel_version }}-installer-darwin-x86_64.sh --user + echo ::add-path::${HOME}/bin - name: Install Kustomize run: | wget -O kustomize.tar.gz https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v${{ matrix.kustomize_version }}/kustomize_v${{ matrix.kustomize_version }}_darwin_amd64.tar.gz sudo tar -xvf kustomize.tar.gz -C /usr/local/bin/ - echo ::add-path::${HOME}/bin - name: Install Ko run: | From 69544728bb97d2e17289de0e850a575e62562255 Mon Sep 17 00:00:00 2001 From: Nick Kubala Date: Mon, 3 Aug 2020 13:20:41 -0700 Subject: [PATCH 060/138] Fix CustomTagger docs --- docs/content/en/docs/references/yaml/main.js | 11 ++++-- docs/content/en/schemas/v2beta6.json | 39 ++++++++++++++++++++ hack/schemas/main.go | 19 ++++++++-- pkg/skaffold/schema/latest/config.go | 2 +- pkg/skaffold/yamltags/tags.go | 22 +++++++++++ 5 files changed, 85 insertions(+), 8 deletions(-) diff --git a/docs/content/en/docs/references/yaml/main.js b/docs/content/en/docs/references/yaml/main.js index 6f850275472..fc9e106659e 100644 --- a/docs/content/en/docs/references/yaml/main.js +++ b/docs/content/en/docs/references/yaml/main.js @@ -193,9 +193,14 @@ function* template(definitions, parentDefinition, ref, ident, parent) { // This definition is an array if (definition.items && definition.items.$ref) { - yield html` - ${template(definitions, definition, definition.items.$ref, ident + 1, path)} - `; + // don't infinitely recurse into nested tagger components + if (definition.items.$ref === "#/definitions/TaggerComponent") { + yield html ``; + } else { + yield html` + ${template(definitions, definition, definition.items.$ref, ident + 1, path)} + `; + } } } } diff --git a/docs/content/en/schemas/v2beta6.json b/docs/content/en/schemas/v2beta6.json index 74046c58b32..32438e34588 100755 --- a/docs/content/en/schemas/v2beta6.json +++ b/docs/content/en/schemas/v2beta6.json @@ -2117,6 +2117,45 @@ "description": "specifies which local files to sync to remote folders.", "x-intellij-html-description": "specifies which local files to sync to remote folders." }, + "TagPolicy": { + "properties": { + "customTemplate": { + "$ref": "#/definitions/CustomTemplateTagger", + "description": "*beta* tags images with a configurable template string *composed of other taggers*.", + "x-intellij-html-description": "beta tags images with a configurable template string composed of other taggers." + }, + "dateTime": { + "$ref": "#/definitions/DateTimeTagger", + "description": "*beta* tags images with the build timestamp.", + "x-intellij-html-description": "beta tags images with the build timestamp." + }, + "envTemplate": { + "$ref": "#/definitions/EnvTemplateTagger", + "description": "*beta* tags images with a configurable template string.", + "x-intellij-html-description": "beta tags images with a configurable template string." + }, + "gitCommit": { + "$ref": "#/definitions/GitTagger", + "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." + }, + "sha256": { + "$ref": "#/definitions/ShaTagger", + "description": "*beta* tags images with their sha256 digest.", + "x-intellij-html-description": "beta tags images with their sha256 digest." + } + }, + "preferredOrder": [ + "gitCommit", + "sha256", + "envTemplate", + "dateTime", + "customTemplate" + ], + "additionalProperties": false, + "description": "contains all the configuration for the tagging step.", + "x-intellij-html-description": "contains all the configuration for the tagging step." + }, "TaggerComponent": { "anyOf": [ { diff --git a/hack/schemas/main.go b/hack/schemas/main.go index ecefc3a9948..3e02cb64a86 100644 --- a/hack/schemas/main.go +++ b/hack/schemas/main.go @@ -77,8 +77,9 @@ type Definition struct { Examples []string `json:"examples,omitempty"` Enum []string `json:"enum,omitempty"` - inlines []*Definition - tags string + inlines []*Definition + tags string + skipTrim bool } func main() { @@ -238,7 +239,8 @@ func (g *schemaGenerator) newDefinition(name string, t ast.Expr, comment string, if strings.Contains(field.Tag.Value, "inline") { def.PreferredOrder = append(def.PreferredOrder, "") def.inlines = append(def.inlines, &Definition{ - Ref: defPrefix + field.Type.(*ast.Ident).Name, + Ref: defPrefix + field.Type.(*ast.Ident).Name, + skipTrim: strings.Contains(field.Tag.Value, "skipTrim"), }) continue } @@ -354,6 +356,9 @@ func (g *schemaGenerator) Apply(inputPath string) ([]byte, error) { } for _, inlineStruct := range def.inlines { + if inlineStruct.skipTrim { + continue + } ref := strings.TrimPrefix(inlineStruct.Ref, defPrefix) inlines = append(inlines, ref) } @@ -435,7 +440,13 @@ func (g *schemaGenerator) Apply(inputPath string) ([]byte, error) { } for _, ref := range inlines { - delete(definitions, ref) + existingDef, ok := definitions[ref] + if !ok { + continue + } + if !existingDef.skipTrim { + delete(definitions, ref) + } } schema := Schema{ diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 9ead532f4dd..d5cc062d6a1 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -191,7 +191,7 @@ type TaggerComponent struct { Name string `yaml:"name,omitempty"` // Component is a tagging strategy to be used in CustomTemplateTagger. - Component TagPolicy `yaml:",inline"` + Component TagPolicy `yaml:",inline" yamltags:"skipTrim"` } // BuildType contains the specific implementation and parameters needed diff --git a/pkg/skaffold/yamltags/tags.go b/pkg/skaffold/yamltags/tags.go index b3c4c7cbbaf..35a4869d2b9 100644 --- a/pkg/skaffold/yamltags/tags.go +++ b/pkg/skaffold/yamltags/tags.go @@ -72,6 +72,10 @@ func processTags(yamltags string, val reflect.Value, parentStruct reflect.Value, Field: field, Parent: parentStruct, } + case "skipTrim": + yt = &skipTrimTag{ + Field: field, + } default: logrus.Panicf("unknown yaml tag in %s", yamltags) } @@ -166,6 +170,24 @@ func (oot *oneOfTag) Process(val reflect.Value) error { return nil } +type skipTrimTag struct { + Field reflect.StructField +} + +func (tag *skipTrimTag) Load(s []string) error { + return nil +} + +func (tag *skipTrimTag) Process(val reflect.Value) error { + if isZeroValue(val) { + if tags, ok := tag.Field.Tag.Lookup("yaml"); ok { + return fmt.Errorf("skipTrim value not set: %s", strings.Split(tags, ",")[0]) + } + return fmt.Errorf("skipTrim value not set: %s", tag.Field.Name) + } + return nil +} + func isZeroValue(val reflect.Value) bool { if val.Kind() == reflect.Invalid { return true From 0ea7ea81e5823baab9cf54c231ff7e8bee1575f7 Mon Sep 17 00:00:00 2001 From: Thomas Stromberg Date: Tue, 4 Aug 2020 10:27:26 -0700 Subject: [PATCH 061/138] Clarify 'survey' command text --- cmd/skaffold/app/cmd/survey.go | 2 +- pkg/skaffold/survey/survey.go | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cmd/skaffold/app/cmd/survey.go b/cmd/skaffold/app/cmd/survey.go index a65f045e218..cc4c8214d96 100644 --- a/cmd/skaffold/app/cmd/survey.go +++ b/cmd/skaffold/app/cmd/survey.go @@ -27,7 +27,7 @@ import ( func NewCmdSurvey() *cobra.Command { return NewCmd("survey"). - WithDescription("Show Skaffold survey url"). + WithDescription("Opens a web browser to fill out the Skaffold survey"). NoArgs(showSurvey) } diff --git a/pkg/skaffold/survey/survey.go b/pkg/skaffold/survey/survey.go index 476ff2c88b6..ef85b02b65a 100644 --- a/pkg/skaffold/survey/survey.go +++ b/pkg/skaffold/survey/survey.go @@ -37,9 +37,12 @@ const ( var ( Form = fmt.Sprintf(`Thank you for offering your feedback on Skaffold! Understanding your experiences and opinions helps us make Skaffold better for you and other users. - Our survey can be found here: %s -To permanently disable the survey prompt, run: +Skaffold will now attempt to open the survey in your default web browser. You may also manually open it using this link: + +%s + +Tip: To permanently disable the survey prompt, run: skaffold config set --survey --global disable-prompt true`, URL) // for testing From 6a016f009097c5b506456a03fed6e5952cb74643 Mon Sep 17 00:00:00 2001 From: Thomas Stromberg Date: Tue, 4 Aug 2020 10:32:37 -0700 Subject: [PATCH 062/138] Update markdown docs via hack/generate-man.sh --- docs/content/en/docs/references/cli/_index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/en/docs/references/cli/_index.md b/docs/content/en/docs/references/cli/_index.md index 76379ad6da9..b0dfb5bceb1 100644 --- a/docs/content/en/docs/references/cli/_index.md +++ b/docs/content/en/docs/references/cli/_index.md @@ -87,7 +87,7 @@ Other Commands: credits Export third party notices to given path (./skaffold-credits by default) diagnose Run a diagnostic on Skaffold schema List and print json schemas used to validate skaffold.yaml configuration - survey Show Skaffold survey url + survey Opens a web browser to fill out the Skaffold survey version Print the version information Use "skaffold --help" for more information about a given command. @@ -930,7 +930,7 @@ Env vars: ### skaffold survey -Show Skaffold survey url +Opens a web browser to fill out the Skaffold survey ``` From 27c3dc1f97e1e09015849b3ac0fb915d3a263ac3 Mon Sep 17 00:00:00 2001 From: Nick Kubala Date: Tue, 4 Aug 2020 12:37:51 -0700 Subject: [PATCH 063/138] Disable survey prompt until the next release --- pkg/skaffold/survey/survey.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pkg/skaffold/survey/survey.go b/pkg/skaffold/survey/survey.go index ef85b02b65a..54b1ee4ab6f 100644 --- a/pkg/skaffold/survey/survey.go +++ b/pkg/skaffold/survey/survey.go @@ -22,10 +22,12 @@ import ( "io" "os" + "github.com/blang/semver" "github.com/pkg/browser" "github.com/sirupsen/logrus" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/version" ) const ( @@ -62,6 +64,14 @@ func New(configFile string) *Runner { } func (s *Runner) DisplaySurveyPrompt(out io.Writer) error { + // TODO(nkubala): remove after v1.14.0 is released + currentVersion, err := version.ParseVersion(version.Get().Version) + if err == nil { + nextRelease := semver.MustParse("1.14.0") + if currentVersion.LT(nextRelease) { + return nil + } + } if isStdOut(out) { fmt.Fprintln(out, Prompt) } From c8be9084238e78faec87551b60e8aeff5af8f163 Mon Sep 17 00:00:00 2001 From: Nick Kubala Date: Tue, 4 Aug 2020 13:52:38 -0700 Subject: [PATCH 064/138] Release v1.13.1 --- CHANGELOG.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 431c7ac15ca..4c7b7972f71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,27 @@ +# v1.13.1 Release - 08/04/2020 + +**Linux** +`curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v1.13.1/skaffold-linux-amd64 && chmod +x skaffold && sudo mv skaffold /usr/local/bin` + +**macOS** +`curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v1.13.1/skaffold-darwin-amd64 && chmod +x skaffold && sudo mv skaffold /usr/local/bin` + +**Windows** + https://storage.googleapis.com/skaffold/releases/v1.13.1/skaffold-windows-amd64.exe + +**Docker image** +`gcr.io/k8s-skaffold/skaffold:v1.13.1` + +**This is a hotfix release for a breaking issue causing our survey link to automatically open itself in a browser. The survey has been disabled completely as +we investigate and fix the root cause of the issue. Sincere apologies to anyone who was adversely affected by this.** + +Highlights: +* Fix CustomTagger docs [#4621](https://github.com/GoogleContainerTools/skaffold/pull/4621) +* Disable survey prompt until the next release [#4629](https://github.com/GoogleContainerTools/skaffol +d/pull/4629) +* Clarify 'survey' command text [#4625](https://github.com/GoogleContainerTools/skaffold/pull/4625) + + # v1.13.0 Release - 07/30/2020 **Linux** From 8b8dce1d3b98a86937b05728d9a0b8c2a9122b89 Mon Sep 17 00:00:00 2001 From: Gaurav <39389231+gsquared94@users.noreply.github.com> Date: Wed, 5 Aug 2020 08:14:32 +0530 Subject: [PATCH 065/138] Explicitly specify commands that should show surveys and update messages post execution (#4622) * Exclude update or survey messages for command outputs that will be evaluated on shell * Switch to allow list * Move check location * rename function * add UT * add Integration Test --- cmd/skaffold/app/cmd/build.go | 1 + cmd/skaffold/app/cmd/cmd.go | 28 +++++++++++++++-- cmd/skaffold/app/cmd/commands.go | 6 ++++ cmd/skaffold/app/cmd/commands_test.go | 5 +++ cmd/skaffold/app/cmd/debug.go | 1 + cmd/skaffold/app/cmd/deploy.go | 1 + cmd/skaffold/app/cmd/dev.go | 1 + cmd/skaffold/app/cmd/render.go | 1 + cmd/skaffold/app/cmd/run.go | 1 + integration/housekeeing_messages_test.go | 39 ++++++++++++++++++++++++ testutil/util.go | 8 +++++ 11 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 integration/housekeeing_messages_test.go diff --git a/cmd/skaffold/app/cmd/build.go b/cmd/skaffold/app/cmd/build.go index 597e24830be..7184056d930 100644 --- a/cmd/skaffold/app/cmd/build.go +++ b/cmd/skaffold/app/cmd/build.go @@ -57,6 +57,7 @@ func NewCmdBuild() *cobra.Command { f.StringVar(&buildOutputFlag, "file-output", "", "Filename to write build images to") f.BoolVar(&opts.DryRun, "dry-run", false, "Don't build images, just compute the tag for each artifact.") }). + WithHouseKeepingMessages(). NoArgs(doBuild) } diff --git a/cmd/skaffold/app/cmd/cmd.go b/cmd/skaffold/app/cmd/cmd.go index 114189a5474..61c76b47dcd 100644 --- a/cmd/skaffold/app/cmd/cmd.go +++ b/cmd/skaffold/app/cmd/cmd.go @@ -48,9 +48,14 @@ var ( shutdownAPIServer func() error ) +// Annotation for commands that should allow post execution housekeeping messages like updates and surveys +const ( + HouseKeepingMessagesAllowedAnnotation = "skaffold_annotation_housekeeping_allowed" +) + func NewSkaffoldCommand(out, err io.Writer) *cobra.Command { - updateMsg := make(chan string) - surveyPrompt := make(chan bool) + updateMsg := make(chan string, 1) + surveyPrompt := make(chan bool, 1) rootCmd := &cobra.Command{ Use: "skaffold", @@ -84,7 +89,10 @@ func NewSkaffoldCommand(out, err io.Writer) *cobra.Command { // Print version version := version.Get() logrus.Infof("Skaffold %+v", version) - + if !isHouseKeepingMessagesAllowed(cmd) { + logrus.Debugf("Disable housekeeping messages for command explicitly") + return nil + } switch { case !interactive: logrus.Debugf("Update check and survey prompt disabled in non-interactive mode") @@ -253,3 +261,17 @@ func alwaysSucceedWhenCancelled(ctx context.Context, err error) error { } return err } + +func isHouseKeepingMessagesAllowed(cmd *cobra.Command) bool { + if cmd.Annotations == nil { + return false + } + return cmd.Annotations[HouseKeepingMessagesAllowedAnnotation] == "true" +} + +func allowHouseKeepingMessages(cmd *cobra.Command) { + if cmd.Annotations == nil { + cmd.Annotations = make(map[string]string) + } + cmd.Annotations[HouseKeepingMessagesAllowedAnnotation] = "true" +} diff --git a/cmd/skaffold/app/cmd/commands.go b/cmd/skaffold/app/cmd/commands.go index 6c4f924dc3f..b0589b151fa 100644 --- a/cmd/skaffold/app/cmd/commands.go +++ b/cmd/skaffold/app/cmd/commands.go @@ -33,6 +33,7 @@ type Builder interface { WithLongDescription(long string) Builder WithExample(comment, command string) Builder WithFlags(adder func(*pflag.FlagSet)) Builder + WithHouseKeepingMessages() Builder WithCommonFlags() Builder Hidden() Builder ExactArgs(argCount int, action func(context.Context, io.Writer, []string) error) *cobra.Command @@ -75,6 +76,11 @@ func (b *builder) WithCommonFlags() Builder { return b } +func (b *builder) WithHouseKeepingMessages() Builder { + allowHouseKeepingMessages(&b.cmd) + return b +} + func (b *builder) WithFlags(adder func(*pflag.FlagSet)) Builder { adder(b.cmd.Flags()) return b diff --git a/cmd/skaffold/app/cmd/commands_test.go b/cmd/skaffold/app/cmd/commands_test.go index 82db2ebabe7..1b60cf0a758 100644 --- a/cmd/skaffold/app/cmd/commands_test.go +++ b/cmd/skaffold/app/cmd/commands_test.go @@ -105,6 +105,11 @@ func TestNewCmdWithFlags(t *testing.T) { testutil.CheckDeepEqual(t, "usage", flags["test"].Usage) } +func TestNewCmdWithHouseKeepingMessages(t *testing.T) { + cmd := NewCmd("run").WithHouseKeepingMessages().NoArgs(nil) + testutil.CheckDeepEqual(t, map[string]string{HouseKeepingMessagesAllowedAnnotation: "true"}, cmd.Annotations) +} + func TestNewCmdWithCommonFlags(t *testing.T) { cmd := NewCmd("run").WithCommonFlags().NoArgs(nil) diff --git a/cmd/skaffold/app/cmd/debug.go b/cmd/skaffold/app/cmd/debug.go index 345471178c1..0202043c7be 100644 --- a/cmd/skaffold/app/cmd/debug.go +++ b/cmd/skaffold/app/cmd/debug.go @@ -36,6 +36,7 @@ func NewCmdDebug() *cobra.Command { WithDescription("[beta] Run a pipeline in debug mode"). WithLongDescription("Similar to `dev`, but configures the pipeline for debugging."). WithCommonFlags(). + WithHouseKeepingMessages(). NoArgs(func(ctx context.Context, out io.Writer) error { return doDebug(ctx, out) }) diff --git a/cmd/skaffold/app/cmd/deploy.go b/cmd/skaffold/app/cmd/deploy.go index 8ed1411e00f..59f3cd1b473 100644 --- a/cmd/skaffold/app/cmd/deploy.go +++ b/cmd/skaffold/app/cmd/deploy.go @@ -50,6 +50,7 @@ func NewCmdDeploy() *cobra.Command { f.VarP(&deployFromBuildOutputFile, "build-artifacts", "a", "File containing build result from a previous 'skaffold build --file-output'") f.BoolVar(&opts.SkipRender, "skip-render", false, "Don't render the manifests, just deploy them") }). + WithHouseKeepingMessages(). NoArgs(doDeploy) } diff --git a/cmd/skaffold/app/cmd/dev.go b/cmd/skaffold/app/cmd/dev.go index aa923a2049a..524a17ce4b0 100644 --- a/cmd/skaffold/app/cmd/dev.go +++ b/cmd/skaffold/app/cmd/dev.go @@ -36,6 +36,7 @@ func NewCmdDev() *cobra.Command { return NewCmd("dev"). WithDescription("Run a pipeline in development mode"). WithCommonFlags(). + WithHouseKeepingMessages(). NoArgs(doDev) } diff --git a/cmd/skaffold/app/cmd/render.go b/cmd/skaffold/app/cmd/render.go index c0d9d20af90..9c63eb62f68 100644 --- a/cmd/skaffold/app/cmd/render.go +++ b/cmd/skaffold/app/cmd/render.go @@ -51,6 +51,7 @@ func NewCmdRender() *cobra.Command { f.StringVar(&renderOutputPath, "output", "", "file to write rendered manifests to") f.StringVar(&opts.DigestSource, "digest-source", "local", "Set to 'local' to build images locally and use digests from built images; Set to 'remote' to resolve the digest of images by tag from the remote registry; Set to 'none' to use tags directly from the Kubernetes manifests") }). + WithHouseKeepingMessages(). NoArgs(doRender) } diff --git a/cmd/skaffold/app/cmd/run.go b/cmd/skaffold/app/cmd/run.go index 6188a1aed6d..34564ee74f5 100644 --- a/cmd/skaffold/app/cmd/run.go +++ b/cmd/skaffold/app/cmd/run.go @@ -36,6 +36,7 @@ func NewCmdRun() *cobra.Command { WithExample("Build, test, deploy and tail the logs", "run --tail"). WithExample("Run with a given profile", "run -p "). WithCommonFlags(). + WithHouseKeepingMessages(). NoArgs(doRun) } diff --git a/integration/housekeeing_messages_test.go b/integration/housekeeing_messages_test.go new file mode 100644 index 00000000000..2cce4b41491 --- /dev/null +++ b/integration/housekeeing_messages_test.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 integration + +import ( + "testing" + + "github.com/GoogleContainerTools/skaffold/integration/skaffold" + "github.com/GoogleContainerTools/skaffold/testutil" +) + +func TestHouseKeepingMessagesNotShownForDiagnose(t *testing.T) { + MarkIntegrationTest(t, CanRunWithoutGcp) + file := testutil.TempFile(t, "config", nil) + out := skaffold.Diagnose("-c", file).InDir("examples/getting-started").RunOrFailOutput(t) + testutil.CheckNotContains(t, "Help improve Skaffold!", string(out)) +} + +func TestHouseKeepingMessagesShownForDev(t *testing.T) { + MarkIntegrationTest(t, CanRunWithoutGcp) + file := testutil.TempFile(t, "config", nil) + out := skaffold.Run("-c", file).InDir("examples/getting-started").RunOrFailOutput(t) + testutil.CheckContains(t, "Help improve Skaffold!", string(out)) + skaffold.Delete().InDir("examples/getting-started") +} diff --git a/testutil/util.go b/testutil/util.go index 643f3990efb..8f1215ab482 100644 --- a/testutil/util.go +++ b/testutil/util.go @@ -230,6 +230,14 @@ func CheckContains(t *testing.T, expected, actual string) { } } +func CheckNotContains(t *testing.T, excluded, actual string) { + t.Helper() + if strings.Contains(actual, excluded) { + t.Errorf("excluded output %s found in output: %s", excluded, actual) + return + } +} + func CheckDeepEqual(t *testing.T, expected, actual interface{}, opts ...cmp.Option) { t.Helper() if diff := cmp.Diff(actual, expected, opts...); diff != "" { From b87966e0430f9439132398fd53bc63eeacfb16a2 Mon Sep 17 00:00:00 2001 From: Hasso Mehide Date: Wed, 5 Aug 2020 20:29:35 +0300 Subject: [PATCH 066/138] Target k3d in integration tests (#4241) --- .travis.yml | 78 ++++++++++++++++++++++++----------------- Makefile | 20 +++++++++++ integration/dev_test.go | 3 ++ integration/util.go | 5 ++- 4 files changed, 73 insertions(+), 33 deletions(-) diff --git a/.travis.yml b/.travis.yml index fecfe6df23c..382ca5c4264 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,24 @@ branches: only: - master +_integration_test: &integration_test + os: linux + language: minimal + cache: + directories: + - $HOME/.cache/go-build + - $HOME/.gradle + - $HOME/.m2 + +# https://docs.travis-ci.com/user/docker/#installing-a-newer-docker-version +# include in jobs where the docker version needs to be newer than what is shipped with xenial +_upgrade_docker: &upgrade_docker + before_install: + - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - + - sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" + - sudo apt-get update + - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce + jobs: include: - os: linux @@ -45,46 +63,42 @@ jobs: cache: directories: - C:\\Users\\travis\\AppData\\Local\\go-build - - os: linux - name: "integration partition 0" - language: minimal + - name: "kind integration partition 0" + <<: *integration_test script: - env IT_PARTITION=0 make integration-in-kind - cache: - directories: - - $HOME/.cache/go-build - - $HOME/.gradle - - $HOME/.m2 - - os: linux - name: "integration partition 1" - language: minimal + - name: "kind integration partition 1" + <<: *integration_test script: - env IT_PARTITION=1 make integration-in-kind - cache: - directories: - - $HOME/.cache/go-build - - $HOME/.gradle - - $HOME/.m2 - - os: linux - name: "integration partition 2" - language: minimal + - name: "kind integration partition 2" + <<: *integration_test script: - env IT_PARTITION=2 make integration-in-kind - cache: - directories: - - $HOME/.cache/go-build - - $HOME/.gradle - - $HOME/.m2 - - os: linux - name: "integration partition 3" - language: minimal + - name: "kind integration partition 3" + <<: *integration_test script: - env IT_PARTITION=3 make integration-in-kind - cache: - directories: - - $HOME/.cache/go-build - - $HOME/.gradle - - $HOME/.m2 + - name: "k3d integration partition 0" + <<: *integration_test + <<: *upgrade_docker + script: + - env IT_PARTITION=0 make integration-in-k3d + - name: "k3d integration partition 1" + <<: *integration_test + <<: *upgrade_docker + script: + - env IT_PARTITION=1 make integration-in-k3d + - name: "k3d integration partition 2" + <<: *integration_test + <<: *upgrade_docker + script: + - env IT_PARTITION=2 make integration-in-k3d + - name: "k3d integration partition 3" + <<: *integration_test + <<: *upgrade_docker + script: + - env IT_PARTITION=3 make integration-in-k3d - os: linux name: "diag/Linux unit" script: diff --git a/Makefile b/Makefile index 2a8c714ad04..6374aaf514c 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,7 @@ GSC_BUILD_LATEST ?= gs://$(RELEASE_BUCKET)/builds/latest GSC_RELEASE_PATH ?= gs://$(RELEASE_BUCKET)/releases/$(VERSION) GSC_RELEASE_LATEST ?= gs://$(RELEASE_BUCKET)/releases/latest KIND_NODE ?= kindest/node:v1.13.12@sha256:214476f1514e47fe3f6f54d0f9e24cfb1e4cda449529791286c7161b7f9c08e7 +K3D_NODE ?= rancher/k3s:v1.18.6-k3s1@sha256:a835d76608a2503af8b681bb5888499d7c3456902f6853c8c1031f4a884715ca GCP_ONLY ?= false GCP_PROJECT ?= k8s-skaffold @@ -205,6 +206,25 @@ integration-in-kind: skaffold-builder make integration \ ' +.PHONY: integration-in-k3d +integration-in-k3d: skaffold-builder + echo '{}' > /tmp/docker-config + docker pull $(K3D_NODE) + docker run --rm \ + --network="host" \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v $(HOME)/.gradle:/root/.gradle \ + -v $(HOME)/.cache:/root/.cache \ + -v /tmp/docker-config:/root/.docker/config.json \ + -v $(CURDIR)/hack/maven/settings.xml:/root/.m2/settings.xml \ + -e INTEGRATION_TEST_ARGS=$(INTEGRATION_TEST_ARGS) \ + -e IT_PARTITION=$(IT_PARTITION) \ + gcr.io/$(GCP_PROJECT)/skaffold-builder \ + sh -c ' \ + k3d cluster list | grep -q k3s-default || TERM=dumb k3d cluster create --image=$(K3D_NODE); \ + make integration \ + ' + .PHONY: integration-in-docker integration-in-docker: skaffold-builder docker run --rm \ diff --git a/integration/dev_test.go b/integration/dev_test.go index 74332c72c9c..2679c129cbf 100644 --- a/integration/dev_test.go +++ b/integration/dev_test.go @@ -356,6 +356,9 @@ func createModifiedKubeconfig(namespace string) ([]byte, string, error) { if config.IsKindCluster(kubeConfig.CurrentContext) { contextName = "kind-" + contextName } + if config.IsK3dCluster(kubeConfig.CurrentContext) { + contextName = "k3d-" + contextName + } if kubeConfig.CurrentContext == constants.DefaultMinikubeContext { contextName = constants.DefaultMinikubeContext // skip, since integration test with minikube runs on single cluster diff --git a/integration/util.go b/integration/util.go index 946cd955fbb..0efc029a881 100644 --- a/integration/util.go +++ b/integration/util.go @@ -204,6 +204,9 @@ func (k *NSKubernetesClient) WaitForPodsInPhase(expectedPhase v1.PodPhase, podNa k.t.Fatalf("Timed out waiting for pods %v ready in namespace %s", podNames, k.ns) case event := <-w.ResultChan(): + if event.Object == nil { + return + } pod := event.Object.(*v1.Pod) logrus.Infoln("Pod", pod.Name, "is", pod.Status.Phase) if pod.Status.Phase == v1.PodFailed { @@ -254,7 +257,7 @@ func (k *NSKubernetesClient) GetDeployment(depName string) *appsv1.Deployment { // WaitForDeploymentsToStabilize waits for a list of deployments to become stable. func (k *NSKubernetesClient) WaitForDeploymentsToStabilize(depNames ...string) { k.t.Helper() - k.waitForDeploymentsToStabilizeWithTimeout(30*time.Second, depNames...) + k.waitForDeploymentsToStabilizeWithTimeout(60*time.Second, depNames...) } func (k *NSKubernetesClient) waitForDeploymentsToStabilizeWithTimeout(timeout time.Duration, depNames ...string) { From da332e4a900e3031a3d4f4dcd5d28d40fce63490 Mon Sep 17 00:00:00 2001 From: gsquared94 Date: Mon, 10 Aug 2020 21:30:06 +0530 Subject: [PATCH 067/138] Make event handling sequential and set the correct timestamp --- pkg/skaffold/event/event.go | 56 +++++-- pkg/skaffold/event/event_test.go | 265 ++++++++++++++----------------- 2 files changed, 164 insertions(+), 157 deletions(-) diff --git a/pkg/skaffold/event/event.go b/pkg/skaffold/event/event.go index 7163e36612b..bea5091569b 100644 --- a/pkg/skaffold/event/event.go +++ b/pkg/skaffold/event/event.go @@ -22,6 +22,7 @@ import ( "sync" "github.com/golang/protobuf/ptypes" + "github.com/golang/protobuf/ptypes/timestamp" sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" @@ -40,7 +41,20 @@ const ( Terminated = "Terminated" ) -var handler = &eventHandler{} +var handler = newHandler() + +func newHandler() *eventHandler { + h := &eventHandler{ + eventChan: make(chan firedEvent), + } + go func() { + for { + ev := <-h.eventChan + h.handleExec(ev) + } + }() + return h +} type eventHandler struct { eventLog []proto.LogEntry @@ -48,10 +62,15 @@ type eventHandler struct { state proto.State stateLock sync.Mutex - + eventChan chan firedEvent listeners []*listener } +type firedEvent struct { + event *proto.Event + ts *timestamp.Timestamp +} + type listener struct { callback func(*proto.LogEntry) error errors chan error @@ -361,7 +380,7 @@ func FileSyncSucceeded(fileCount int, image string) { // PortForwarded notifies that a remote port has been forwarded locally. func PortForwarded(localPort, remotePort int32, podName, containerName, namespace string, portName string, resourceType, resourceName, address string) { - go handler.handle(&proto.Event{ + handler.handle(&proto.Event{ EventType: &proto.Event_PortEvent{ PortEvent: &proto.PortEvent{ LocalPort: localPort, @@ -380,7 +399,7 @@ func PortForwarded(localPort, remotePort int32, podName, containerName, namespac // DebuggingContainerStarted notifies that a debuggable container has appeared. func DebuggingContainerStarted(podName, containerName, namespace, artifact, runtime, workingDir string, debugPorts map[string]uint32) { - go handler.handle(&proto.Event{ + handler.handle(&proto.Event{ EventType: &proto.Event_DebuggingContainerEvent{ DebuggingContainerEvent: &proto.DebuggingContainerEvent{ Status: Started, @@ -398,7 +417,7 @@ func DebuggingContainerStarted(podName, containerName, namespace, artifact, runt // DebuggingContainerTerminated notifies that a debuggable container has disappeared. func DebuggingContainerTerminated(podName, containerName, namespace, artifact, runtime, workingDir string, debugPorts map[string]uint32) { - go handler.handle(&proto.Event{ + handler.handle(&proto.Event{ EventType: &proto.Event_DebuggingContainerEvent{ DebuggingContainerEvent: &proto.DebuggingContainerEvent{ Status: Terminated, @@ -421,7 +440,7 @@ func (ev *eventHandler) setState(state proto.State) { } func (ev *eventHandler) handleDeployEvent(e *proto.DeployEvent) { - go ev.handle(&proto.Event{ + ev.handle(&proto.Event{ EventType: &proto.Event_DeployEvent{ DeployEvent: e, }, @@ -429,7 +448,7 @@ func (ev *eventHandler) handleDeployEvent(e *proto.DeployEvent) { } func (ev *eventHandler) handleStatusCheckEvent(e *proto.StatusCheckEvent) { - go ev.handle(&proto.Event{ + ev.handle(&proto.Event{ EventType: &proto.Event_StatusCheckEvent{ StatusCheckEvent: e, }, @@ -437,7 +456,7 @@ func (ev *eventHandler) handleStatusCheckEvent(e *proto.StatusCheckEvent) { } func (ev *eventHandler) handleResourceStatusCheckEvent(e *proto.ResourceStatusCheckEvent) { - go ev.handle(&proto.Event{ + ev.handle(&proto.Event{ EventType: &proto.Event_ResourceStatusCheckEvent{ ResourceStatusCheckEvent: e, }, @@ -445,7 +464,7 @@ func (ev *eventHandler) handleResourceStatusCheckEvent(e *proto.ResourceStatusCh } func (ev *eventHandler) handleBuildEvent(e *proto.BuildEvent) { - go ev.handle(&proto.Event{ + ev.handle(&proto.Event{ EventType: &proto.Event_BuildEvent{ BuildEvent: e, }, @@ -453,7 +472,7 @@ func (ev *eventHandler) handleBuildEvent(e *proto.BuildEvent) { } func (ev *eventHandler) handleDevLoopEvent(e *proto.DevLoopEvent) { - go ev.handle(&proto.Event{ + ev.handle(&proto.Event{ EventType: &proto.Event_DevLoopEvent{ DevLoopEvent: e, }, @@ -461,7 +480,7 @@ func (ev *eventHandler) handleDevLoopEvent(e *proto.DevLoopEvent) { } func (ev *eventHandler) handleFileSyncEvent(e *proto.FileSyncEvent) { - go ev.handle(&proto.Event{ + ev.handle(&proto.Event{ EventType: &proto.Event_FileSyncEvent{ FileSyncEvent: e, }, @@ -484,12 +503,21 @@ func LogMetaEvent() { } func (ev *eventHandler) handle(event *proto.Event) { + go func(t *timestamp.Timestamp) { + ev.eventChan <- firedEvent{ + event: event, + ts: t, + } + }(ptypes.TimestampNow()) +} + +func (ev *eventHandler) handleExec(f firedEvent) { logEntry := &proto.LogEntry{ - Timestamp: ptypes.TimestampNow(), - Event: event, + Timestamp: f.ts, + Event: f.event, } - switch e := event.GetEventType().(type) { + switch e := f.event.GetEventType().(type) { case *proto.Event_BuildEvent: be := e.BuildEvent ev.stateLock.Lock() diff --git a/pkg/skaffold/event/event_test.go b/pkg/skaffold/event/event_test.go index 3413f00623d..bbf65060303 100644 --- a/pkg/skaffold/event/event_test.go +++ b/pkg/skaffold/event/event_test.go @@ -32,8 +32,8 @@ import ( ) func TestGetLogEvents(t *testing.T) { - for step := 0; step < 10000; step++ { - ev := &eventHandler{} + for step := 0; step < 1000; step++ { + ev := newHandler() ev.logEvent(proto.LogEntry{Entry: "OLD"}) go func() { @@ -58,9 +58,8 @@ func TestGetLogEvents(t *testing.T) { } func TestGetState(t *testing.T) { - ev := &eventHandler{ - state: emptyState(latest.Pipeline{}, "test", true, true, true), - } + ev := newHandler() + ev.state = emptyState(latest.Pipeline{}, "test", true, true, true) ev.stateLock.Lock() ev.state.BuildState.Artifacts["img"] = Complete @@ -72,11 +71,10 @@ func TestGetState(t *testing.T) { } func TestDeployInProgress(t *testing.T) { - defer func() { handler = &eventHandler{} }() + defer func() { handler = newHandler() }() - handler = &eventHandler{ - state: emptyState(latest.Pipeline{}, "test", true, true, true), - } + handler = newHandler() + handler.state = emptyState(latest.Pipeline{}, "test", true, true, true) wait(t, func() bool { return handler.getState().DeployState.Status == NotStarted }) DeployInProgress() @@ -84,11 +82,10 @@ func TestDeployInProgress(t *testing.T) { } func TestDeployFailed(t *testing.T) { - defer func() { handler = &eventHandler{} }() + defer func() { handler = newHandler() }() - handler = &eventHandler{ - state: emptyState(latest.Pipeline{}, "test", true, true, true), - } + handler = newHandler() + handler.state = emptyState(latest.Pipeline{}, "test", true, true, true) wait(t, func() bool { return handler.getState().DeployState.Status == NotStarted }) DeployFailed(errors.New("BUG")) @@ -99,11 +96,10 @@ func TestDeployFailed(t *testing.T) { } func TestDeployComplete(t *testing.T) { - defer func() { handler = &eventHandler{} }() + defer func() { handler = newHandler() }() - handler = &eventHandler{ - state: emptyState(latest.Pipeline{}, "test", true, true, true), - } + handler = newHandler() + handler.state = emptyState(latest.Pipeline{}, "test", true, true, true) wait(t, func() bool { return handler.getState().DeployState.Status == NotStarted }) DeployComplete() @@ -114,15 +110,14 @@ func TestDeployComplete(t *testing.T) { } func TestBuildInProgress(t *testing.T) { - defer func() { handler = &eventHandler{} }() - - handler = &eventHandler{ - state: emptyState(latest.Pipeline{Build: latest.BuildConfig{ - Artifacts: []*latest.Artifact{{ - ImageName: "img", - }}, - }}, "test", true, true, true), - } + defer func() { handler = newHandler() }() + + handler = newHandler() + handler.state = emptyState(latest.Pipeline{Build: latest.BuildConfig{ + Artifacts: []*latest.Artifact{{ + ImageName: "img", + }}, + }}, "test", true, true, true) wait(t, func() bool { return handler.getState().BuildState.Artifacts["img"] == NotStarted }) BuildInProgress("img") @@ -130,15 +125,14 @@ func TestBuildInProgress(t *testing.T) { } func TestBuildFailed(t *testing.T) { - defer func() { handler = &eventHandler{} }() - - handler = &eventHandler{ - state: emptyState(latest.Pipeline{Build: latest.BuildConfig{ - Artifacts: []*latest.Artifact{{ - ImageName: "img", - }}, - }}, "test", true, true, true), - } + defer func() { handler = newHandler() }() + + handler = newHandler() + handler.state = emptyState(latest.Pipeline{Build: latest.BuildConfig{ + Artifacts: []*latest.Artifact{{ + ImageName: "img", + }}, + }}, "test", true, true, true) wait(t, func() bool { return handler.getState().BuildState.Artifacts["img"] == NotStarted }) BuildFailed("img", errors.New("BUG")) @@ -149,15 +143,14 @@ func TestBuildFailed(t *testing.T) { } func TestBuildComplete(t *testing.T) { - defer func() { handler = &eventHandler{} }() - - handler = &eventHandler{ - state: emptyState(latest.Pipeline{Build: latest.BuildConfig{ - Artifacts: []*latest.Artifact{{ - ImageName: "img", - }}, - }}, "test", true, true, true), - } + defer func() { handler = newHandler() }() + + handler = newHandler() + handler.state = emptyState(latest.Pipeline{Build: latest.BuildConfig{ + Artifacts: []*latest.Artifact{{ + ImageName: "img", + }}, + }}, "test", true, true, true) wait(t, func() bool { return handler.getState().BuildState.Artifacts["img"] == NotStarted }) BuildComplete("img") @@ -165,11 +158,10 @@ func TestBuildComplete(t *testing.T) { } func TestPortForwarded(t *testing.T) { - defer func() { handler = &eventHandler{} }() + defer func() { handler = newHandler() }() - handler = &eventHandler{ - state: emptyState(latest.Pipeline{}, "test", true, true, true), - } + handler = newHandler() + handler.state = emptyState(latest.Pipeline{}, "test", true, true, true) wait(t, func() bool { return handler.getState().ForwardedPorts[8080] == nil }) PortForwarded(8080, 8888, "pod", "container", "ns", "portname", "resourceType", "resourceName", "127.0.0.1") @@ -177,11 +169,10 @@ func TestPortForwarded(t *testing.T) { } func TestStatusCheckEventStarted(t *testing.T) { - defer func() { handler = &eventHandler{} }() + defer func() { handler = newHandler() }() - handler = &eventHandler{ - state: emptyState(latest.Pipeline{}, "test", true, true, true), - } + handler = newHandler() + handler.state = emptyState(latest.Pipeline{}, "test", true, true, true) wait(t, func() bool { return handler.getState().StatusCheckState.Status == NotStarted }) StatusCheckEventStarted() @@ -189,11 +180,10 @@ func TestStatusCheckEventStarted(t *testing.T) { } func TestStatusCheckEventInProgress(t *testing.T) { - defer func() { handler = &eventHandler{} }() + defer func() { handler = newHandler() }() - handler = &eventHandler{ - state: emptyState(latest.Pipeline{}, "test", true, true, true), - } + handler = newHandler() + handler.state = emptyState(latest.Pipeline{}, "test", true, true, true) wait(t, func() bool { return handler.getState().StatusCheckState.Status == NotStarted }) StatusCheckEventInProgress("[2/5 deployment(s) are still pending]") @@ -201,11 +191,10 @@ func TestStatusCheckEventInProgress(t *testing.T) { } func TestStatusCheckEventSucceeded(t *testing.T) { - defer func() { handler = &eventHandler{} }() + defer func() { handler = newHandler() }() - handler = &eventHandler{ - state: emptyState(latest.Pipeline{}, "test", true, true, true), - } + handler = newHandler() + handler.state = emptyState(latest.Pipeline{}, "test", true, true, true) wait(t, func() bool { return handler.getState().StatusCheckState.Status == NotStarted }) statusCheckEventSucceeded() @@ -213,11 +202,10 @@ func TestStatusCheckEventSucceeded(t *testing.T) { } func TestStatusCheckEventFailed(t *testing.T) { - defer func() { handler = &eventHandler{} }() + defer func() { handler = newHandler() }() - handler = &eventHandler{ - state: emptyState(latest.Pipeline{}, "test", true, true, true), - } + handler = newHandler() + handler.state = emptyState(latest.Pipeline{}, "test", true, true, true) wait(t, func() bool { return handler.getState().StatusCheckState.Status == NotStarted }) StatusCheckEventEnded(proto.StatusCode_STATUSCHECK_FAILED_SCHEDULING, errors.New("one or more deployments failed")) @@ -228,11 +216,10 @@ func TestStatusCheckEventFailed(t *testing.T) { } func TestResourceStatusCheckEventUpdated(t *testing.T) { - defer func() { handler = &eventHandler{} }() + defer func() { handler = newHandler() }() - handler = &eventHandler{ - state: emptyState(latest.Pipeline{}, "test", true, true, true), - } + handler = newHandler() + handler.state = emptyState(latest.Pipeline{}, "test", true, true, true) wait(t, func() bool { return handler.getState().StatusCheckState.Status == NotStarted }) ResourceStatusCheckEventUpdated("ns:pod/foo", proto.ActionableErr{ @@ -243,11 +230,10 @@ func TestResourceStatusCheckEventUpdated(t *testing.T) { } func TestResourceStatusCheckEventSucceeded(t *testing.T) { - defer func() { handler = &eventHandler{} }() + defer func() { handler = newHandler() }() - handler = &eventHandler{ - state: emptyState(latest.Pipeline{}, "test", true, true, true), - } + handler = newHandler() + handler.state = emptyState(latest.Pipeline{}, "test", true, true, true) wait(t, func() bool { return handler.getState().StatusCheckState.Status == NotStarted }) resourceStatusCheckEventSucceeded("ns:pod/foo") @@ -255,11 +241,10 @@ func TestResourceStatusCheckEventSucceeded(t *testing.T) { } func TestResourceStatusCheckEventFailed(t *testing.T) { - defer func() { handler = &eventHandler{} }() + defer func() { handler = newHandler() }() - handler = &eventHandler{ - state: emptyState(latest.Pipeline{}, "test", true, true, true), - } + handler = newHandler() + handler.state = emptyState(latest.Pipeline{}, "test", true, true, true) wait(t, func() bool { return handler.getState().StatusCheckState.Status == NotStarted }) resourceStatusCheckEventFailed("ns:pod/foo", proto.ActionableErr{ @@ -270,11 +255,10 @@ func TestResourceStatusCheckEventFailed(t *testing.T) { } func TestFileSyncInProgress(t *testing.T) { - defer func() { handler = &eventHandler{} }() + defer func() { handler = newHandler() }() - handler = &eventHandler{ - state: emptyState(latest.Pipeline{}, "test", true, true, true), - } + handler = newHandler() + handler.state = emptyState(latest.Pipeline{}, "test", true, true, true) wait(t, func() bool { return handler.getState().FileSyncState.Status == NotStarted }) FileSyncInProgress(5, "image") @@ -282,11 +266,10 @@ func TestFileSyncInProgress(t *testing.T) { } func TestFileSyncFailed(t *testing.T) { - defer func() { handler = &eventHandler{} }() + defer func() { handler = newHandler() }() - handler = &eventHandler{ - state: emptyState(latest.Pipeline{}, "test", true, true, true), - } + handler = newHandler() + handler.state = emptyState(latest.Pipeline{}, "test", true, true, true) wait(t, func() bool { return handler.getState().FileSyncState.Status == NotStarted }) FileSyncFailed(5, "image", errors.New("BUG")) @@ -294,11 +277,10 @@ func TestFileSyncFailed(t *testing.T) { } func TestFileSyncSucceeded(t *testing.T) { - defer func() { handler = &eventHandler{} }() + defer func() { handler = newHandler() }() - handler = &eventHandler{ - state: emptyState(latest.Pipeline{}, "test", true, true, true), - } + handler = newHandler() + handler.state = emptyState(latest.Pipeline{}, "test", true, true, true) wait(t, func() bool { return handler.getState().FileSyncState.Status == NotStarted }) FileSyncSucceeded(5, "image") @@ -306,11 +288,10 @@ func TestFileSyncSucceeded(t *testing.T) { } func TestDebuggingContainer(t *testing.T) { - defer func() { handler = &eventHandler{} }() + defer func() { handler = newHandler() }() - handler = &eventHandler{ - state: emptyState(latest.Pipeline{}, "test", true, true, true), - } + handler = newHandler() + handler.state = emptyState(latest.Pipeline{}, "test", true, true, true) found := func() bool { for _, dc := range handler.getState().DebuggingContainers { @@ -349,26 +330,26 @@ func wait(t *testing.T, condition func() bool) { } func TestResetStateOnBuild(t *testing.T) { - defer func() { handler = &eventHandler{} }() - handler = &eventHandler{ - state: proto.State{ - BuildState: &proto.BuildState{ - Artifacts: map[string]string{ - "image1": Complete, - }, + defer func() { handler = newHandler() }() + handler = newHandler() + handler.state = proto.State{ + BuildState: &proto.BuildState{ + Artifacts: map[string]string{ + "image1": Complete, }, - DeployState: &proto.DeployState{Status: Complete}, - ForwardedPorts: map[int32]*proto.PortEvent{ - 2001: { - LocalPort: 2000, - RemotePort: 2001, - PodName: "test/pod", - }, + }, + DeployState: &proto.DeployState{Status: Complete}, + ForwardedPorts: map[int32]*proto.PortEvent{ + 2001: { + LocalPort: 2000, + RemotePort: 2001, + PodName: "test/pod", }, - StatusCheckState: &proto.StatusCheckState{Status: Complete}, - FileSyncState: &proto.FileSyncState{Status: Succeeded}, }, + StatusCheckState: &proto.StatusCheckState{Status: Complete}, + FileSyncState: &proto.FileSyncState{Status: Succeeded}, } + ResetStateOnBuild() expected := proto.State{ BuildState: &proto.BuildState{ @@ -384,24 +365,23 @@ func TestResetStateOnBuild(t *testing.T) { } func TestResetStateOnDeploy(t *testing.T) { - defer func() { handler = &eventHandler{} }() - handler = &eventHandler{ - state: proto.State{ - BuildState: &proto.BuildState{ - Artifacts: map[string]string{ - "image1": Complete, - }, + defer func() { handler = newHandler() }() + handler = newHandler() + handler.state = proto.State{ + BuildState: &proto.BuildState{ + Artifacts: map[string]string{ + "image1": Complete, }, - DeployState: &proto.DeployState{Status: Complete}, - ForwardedPorts: map[int32]*proto.PortEvent{ - 2001: { - LocalPort: 2000, - RemotePort: 2001, - PodName: "test/pod", - }, + }, + DeployState: &proto.DeployState{Status: Complete}, + ForwardedPorts: map[int32]*proto.PortEvent{ + 2001: { + LocalPort: 2000, + RemotePort: 2001, + PodName: "test/pod", }, - StatusCheckState: &proto.StatusCheckState{Status: Complete}, }, + StatusCheckState: &proto.StatusCheckState{Status: Complete}, } ResetStateOnDeploy() expected := proto.State{ @@ -427,29 +407,28 @@ func TestEmptyStateCheckState(t *testing.T) { } func TestUpdateStateAutoTriggers(t *testing.T) { - defer func() { handler = &eventHandler{} }() - handler = &eventHandler{ - state: proto.State{ - BuildState: &proto.BuildState{ - Artifacts: map[string]string{ - "image1": Complete, - }, - AutoTrigger: false, - }, - DeployState: &proto.DeployState{Status: Complete, AutoTrigger: false}, - ForwardedPorts: map[int32]*proto.PortEvent{ - 2001: { - LocalPort: 2000, - RemotePort: 2001, - PodName: "test/pod", - }, + defer func() { handler = newHandler() }() + handler = newHandler() + handler.state = proto.State{ + BuildState: &proto.BuildState{ + Artifacts: map[string]string{ + "image1": Complete, }, - StatusCheckState: &proto.StatusCheckState{Status: Complete}, - FileSyncState: &proto.FileSyncState{ - Status: "Complete", - AutoTrigger: false, + AutoTrigger: false, + }, + DeployState: &proto.DeployState{Status: Complete, AutoTrigger: false}, + ForwardedPorts: map[int32]*proto.PortEvent{ + 2001: { + LocalPort: 2000, + RemotePort: 2001, + PodName: "test/pod", }, }, + StatusCheckState: &proto.StatusCheckState{Status: Complete}, + FileSyncState: &proto.FileSyncState{ + Status: "Complete", + AutoTrigger: false, + }, } UpdateStateAutoBuildTrigger(true) UpdateStateAutoDeployTrigger(true) From 2994c6bbaf450f29e774e172856aa743df21837e Mon Sep 17 00:00:00 2001 From: Marlon Gamez Date: Mon, 10 Aug 2020 16:41:22 -0700 Subject: [PATCH 068/138] skip test TestHouseKeepingMessagesShownForDev until 1.14.0 --- integration/housekeeing_messages_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/integration/housekeeing_messages_test.go b/integration/housekeeing_messages_test.go index 2cce4b41491..ef68b34d5d8 100644 --- a/integration/housekeeing_messages_test.go +++ b/integration/housekeeing_messages_test.go @@ -31,6 +31,9 @@ func TestHouseKeepingMessagesNotShownForDiagnose(t *testing.T) { } func TestHouseKeepingMessagesShownForDev(t *testing.T) { + // TODO(marlongamez): remove after v1.14.0 is released + t.Skip() + MarkIntegrationTest(t, CanRunWithoutGcp) file := testutil.TempFile(t, "config", nil) out := skaffold.Run("-c", file).InDir("examples/getting-started").RunOrFailOutput(t) From 7f30b8211f0caedb847e04b3e3b38586df9d84e4 Mon Sep 17 00:00:00 2001 From: Gaurav <39389231+gsquared94@users.noreply.github.com> Date: Tue, 11 Aug 2020 12:46:32 +0530 Subject: [PATCH 069/138] Dependabot fix done explicitly (#4635) --- examples/react-reload/app/package-lock.json | 6 +++--- integration/examples/react-reload/app/package-lock.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/react-reload/app/package-lock.json b/examples/react-reload/app/package-lock.json index 5fb9a65de33..20223262608 100644 --- a/examples/react-reload/app/package-lock.json +++ b/examples/react-reload/app/package-lock.json @@ -2477,9 +2477,9 @@ "dev": true }, "elliptic": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", - "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", "dev": true, "requires": { "bn.js": "^4.4.0", diff --git a/integration/examples/react-reload/app/package-lock.json b/integration/examples/react-reload/app/package-lock.json index 5fb9a65de33..20223262608 100644 --- a/integration/examples/react-reload/app/package-lock.json +++ b/integration/examples/react-reload/app/package-lock.json @@ -2477,9 +2477,9 @@ "dev": true }, "elliptic": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", - "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", "dev": true, "requires": { "bn.js": "^4.4.0", From bbfa6bde2c1e964a496d3c362879d091e738b071 Mon Sep 17 00:00:00 2001 From: gsquared94 Date: Tue, 11 Aug 2020 19:37:59 +0530 Subject: [PATCH 070/138] Prepare v2beta7 --- docs/config.toml | 2 +- docs/content/en/docs/references/cli/_index.md | 2 +- docs/content/en/schemas/v2beta7.json | 2308 +++++++++++++++++ integration/examples/bazel/skaffold.yaml | 2 +- .../examples/buildpacks-java/skaffold.yaml | 2 +- .../examples/buildpacks-node/skaffold.yaml | 2 +- .../examples/buildpacks-python/skaffold.yaml | 2 +- integration/examples/buildpacks/skaffold.yaml | 2 +- integration/examples/custom/skaffold.yaml | 2 +- integration/examples/gcb-kaniko/skaffold.yaml | 2 +- .../examples/generate-pipeline/skaffold.yaml | 2 +- .../getting-started-kustomize/skaffold.yaml | 2 +- .../examples/getting-started/skaffold.yaml | 2 +- .../examples/google-cloud-build/skaffold.yaml | 2 +- .../skaffold.yaml | 2 +- .../examples/helm-deployment/skaffold.yaml | 2 +- integration/examples/hot-reload/skaffold.yaml | 2 +- integration/examples/jib-gradle/skaffold.yaml | 2 +- .../examples/jib-multimodule/skaffold.yaml | 2 +- .../examples/jib-sync/skaffold-gradle.yaml | 2 +- .../examples/jib-sync/skaffold-maven.yaml | 2 +- integration/examples/jib/skaffold.yaml | 2 +- integration/examples/kaniko/skaffold.yaml | 2 +- .../kustomize/skaffold-kustomize-args.yaml | 2 +- integration/examples/kustomize/skaffold.yaml | 2 +- .../examples/microservices/skaffold.yaml | 2 +- integration/examples/nodejs/skaffold.yaml | 2 +- .../examples/profile-patches/skaffold.yaml | 2 +- integration/examples/profiles/skaffold.yaml | 2 +- .../examples/react-reload/skaffold.yaml | 2 +- integration/examples/ruby/skaffold.yaml | 2 +- .../examples/structure-tests/skaffold.yaml | 2 +- .../skaffold.yaml | 2 +- .../examples/templated-fields/skaffold.yaml | 2 +- integration/testdata/build/skaffold.yaml | 2 +- integration/testdata/debug/skaffold.yaml | 2 +- .../testdata/deploy-multiple/skaffold.yaml | 2 +- integration/testdata/dev/skaffold.yaml | 2 +- .../testdata/gke_loadbalancer/skaffold.yaml | 2 +- integration/testdata/hello/skaffold.yaml | 2 +- .../testdata/init/compose/skaffold.yaml | 2 +- integration/testdata/jib/skaffold.yaml | 2 +- .../kaniko-explicit-repo/skaffold.yaml | 2 +- .../app/skaffold.yaml | 2 +- .../kaniko-insecure-registry/skaffold.yaml | 2 +- .../kaniko-microservices/skaffold.yaml | 2 +- .../testdata/kaniko-sub-folder/skaffold.yaml | 2 +- .../testdata/kaniko-target/skaffold.yaml | 2 +- integration/testdata/tagPolicy/skaffold.yaml | 2 +- .../testdata/init/allcli/skaffold.yaml | 2 +- .../getting-started-kustomize/skaffold.yaml | 2 +- .../init/hello-no-manifest/skaffold.yaml | 2 +- .../testdata/init/hello/skaffold.yaml | 2 +- .../testdata/init/ignore-tags/skaffold.yaml | 2 +- .../testdata/init/microservices/skaffold.yaml | 2 +- pkg/skaffold/schema/latest/config.go | 4 +- pkg/skaffold/schema/v2beta5/upgrade.go | 2 +- pkg/skaffold/schema/v2beta5/upgrade_test.go | 2 +- pkg/skaffold/schema/v2beta6/config.go | 960 +++++++ pkg/skaffold/schema/v2beta6/upgrade.go | 40 + pkg/skaffold/schema/v2beta6/upgrade_test.go | 182 ++ pkg/skaffold/schema/versions.go | 2 + 62 files changed, 3550 insertions(+), 58 deletions(-) create mode 100755 docs/content/en/schemas/v2beta7.json create mode 100755 pkg/skaffold/schema/v2beta6/config.go create mode 100755 pkg/skaffold/schema/v2beta6/upgrade.go create mode 100755 pkg/skaffold/schema/v2beta6/upgrade_test.go diff --git a/docs/config.toml b/docs/config.toml index f84034ab0a9..fc954c20213 100644 --- a/docs/config.toml +++ b/docs/config.toml @@ -82,7 +82,7 @@ weight = 1 copyright = "Skaffold Authors" privacy_policy = "https://policies.google.com/privacy" github_repo = "https://github.com/GoogleContainerTools/skaffold" -skaffold_version = "skaffold/v2beta6" +skaffold_version = "skaffold/v2beta7" # Google Custom Search Engine ID. Remove or comment out to disable search. gcs_engine_id = "013756393218025596041:3nojel67sum" diff --git a/docs/content/en/docs/references/cli/_index.md b/docs/content/en/docs/references/cli/_index.md index b0dfb5bceb1..676cd37b4ab 100644 --- a/docs/content/en/docs/references/cli/_index.md +++ b/docs/content/en/docs/references/cli/_index.md @@ -664,7 +664,7 @@ Examples: Options: -f, --filename='skaffold.yaml': Path or URL to the Skaffold config file --overwrite=false: Overwrite original config with fixed config - --version='skaffold/v2beta6': Target schema version to upgrade to + --version='skaffold/v2beta7': Target schema version to upgrade to Usage: skaffold fix [options] diff --git a/docs/content/en/schemas/v2beta7.json b/docs/content/en/schemas/v2beta7.json new file mode 100755 index 00000000000..32438e34588 --- /dev/null +++ b/docs/content/en/schemas/v2beta7.json @@ -0,0 +1,2308 @@ +{ + "type": "object", + "anyOf": [ + { + "$ref": "#/definitions/SkaffoldConfig" + } + ], + "$schema": "http://json-schema-org/draft-07/schema#", + "definitions": { + "Activation": { + "properties": { + "command": { + "type": "string", + "description": "a Skaffold command for which the profile is auto-activated.", + "x-intellij-html-description": "a Skaffold command for which the profile is auto-activated.", + "examples": [ + "dev" + ] + }, + "env": { + "type": "string", + "description": "a `key=pattern` pair. The profile is auto-activated if an Environment Variable `key` matches the pattern. If the pattern starts with `!`, activation happens if the remaining pattern is _not_ matched. The pattern matches if the Environment Variable value is exactly `pattern`, or the regex `pattern` is found in it. An empty `pattern` (e.g. `env: \"key=\"`) always only matches if the Environment Variable is undefined or empty.", + "x-intellij-html-description": "a key=pattern pair. The profile is auto-activated if an Environment Variable key matches the pattern. If the pattern starts with !, activation happens if the remaining pattern is not matched. The pattern matches if the Environment Variable value is exactly pattern, or the regex pattern is found in it. An empty pattern (e.g. env: "key=") always only matches if the Environment Variable is undefined or empty.", + "examples": [ + "ENV=production" + ] + }, + "kubeContext": { + "type": "string", + "description": "a Kubernetes context for which the profile is auto-activated.", + "x-intellij-html-description": "a Kubernetes context for which the profile is auto-activated.", + "examples": [ + "minikube" + ] + } + }, + "preferredOrder": [ + "env", + "kubeContext", + "command" + ], + "additionalProperties": false, + "description": "criteria by which a profile is auto-activated.", + "x-intellij-html-description": "criteria by which a profile is auto-activated." + }, + "Artifact": { + "required": [ + "image" + ], + "anyOf": [ + { + "properties": { + "context": { + "type": "string", + "description": "directory containing the artifact's sources.", + "x-intellij-html-description": "directory containing the artifact's sources.", + "default": "." + }, + "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": { + "$ref": "#/definitions/Sync", + "description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "x-intellij-html-description": "beta local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "default": "infer: [\"**/*\"]" + } + }, + "preferredOrder": [ + "image", + "context", + "sync" + ], + "additionalProperties": false + }, + { + "properties": { + "context": { + "type": "string", + "description": "directory containing the artifact's sources.", + "x-intellij-html-description": "directory containing the artifact's sources.", + "default": "." + }, + "docker": { + "$ref": "#/definitions/DockerArtifact", + "description": "*beta* describes an artifact built from a Dockerfile.", + "x-intellij-html-description": "beta describes an artifact built from a Dockerfile." + }, + "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": { + "$ref": "#/definitions/Sync", + "description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "x-intellij-html-description": "beta local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "default": "infer: [\"**/*\"]" + } + }, + "preferredOrder": [ + "image", + "context", + "sync", + "docker" + ], + "additionalProperties": false + }, + { + "properties": { + "bazel": { + "$ref": "#/definitions/BazelArtifact", + "description": "*beta* requires bazel CLI to be installed and the sources to contain [Bazel](https://bazel.build/) configuration files.", + "x-intellij-html-description": "beta requires bazel CLI to be installed and the sources to contain Bazel configuration files." + }, + "context": { + "type": "string", + "description": "directory containing the artifact's sources.", + "x-intellij-html-description": "directory containing the artifact's sources.", + "default": "." + }, + "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": { + "$ref": "#/definitions/Sync", + "description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "x-intellij-html-description": "beta local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "default": "infer: [\"**/*\"]" + } + }, + "preferredOrder": [ + "image", + "context", + "sync", + "bazel" + ], + "additionalProperties": false + }, + { + "properties": { + "context": { + "type": "string", + "description": "directory containing the artifact's sources.", + "x-intellij-html-description": "directory containing the artifact's sources.", + "default": "." + }, + "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" + ] + }, + "jib": { + "$ref": "#/definitions/JibArtifact", + "description": "builds images using the [Jib plugins for Maven or Gradle](https://github.com/GoogleContainerTools/jib/).", + "x-intellij-html-description": "builds images using the Jib plugins for Maven or Gradle." + }, + "sync": { + "$ref": "#/definitions/Sync", + "description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "x-intellij-html-description": "beta local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "default": "infer: [\"**/*\"]" + } + }, + "preferredOrder": [ + "image", + "context", + "sync", + "jib" + ], + "additionalProperties": false + }, + { + "properties": { + "context": { + "type": "string", + "description": "directory containing the artifact's sources.", + "x-intellij-html-description": "directory containing the artifact's sources.", + "default": "." + }, + "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" + ] + }, + "kaniko": { + "$ref": "#/definitions/KanikoArtifact", + "description": "builds images using [kaniko](https://github.com/GoogleContainerTools/kaniko).", + "x-intellij-html-description": "builds images using kaniko." + }, + "sync": { + "$ref": "#/definitions/Sync", + "description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "x-intellij-html-description": "beta local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "default": "infer: [\"**/*\"]" + } + }, + "preferredOrder": [ + "image", + "context", + "sync", + "kaniko" + ], + "additionalProperties": false + }, + { + "properties": { + "buildpacks": { + "$ref": "#/definitions/BuildpackArtifact", + "description": "builds images using [Cloud Native Buildpacks](https://buildpacks.io/).", + "x-intellij-html-description": "builds images using Cloud Native Buildpacks." + }, + "context": { + "type": "string", + "description": "directory containing the artifact's sources.", + "x-intellij-html-description": "directory containing the artifact's sources.", + "default": "." + }, + "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": { + "$ref": "#/definitions/Sync", + "description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "x-intellij-html-description": "beta local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "default": "infer: [\"**/*\"]" + } + }, + "preferredOrder": [ + "image", + "context", + "sync", + "buildpacks" + ], + "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": "*beta* builds images using a custom build script written by the user.", + "x-intellij-html-description": "beta 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": { + "$ref": "#/definitions/Sync", + "description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "x-intellij-html-description": "beta local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "default": "infer: [\"**/*\"]" + } + }, + "preferredOrder": [ + "image", + "context", + "sync", + "custom" + ], + "additionalProperties": false + } + ], + "description": "items that need to be built, along with the context in which they should be built.", + "x-intellij-html-description": "items that need to be built, along with the context in which they should be built." + }, + "Auto": { + "description": "cannot be customized.", + "x-intellij-html-description": "cannot be customized." + }, + "BazelArtifact": { + "required": [ + "target" + ], + "properties": { + "args": { + "items": { + "type": "string" + }, + "type": "array", + "description": "additional args to pass to `bazel build`.", + "x-intellij-html-description": "additional args to pass to bazel build.", + "default": "[]", + "examples": [ + "[\"-flag\", \"--otherflag\"]" + ] + }, + "target": { + "type": "string", + "description": "`bazel build` target to run.", + "x-intellij-html-description": "bazel build target to run.", + "examples": [ + "//:skaffold_example.tar" + ] + } + }, + "preferredOrder": [ + "target", + "args" + ], + "additionalProperties": false, + "description": "describes an artifact built with [Bazel](https://bazel.build/).", + "x-intellij-html-description": "describes an artifact built with Bazel." + }, + "BuildConfig": { + "anyOf": [ + { + "properties": { + "artifacts": { + "items": { + "$ref": "#/definitions/Artifact" + }, + "type": "array", + "description": "the images you're going to be building.", + "x-intellij-html-description": "the images you're going to be building." + }, + "insecureRegistries": { + "items": { + "type": "string" + }, + "type": "array", + "description": "a list of registries declared by the user to be insecure. These registries will be connected to via HTTP instead of HTTPS.", + "x-intellij-html-description": "a list of registries declared by the user to be insecure. These registries will be connected to via HTTP instead of HTTPS.", + "default": "[]" + }, + "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: {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": [ + "artifacts", + "insecureRegistries", + "tagPolicy" + ], + "additionalProperties": false + }, + { + "properties": { + "artifacts": { + "items": { + "$ref": "#/definitions/Artifact" + }, + "type": "array", + "description": "the images you're going to be building.", + "x-intellij-html-description": "the images you're going to be building." + }, + "insecureRegistries": { + "items": { + "type": "string" + }, + "type": "array", + "description": "a list of registries declared by the user to be insecure. These registries will be connected to via HTTP instead of HTTPS.", + "x-intellij-html-description": "a list of registries declared by the user to be insecure. These registries will be connected to via HTTP instead of HTTPS.", + "default": "[]" + }, + "local": { + "$ref": "#/definitions/LocalBuild", + "description": "*beta* describes how to do a build on the local docker daemon and optionally push to a repository.", + "x-intellij-html-description": "beta describes how to do a build on the local docker daemon and optionally push to a repository." + }, + "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: {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": [ + "artifacts", + "insecureRegistries", + "tagPolicy", + "local" + ], + "additionalProperties": false + }, + { + "properties": { + "artifacts": { + "items": { + "$ref": "#/definitions/Artifact" + }, + "type": "array", + "description": "the images you're going to be building.", + "x-intellij-html-description": "the images you're going to be building." + }, + "googleCloudBuild": { + "$ref": "#/definitions/GoogleCloudBuild", + "description": "*beta* describes how to do a remote build on [Google Cloud Build](https://cloud.google.com/cloud-build/).", + "x-intellij-html-description": "beta describes how to do a remote build on Google Cloud Build." + }, + "insecureRegistries": { + "items": { + "type": "string" + }, + "type": "array", + "description": "a list of registries declared by the user to be insecure. These registries will be connected to via HTTP instead of HTTPS.", + "x-intellij-html-description": "a list of registries declared by the user to be insecure. These registries will be connected to via HTTP instead of HTTPS.", + "default": "[]" + }, + "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: {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": [ + "artifacts", + "insecureRegistries", + "tagPolicy", + "googleCloudBuild" + ], + "additionalProperties": false + }, + { + "properties": { + "artifacts": { + "items": { + "$ref": "#/definitions/Artifact" + }, + "type": "array", + "description": "the images you're going to be building.", + "x-intellij-html-description": "the images you're going to be building." + }, + "cluster": { + "$ref": "#/definitions/ClusterDetails", + "description": "*beta* describes how to do an on-cluster build.", + "x-intellij-html-description": "beta describes how to do an on-cluster build." + }, + "insecureRegistries": { + "items": { + "type": "string" + }, + "type": "array", + "description": "a list of registries declared by the user to be insecure. These registries will be connected to via HTTP instead of HTTPS.", + "x-intellij-html-description": "a list of registries declared by the user to be insecure. These registries will be connected to via HTTP instead of HTTPS.", + "default": "[]" + }, + "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: {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": [ + "artifacts", + "insecureRegistries", + "tagPolicy", + "cluster" + ], + "additionalProperties": false + } + ], + "description": "contains all the configuration for the build steps.", + "x-intellij-html-description": "contains all the configuration for the build steps." + }, + "BuildpackArtifact": { + "required": [ + "builder" + ], + "properties": { + "builder": { + "type": "string", + "description": "builder image used.", + "x-intellij-html-description": "builder image used." + }, + "buildpacks": { + "items": { + "type": "string" + }, + "type": "array", + "description": "a list of strings, where each string is a specific buildpack to use with the builder. If you specify buildpacks the builder image automatic detection will be ignored. These buildpacks will be used to build the Image from your source code. Order matters.", + "x-intellij-html-description": "a list of strings, where each string is a specific buildpack to use with the builder. If you specify buildpacks the builder image automatic detection will be ignored. These buildpacks will be used to build the Image from your source code. Order matters.", + "default": "[]" + }, + "dependencies": { + "$ref": "#/definitions/BuildpackDependencies", + "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." + }, + "env": { + "items": { + "type": "string" + }, + "type": "array", + "description": "environment variables, in the `key=value` form, passed to the build. Values can use the go template syntax.", + "x-intellij-html-description": "environment variables, in the key=value form, passed to the build. Values can use the go template syntax.", + "default": "[]", + "examples": [ + "[\"key1=value1\", \"key2=value2\", \"key3={{.ENV_VARIABLE}}\"]" + ] + }, + "projectDescriptor": { + "type": "string", + "description": "path to the project descriptor file.", + "x-intellij-html-description": "path to the project descriptor file.", + "default": "project.toml" + }, + "runImage": { + "type": "string", + "description": "overrides the stack's default run image.", + "x-intellij-html-description": "overrides the stack's default run image." + }, + "trustBuilder": { + "type": "boolean", + "description": "indicates that the builder should be trusted.", + "x-intellij-html-description": "indicates that the builder should be trusted.", + "default": "false" + } + }, + "preferredOrder": [ + "builder", + "runImage", + "env", + "buildpacks", + "trustBuilder", + "projectDescriptor", + "dependencies" + ], + "additionalProperties": false, + "description": "*alpha* describes an artifact built using [Cloud Native Buildpacks](https://buildpacks.io/). It can be used to build images out of project's sources without any additional configuration.", + "x-intellij-html-description": "alpha describes an artifact built using Cloud Native Buildpacks. It can be used to build images out of project's sources without any additional configuration." + }, + "BuildpackDependencies": { + "properties": { + "ignore": { + "items": { + "type": "string" + }, + "type": "array", + "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": { + "items": { + "type": "string" + }, + "type": "array", + "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": "[]" + } + }, + "preferredOrder": [ + "paths", + "ignore" + ], + "additionalProperties": false, + "description": "*alpha* used to specify dependencies for an artifact built by buildpacks.", + "x-intellij-html-description": "alpha used to specify dependencies for an artifact built by buildpacks." + }, + "ClusterDetails": { + "properties": { + "HTTPS_PROXY": { + "type": "string", + "description": "for kaniko pod.", + "x-intellij-html-description": "for kaniko pod." + }, + "HTTP_PROXY": { + "type": "string", + "description": "for kaniko pod.", + "x-intellij-html-description": "for kaniko pod." + }, + "annotations": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "description": "describes the Kubernetes annotations for the pod.", + "x-intellij-html-description": "describes the Kubernetes annotations for the pod.", + "default": "{}" + }, + "concurrency": { + "type": "integer", + "description": "how many artifacts can be built concurrently. 0 means \"no-limit\".", + "x-intellij-html-description": "how many artifacts can be built concurrently. 0 means "no-limit".", + "default": "0" + }, + "dockerConfig": { + "$ref": "#/definitions/DockerConfig", + "description": "describes how to mount the local Docker configuration into a pod.", + "x-intellij-html-description": "describes how to mount the local Docker configuration into a pod." + }, + "namespace": { + "type": "string", + "description": "Kubernetes namespace. Defaults to current namespace in Kubernetes configuration.", + "x-intellij-html-description": "Kubernetes namespace. Defaults to current namespace in Kubernetes configuration." + }, + "pullSecretMountPath": { + "type": "string", + "description": "path the pull secret will be mounted at within the running container.", + "x-intellij-html-description": "path the pull secret will be mounted at within the running container." + }, + "pullSecretName": { + "type": "string", + "description": "name of the Kubernetes secret for pulling base images and pushing the final image. If given, the secret needs to contain the Google Cloud service account secret key under the key `kaniko-secret`.", + "x-intellij-html-description": "name of the Kubernetes secret for pulling base images and pushing the final image. If given, the secret needs to contain the Google Cloud service account secret key under the key kaniko-secret.", + "default": "kaniko-secret" + }, + "pullSecretPath": { + "type": "string", + "description": "path to the Google Cloud service account secret key file.", + "x-intellij-html-description": "path to the Google Cloud service account secret key file." + }, + "randomDockerConfigSecret": { + "type": "boolean", + "description": "adds a random UUID postfix to the default name of the docker secret to facilitate parallel builds, e.g. docker-cfgfd154022-c761-416f-8eb3-cf8258450b85.", + "x-intellij-html-description": "adds a random UUID postfix to the default name of the docker secret to facilitate parallel builds, e.g. docker-cfgfd154022-c761-416f-8eb3-cf8258450b85.", + "default": "false" + }, + "randomPullSecret": { + "type": "boolean", + "description": "adds a random UUID postfix to the default name of the pull secret to facilitate parallel builds, e.g. kaniko-secretdocker-cfgfd154022-c761-416f-8eb3-cf8258450b85.", + "x-intellij-html-description": "adds a random UUID postfix to the default name of the pull secret to facilitate parallel builds, e.g. kaniko-secretdocker-cfgfd154022-c761-416f-8eb3-cf8258450b85.", + "default": "false" + }, + "resources": { + "$ref": "#/definitions/ResourceRequirements", + "description": "define the resource requirements for the kaniko pod.", + "x-intellij-html-description": "define the resource requirements for the kaniko pod." + }, + "runAsUser": { + "type": "integer", + "description": "defines the UID to request for running the container. If omitted, no SeurityContext will be specified for the pod and will therefore be inherited from the service account.", + "x-intellij-html-description": "defines the UID to request for running the container. If omitted, no SeurityContext will be specified for the pod and will therefore be inherited from the service account." + }, + "serviceAccount": { + "type": "string", + "description": "describes the Kubernetes service account to use for the pod. Defaults to 'default'.", + "x-intellij-html-description": "describes the Kubernetes service account to use for the pod. Defaults to 'default'." + }, + "timeout": { + "type": "string", + "description": "amount of time (in seconds) that this build is allowed to run. Defaults to 20 minutes (`20m`).", + "x-intellij-html-description": "amount of time (in seconds) that this build is allowed to run. Defaults to 20 minutes (20m)." + }, + "tolerations": { + "items": {}, + "type": "array", + "description": "describes the Kubernetes tolerations for the pod.", + "x-intellij-html-description": "describes the Kubernetes tolerations for the pod.", + "default": "[]" + }, + "volumes": { + "items": {}, + "type": "array", + "description": "defines container mounts for ConfigMap and Secret resources.", + "x-intellij-html-description": "defines container mounts for ConfigMap and Secret resources.", + "default": "[]" + } + }, + "preferredOrder": [ + "HTTP_PROXY", + "HTTPS_PROXY", + "pullSecretPath", + "pullSecretName", + "pullSecretMountPath", + "namespace", + "timeout", + "dockerConfig", + "serviceAccount", + "tolerations", + "annotations", + "runAsUser", + "resources", + "concurrency", + "volumes", + "randomPullSecret", + "randomDockerConfigSecret" + ], + "additionalProperties": false, + "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 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": [ + "buildCommand", + "dependencies" + ], + "additionalProperties": false, + "description": "*beta* 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": "beta 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": { + "command": { + "type": "string", + "description": "represents a custom command that skaffold executes to obtain dependencies. The output of this command *must* be a valid JSON array.", + "x-intellij-html-description": "represents a custom command that skaffold executes to obtain dependencies. The output of this command must be a valid JSON array." + }, + "dockerfile": { + "$ref": "#/definitions/DockerfileDependency", + "description": "should be set if the artifact is built from a Dockerfile, from which skaffold can determine dependencies.", + "x-intellij-html-description": "should be set if the artifact is built from a Dockerfile, from which skaffold can determine dependencies." + }, + "ignore": { + "items": { + "type": "string" + }, + "type": "array", + "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": { + "items": { + "type": "string" + }, + "type": "array", + "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": "[]" + } + }, + "preferredOrder": [ + "dockerfile", + "command", + "paths", + "ignore" + ], + "additionalProperties": false, + "description": "*beta* used to specify dependencies for an artifact built by a custom build script. Either `dockerfile` or `paths` should be specified for file watching to work as expected.", + "x-intellij-html-description": "beta used to specify dependencies for an artifact built by a custom build script. Either dockerfile or paths should be specified for file watching to work as expected." + }, + "CustomTemplateTagger": { + "required": [ + "template" + ], + "properties": { + "components": { + "items": { + "$ref": "#/definitions/TaggerComponent" + }, + "type": "array", + "description": "TaggerComponents that the template (see field above) can be executed against.", + "x-intellij-html-description": "TaggerComponents that the template (see field above) can be executed against." + }, + "template": { + "type": "string", + "description": "used to produce the image name and tag. See golang [text/template](https://golang.org/pkg/text/template/). The template is executed against the provided components with those variables injected.", + "x-intellij-html-description": "used to produce the image name and tag. See golang text/template. The template is executed against the provided components with those variables injected.", + "examples": [ + "{{.DATE}}" + ] + } + }, + "preferredOrder": [ + "template", + "components" + ], + "additionalProperties": false, + "description": "*beta* tags images with a configurable template string.", + "x-intellij-html-description": "beta tags images with a configurable template string." + }, + "DateTimeTagger": { + "properties": { + "format": { + "type": "string", + "description": "formats the date and time. See [#Time.Format](https://golang.org/pkg/time/#Time.Format).", + "x-intellij-html-description": "formats the date and time. See #Time.Format.", + "default": "2006-01-02_15-04-05.999_MST" + }, + "timezone": { + "type": "string", + "description": "sets the timezone for the date and time. See [Time.LoadLocation](https://golang.org/pkg/time/#Time.LoadLocation). Defaults to the local timezone.", + "x-intellij-html-description": "sets the timezone for the date and time. See Time.LoadLocation. Defaults to the local timezone." + } + }, + "preferredOrder": [ + "format", + "timezone" + ], + "additionalProperties": false, + "description": "*beta* tags images with the build timestamp.", + "x-intellij-html-description": "beta tags images with the build timestamp." + }, + "DeployConfig": { + "properties": { + "helm": { + "$ref": "#/definitions/HelmDeploy", + "description": "*beta* uses the `helm` CLI to apply the charts to the cluster.", + "x-intellij-html-description": "beta uses the helm CLI to apply the charts to the cluster." + }, + "kubeContext": { + "type": "string", + "description": "Kubernetes context that Skaffold should deploy to.", + "x-intellij-html-description": "Kubernetes context that Skaffold should deploy to.", + "examples": [ + "minikube" + ] + }, + "kubectl": { + "$ref": "#/definitions/KubectlDeploy", + "description": "*beta* uses a client side `kubectl apply` to deploy manifests. You'll need a `kubectl` CLI version installed that's compatible with your cluster.", + "x-intellij-html-description": "beta uses a client side kubectl apply to deploy manifests. You'll need a kubectl CLI version installed that's compatible with your cluster." + }, + "kustomize": { + "$ref": "#/definitions/KustomizeDeploy", + "description": "*beta* uses the `kustomize` CLI to \"patch\" a deployment for a target environment.", + "x-intellij-html-description": "beta uses the kustomize CLI to "patch" a deployment for a target environment." + }, + "logs": { + "$ref": "#/definitions/LogsConfig", + "description": "configures how container logs are printed as a result of a deployment.", + "x-intellij-html-description": "configures how container logs are printed as a result of a deployment." + }, + "statusCheckDeadlineSeconds": { + "type": "integer", + "description": "*beta* deadline for deployments to stabilize in seconds.", + "x-intellij-html-description": "beta deadline for deployments to stabilize in seconds." + } + }, + "preferredOrder": [ + "helm", + "kubectl", + "kustomize", + "statusCheckDeadlineSeconds", + "kubeContext", + "logs" + ], + "additionalProperties": false, + "description": "contains all the configuration needed by the deploy steps.", + "x-intellij-html-description": "contains all the configuration needed by the deploy steps." + }, + "DockerArtifact": { + "properties": { + "buildArgs": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "description": "arguments passed to the docker build.", + "x-intellij-html-description": "arguments passed to the docker build.", + "default": "{}", + "examples": [ + "{\"key1\": \"value1\", \"key2\": \"value2\"}" + ] + }, + "cacheFrom": { + "items": { + "type": "string" + }, + "type": "array", + "description": "the Docker images used as cache sources.", + "x-intellij-html-description": "the Docker images used as cache sources.", + "default": "[]", + "examples": [ + "[\"golang:1.10.1-alpine3.7\", \"alpine:3.7\"]" + ] + }, + "dockerfile": { + "type": "string", + "description": "locates the Dockerfile relative to workspace.", + "x-intellij-html-description": "locates the Dockerfile relative to workspace.", + "default": "Dockerfile" + }, + "network": { + "type": "string", + "description": "passed through to docker and overrides the network configuration of docker builder. If unset, use whatever is configured in the underlying docker daemon. Valid modes are `host`: use the host's networking stack. `bridge`: use the bridged network configuration. `none`: no networking in the container.", + "x-intellij-html-description": "passed through to docker and overrides the network configuration of docker builder. If unset, use whatever is configured in the underlying docker daemon. Valid modes are host: use the host's networking stack. bridge: use the bridged network configuration. none: no networking in the container.", + "enum": [ + "host", + "bridge", + "none" + ] + }, + "noCache": { + "type": "boolean", + "description": "used to pass in --no-cache to docker build to prevent caching.", + "x-intellij-html-description": "used to pass in --no-cache to docker build to prevent caching.", + "default": "false" + }, + "target": { + "type": "string", + "description": "Dockerfile target name to build.", + "x-intellij-html-description": "Dockerfile target name to build." + } + }, + "preferredOrder": [ + "dockerfile", + "target", + "buildArgs", + "network", + "cacheFrom", + "noCache" + ], + "additionalProperties": false, + "description": "describes an artifact built from a Dockerfile, usually using `docker build`.", + "x-intellij-html-description": "describes an artifact built from a Dockerfile, usually using docker build." + }, + "DockerConfig": { + "properties": { + "path": { + "type": "string", + "description": "path to the docker `config.json`.", + "x-intellij-html-description": "path to the docker config.json." + }, + "secretName": { + "type": "string", + "description": "Kubernetes secret that contains the `config.json` Docker configuration. Note that the expected secret type is not 'kubernetes.io/dockerconfigjson' but 'Opaque'.", + "x-intellij-html-description": "Kubernetes secret that contains the config.json Docker configuration. Note that the expected secret type is not 'kubernetes.io/dockerconfigjson' but 'Opaque'." + } + }, + "preferredOrder": [ + "path", + "secretName" + ], + "additionalProperties": false, + "description": "contains information about the docker `config.json` to mount.", + "x-intellij-html-description": "contains information about the docker config.json to mount." + }, + "DockerfileDependency": { + "properties": { + "buildArgs": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "description": "key/value pairs used to resolve values of `ARG` instructions in a Dockerfile. Values can be constants or environment variables via the go template syntax.", + "x-intellij-html-description": "key/value pairs used to resolve values of ARG instructions in a Dockerfile. Values can be constants or environment variables via the go template syntax.", + "default": "{}", + "examples": [ + "{\"key1\": \"value1\", \"key2\": \"value2\", \"key3\": \"'{{.ENV_VARIABLE}}'\"}" + ] + }, + "path": { + "type": "string", + "description": "locates the Dockerfile relative to workspace.", + "x-intellij-html-description": "locates the Dockerfile relative to workspace." + } + }, + "preferredOrder": [ + "path", + "buildArgs" + ], + "additionalProperties": false, + "description": "*beta* used to specify a custom build artifact that is built from a Dockerfile. This allows skaffold to determine dependencies from the Dockerfile.", + "x-intellij-html-description": "beta used to specify a custom build artifact that is built from a Dockerfile. This allows skaffold to determine dependencies from the Dockerfile." + }, + "EnvTemplateTagger": { + "required": [ + "template" + ], + "properties": { + "template": { + "type": "string", + "description": "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.", + "x-intellij-html-description": "used to produce the image name and tag. See golang text/template. The template is executed against the current environment, with those variables injected.", + "examples": [ + "{{.RELEASE}}" + ] + } + }, + "preferredOrder": [ + "template" + ], + "additionalProperties": false, + "description": "*beta* tags images with a configurable template string.", + "x-intellij-html-description": "beta tags images with a configurable template string." + }, + "GitTagger": { + "properties": { + "prefix": { + "type": "string", + "description": "adds a fixed prefix to the tag.", + "x-intellij-html-description": "adds a fixed prefix to the tag." + }, + "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. `TreeSha`: use the full tree hash of the artifact workingdir. `AbbrevTreeSha`: use the abbreviated tree hash of the artifact workingdir.", + "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. TreeSha: use the full tree hash of the artifact workingdir. AbbrevTreeSha: use the abbreviated tree hash of the artifact workingdir." + } + }, + "preferredOrder": [ + "variant", + "prefix" + ], + "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." + }, + "GoogleCloudBuild": { + "properties": { + "concurrency": { + "type": "integer", + "description": "how many artifacts can be built concurrently. 0 means \"no-limit\".", + "x-intellij-html-description": "how many artifacts can be built concurrently. 0 means "no-limit".", + "default": "0" + }, + "diskSizeGb": { + "type": "integer", + "description": "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).", + "x-intellij-html-description": "disk size of the VM that runs the build. See Cloud Build Reference." + }, + "dockerImage": { + "type": "string", + "description": "image that runs a Docker build. See [Cloud Builders](https://cloud.google.com/cloud-build/docs/cloud-builders).", + "x-intellij-html-description": "image that runs a Docker build. See Cloud Builders.", + "default": "gcr.io/cloud-builders/docker" + }, + "gradleImage": { + "type": "string", + "description": "image that runs a Gradle build. See [Cloud Builders](https://cloud.google.com/cloud-build/docs/cloud-builders).", + "x-intellij-html-description": "image that runs a Gradle build. See Cloud Builders.", + "default": "gcr.io/cloud-builders/gradle" + }, + "kanikoImage": { + "type": "string", + "description": "image that runs a Kaniko build. See [Cloud Builders](https://cloud.google.com/cloud-build/docs/cloud-builders).", + "x-intellij-html-description": "image that runs a Kaniko build. See Cloud Builders.", + "default": "gcr.io/kaniko-project/executor" + }, + "logStreamingOption": { + "type": "string", + "description": "specifies the behavior when writing build logs to Google Cloud Storage. Valid options are: `STREAM_DEFAULT`: Service may automatically determine build log streaming behavior. `STREAM_ON`: Build logs should be streamed to Google Cloud Storage. `STREAM_OFF`: Build logs should not be streamed to Google Cloud Storage; they will be written when the build is completed. See [Cloud Build Reference](https://cloud.google.com/cloud-build/docs/api/reference/rest/v1/projects.builds#logstreamingoption).", + "x-intellij-html-description": "specifies the behavior when writing build logs to Google Cloud Storage. Valid options are: STREAM_DEFAULT: Service may automatically determine build log streaming behavior. STREAM_ON: Build logs should be streamed to Google Cloud Storage. STREAM_OFF: Build logs should not be streamed to Google Cloud Storage; they will be written when the build is completed. See Cloud Build Reference." + }, + "logging": { + "type": "string", + "description": "specifies the logging mode. Valid modes are: `LOGGING_UNSPECIFIED`: The service determines the logging mode. `LEGACY`: Stackdriver logging and Cloud Storage logging are enabled (default). `GCS_ONLY`: Only Cloud Storage logging is enabled. See [Cloud Build Reference](https://cloud.google.com/cloud-build/docs/api/reference/rest/v1/projects.builds#loggingmode).", + "x-intellij-html-description": "specifies the logging mode. Valid modes are: LOGGING_UNSPECIFIED: The service determines the logging mode. LEGACY: Stackdriver logging and Cloud Storage logging are enabled (default). GCS_ONLY: Only Cloud Storage logging is enabled. See Cloud Build Reference." + }, + "machineType": { + "type": "string", + "description": "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).", + "x-intellij-html-description": "type of the VM that runs the build. See Cloud Build Reference." + }, + "mavenImage": { + "type": "string", + "description": "image that runs a Maven build. See [Cloud Builders](https://cloud.google.com/cloud-build/docs/cloud-builders).", + "x-intellij-html-description": "image that runs a Maven build. See Cloud Builders.", + "default": "gcr.io/cloud-builders/mvn" + }, + "packImage": { + "type": "string", + "description": "image that runs a Cloud Native Buildpacks build. See [Cloud Builders](https://cloud.google.com/cloud-build/docs/cloud-builders).", + "x-intellij-html-description": "image that runs a Cloud Native Buildpacks build. See Cloud Builders.", + "default": "gcr.io/k8s-skaffold/pack" + }, + "projectId": { + "type": "string", + "description": "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.", + "x-intellij-html-description": "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." + }, + "timeout": { + "type": "string", + "description": "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).", + "x-intellij-html-description": "amount of time (in seconds) that this build should be allowed to run. See Cloud Build Reference." + }, + "workerPool": { + "type": "string", + "description": "configures a pool of workers to run the build.", + "x-intellij-html-description": "configures a pool of workers to run the build." + } + }, + "preferredOrder": [ + "projectId", + "diskSizeGb", + "machineType", + "timeout", + "logging", + "logStreamingOption", + "dockerImage", + "kanikoImage", + "mavenImage", + "gradleImage", + "packImage", + "concurrency", + "workerPool" + ], + "additionalProperties": false, + "description": "*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.", + "x-intellij-html-description": "beta describes how to do a remote build on Google Cloud Build. 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." + }, + "HelmConventionConfig": { + "properties": { + "explicitRegistry": { + "type": "boolean", + "description": "separates `image.registry` to the image config syntax. Useful for some charts e.g. `postgresql`.", + "x-intellij-html-description": "separates image.registry to the image config syntax. Useful for some charts e.g. postgresql.", + "default": "false" + } + }, + "preferredOrder": [ + "explicitRegistry" + ], + "additionalProperties": false, + "description": "image config in the syntax of image.repository and image.tag.", + "x-intellij-html-description": "image config in the syntax of image.repository and image.tag." + }, + "HelmDeploy": { + "required": [ + "releases" + ], + "properties": { + "flags": { + "$ref": "#/definitions/HelmDeployFlags", + "description": "additional option flags that are passed on the command line to `helm`.", + "x-intellij-html-description": "additional option flags that are passed on the command line to helm." + }, + "releases": { + "items": { + "$ref": "#/definitions/HelmRelease" + }, + "type": "array", + "description": "a list of Helm releases.", + "x-intellij-html-description": "a list of Helm releases." + } + }, + "preferredOrder": [ + "releases", + "flags" + ], + "additionalProperties": false, + "description": "*beta* uses the `helm` CLI to apply the charts to the cluster.", + "x-intellij-html-description": "beta uses the helm CLI to apply the charts to the cluster." + }, + "HelmDeployFlags": { + "properties": { + "global": { + "items": { + "type": "string" + }, + "type": "array", + "description": "additional flags passed on every command.", + "x-intellij-html-description": "additional flags passed on every command.", + "default": "[]" + }, + "install": { + "items": { + "type": "string" + }, + "type": "array", + "description": "additional flags passed to (`helm install`).", + "x-intellij-html-description": "additional flags passed to (helm install).", + "default": "[]" + }, + "upgrade": { + "items": { + "type": "string" + }, + "type": "array", + "description": "additional flags passed to (`helm upgrade`).", + "x-intellij-html-description": "additional flags passed to (helm upgrade).", + "default": "[]" + } + }, + "preferredOrder": [ + "global", + "install", + "upgrade" + ], + "additionalProperties": false, + "description": "additional option flags that are passed on the command line to `helm`.", + "x-intellij-html-description": "additional option flags that are passed on the command line to helm." + }, + "HelmFQNConfig": { + "properties": { + "property": { + "type": "string", + "description": "defines the image config.", + "x-intellij-html-description": "defines the image config." + } + }, + "preferredOrder": [ + "property" + ], + "additionalProperties": false, + "description": "image config to use the FullyQualifiedImageName as param to set.", + "x-intellij-html-description": "image config to use the FullyQualifiedImageName as param to set." + }, + "HelmImageStrategy": { + "anyOf": [ + { + "additionalProperties": false + }, + { + "properties": { + "fqn": { + "$ref": "#/definitions/HelmFQNConfig", + "description": "image configuration uses the syntax `IMAGE-NAME=IMAGE-REPOSITORY:IMAGE-TAG`.", + "x-intellij-html-description": "image configuration uses the syntax IMAGE-NAME=IMAGE-REPOSITORY:IMAGE-TAG." + } + }, + "preferredOrder": [ + "fqn" + ], + "additionalProperties": false + }, + { + "properties": { + "helm": { + "$ref": "#/definitions/HelmConventionConfig", + "description": "image configuration uses the syntax `IMAGE-NAME.repository=IMAGE-REPOSITORY, IMAGE-NAME.tag=IMAGE-TAG`.", + "x-intellij-html-description": "image configuration uses the syntax IMAGE-NAME.repository=IMAGE-REPOSITORY, IMAGE-NAME.tag=IMAGE-TAG." + } + }, + "preferredOrder": [ + "helm" + ], + "additionalProperties": false + } + ], + "description": "adds image configurations to the Helm `values` file.", + "x-intellij-html-description": "adds image configurations to the Helm values file." + }, + "HelmPackaged": { + "properties": { + "appVersion": { + "type": "string", + "description": "sets the `appVersion` on the chart to this version.", + "x-intellij-html-description": "sets the appVersion on the chart to this version." + }, + "version": { + "type": "string", + "description": "sets the `version` on the chart to this semver version.", + "x-intellij-html-description": "sets the version on the chart to this semver version." + } + }, + "preferredOrder": [ + "version", + "appVersion" + ], + "additionalProperties": false, + "description": "parameters for packaging helm chart (`helm package`).", + "x-intellij-html-description": "parameters for packaging helm chart (helm package)." + }, + "HelmRelease": { + "required": [ + "name", + "chartPath" + ], + "properties": { + "artifactOverrides": { + "description": "key value pairs. If present, Skaffold will send `--set-string` flag to Helm CLI and append all pairs after the flag.", + "x-intellij-html-description": "key value pairs. If present, Skaffold will send --set-string flag to Helm CLI and append all pairs after the flag." + }, + "chartPath": { + "type": "string", + "description": "path to the Helm chart.", + "x-intellij-html-description": "path to the Helm chart." + }, + "imageStrategy": { + "$ref": "#/definitions/HelmImageStrategy", + "description": "adds image configurations to the Helm `values` file.", + "x-intellij-html-description": "adds image configurations to the Helm values file." + }, + "name": { + "type": "string", + "description": "name of the Helm release.", + "x-intellij-html-description": "name of the Helm release." + }, + "namespace": { + "type": "string", + "description": "Kubernetes namespace.", + "x-intellij-html-description": "Kubernetes namespace." + }, + "overrides": { + "description": "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).", + "x-intellij-html-description": "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)." + }, + "packaged": { + "$ref": "#/definitions/HelmPackaged", + "description": "parameters for packaging helm chart (`helm package`).", + "x-intellij-html-description": "parameters for packaging helm chart (helm package)." + }, + "recreatePods": { + "type": "boolean", + "description": "if `true`, Skaffold will send `--recreate-pods` flag to Helm CLI when upgrading a new version of a chart in subsequent dev loop deploy.", + "x-intellij-html-description": "if true, Skaffold will send --recreate-pods flag to Helm CLI when upgrading a new version of a chart in subsequent dev loop deploy.", + "default": "false" + }, + "remote": { + "type": "boolean", + "description": "specifies whether the chart path is remote, or exists on the host filesystem.", + "x-intellij-html-description": "specifies whether the chart path is remote, or exists on the host filesystem.", + "default": "false" + }, + "setFiles": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "description": "key-value pairs. If present, Skaffold will send `--set-file` flag to Helm CLI and append all pairs after the flag.", + "x-intellij-html-description": "key-value pairs. If present, Skaffold will send --set-file flag to Helm CLI and append all pairs after the flag.", + "default": "{}" + }, + "setValueTemplates": { + "description": "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.", + "x-intellij-html-description": "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." + }, + "setValues": { + "description": "key-value pairs. If present, Skaffold will send `--set` flag to Helm CLI and append all pairs after the flag.", + "x-intellij-html-description": "key-value pairs. If present, Skaffold will send --set flag to Helm CLI and append all pairs after the flag." + }, + "skipBuildDependencies": { + "type": "boolean", + "description": "should build dependencies be skipped. Ignored when `remote: true`.", + "x-intellij-html-description": "should build dependencies be skipped. Ignored when remote: true.", + "default": "false" + }, + "upgradeOnChange": { + "type": "boolean", + "description": "specifies whether to upgrade helm chart on code changes. Default is `true` when helm chart is local (`remote: false`). Default is `false` if `remote: true`.", + "x-intellij-html-description": "specifies whether to upgrade helm chart on code changes. Default is true when helm chart is local (remote: false). Default is false if remote: true." + }, + "useHelmSecrets": { + "type": "boolean", + "description": "instructs skaffold to use secrets plugin on deployment.", + "x-intellij-html-description": "instructs skaffold to use secrets plugin on deployment.", + "default": "false" + }, + "valuesFiles": { + "items": { + "type": "string" + }, + "type": "array", + "description": "paths to the Helm `values` files.", + "x-intellij-html-description": "paths to the Helm values files.", + "default": "[]" + }, + "version": { + "type": "string", + "description": "version of the chart.", + "x-intellij-html-description": "version of the chart." + }, + "wait": { + "type": "boolean", + "description": "if `true`, Skaffold will send `--wait` flag to Helm CLI.", + "x-intellij-html-description": "if true, Skaffold will send --wait flag to Helm CLI.", + "default": "false" + } + }, + "preferredOrder": [ + "name", + "chartPath", + "valuesFiles", + "artifactOverrides", + "namespace", + "version", + "setValues", + "setValueTemplates", + "setFiles", + "wait", + "recreatePods", + "skipBuildDependencies", + "useHelmSecrets", + "remote", + "upgradeOnChange", + "overrides", + "packaged", + "imageStrategy" + ], + "additionalProperties": false, + "description": "describes a helm release to be deployed.", + "x-intellij-html-description": "describes a helm release to be deployed." + }, + "JSONPatch": { + "required": [ + "path" + ], + "properties": { + "from": { + "type": "string", + "description": "source position in the yaml, used for `copy` or `move` operations.", + "x-intellij-html-description": "source position in the yaml, used for copy or move operations." + }, + "op": { + "type": "string", + "description": "operation carried by the patch: `add`, `remove`, `replace`, `move`, `copy` or `test`.", + "x-intellij-html-description": "operation carried by the patch: add, remove, replace, move, copy or test.", + "default": "replace" + }, + "path": { + "type": "string", + "description": "position in the yaml where the operation takes place. For example, this targets the `dockerfile` of the first artifact built.", + "x-intellij-html-description": "position in the yaml where the operation takes place. For example, this targets the dockerfile of the first artifact built.", + "examples": [ + "/build/artifacts/0/docker/dockerfile" + ] + }, + "value": { + "description": "value to apply. Can be any portion of yaml.", + "x-intellij-html-description": "value to apply. Can be any portion of yaml." + } + }, + "preferredOrder": [ + "op", + "path", + "from", + "value" + ], + "additionalProperties": false, + "description": "patch to be applied by a profile.", + "x-intellij-html-description": "patch to be applied by a profile." + }, + "JibArtifact": { + "properties": { + "args": { + "items": { + "type": "string" + }, + "type": "array", + "description": "additional build flags passed to the builder.", + "x-intellij-html-description": "additional build flags passed to the builder.", + "default": "[]", + "examples": [ + "[\"--no-build-cache\"]" + ] + }, + "project": { + "type": "string", + "description": "selects which sub-project to build for multi-module builds.", + "x-intellij-html-description": "selects which sub-project to build for multi-module builds." + }, + "type": { + "type": "string", + "description": "the Jib builder type; normally determined automatically. Valid types are `maven`: for Maven. `gradle`: for Gradle.", + "x-intellij-html-description": "the Jib builder type; normally determined automatically. Valid types are maven: for Maven. gradle: for Gradle.", + "enum": [ + "maven", + "gradle" + ] + } + }, + "preferredOrder": [ + "project", + "args", + "type" + ], + "additionalProperties": false, + "description": "builds images using the [Jib plugins for Maven and Gradle](https://github.com/GoogleContainerTools/jib/).", + "x-intellij-html-description": "builds images using the Jib plugins for Maven and Gradle." + }, + "KanikoArtifact": { + "properties": { + "buildArgs": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "description": "arguments passed to the docker build. It also accepts environment variables via the go template syntax.", + "x-intellij-html-description": "arguments passed to the docker build. It also accepts environment variables via the go template syntax.", + "default": "{}", + "examples": [ + "{\"key1\": \"value1\", \"key2\": \"value2\", \"key3\": \"'{{.ENV_VARIABLE}}'\"}" + ] + }, + "cache": { + "$ref": "#/definitions/KanikoCache", + "description": "configures Kaniko caching. If a cache is specified, Kaniko will use a remote cache which will speed up builds.", + "x-intellij-html-description": "configures Kaniko caching. If a cache is specified, Kaniko will use a remote cache which will speed up builds." + }, + "dockerfile": { + "type": "string", + "description": "locates the Dockerfile relative to workspace.", + "x-intellij-html-description": "locates the Dockerfile relative to workspace.", + "default": "Dockerfile" + }, + "env": { + "items": {}, + "type": "array", + "description": "environment variables passed to the kaniko pod.", + "x-intellij-html-description": "environment variables passed to the kaniko pod.", + "default": "[]" + }, + "flags": { + "items": { + "type": "string" + }, + "type": "array", + "description": "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`.", + "x-intellij-html-description": "additional flags to be passed to Kaniko command line. See Kaniko Additional Flags. Deprecated - instead the named, unique fields should be used, e.g. buildArgs, cache, target.", + "default": "[]" + }, + "image": { + "type": "string", + "description": "Docker image used by the Kaniko pod. Defaults to the latest released version of `gcr.io/kaniko-project/executor`.", + "x-intellij-html-description": "Docker image used by the Kaniko pod. Defaults to the latest released version of gcr.io/kaniko-project/executor." + }, + "initImage": { + "type": "string", + "description": "image used to run init container which mounts kaniko context.", + "x-intellij-html-description": "image used to run init container which mounts kaniko context." + }, + "reproducible": { + "type": "boolean", + "description": "used to strip timestamps out of the built image.", + "x-intellij-html-description": "used to strip timestamps out of the built image.", + "default": "false" + }, + "skipTLS": { + "type": "boolean", + "description": "skips TLS verification when pulling and pushing the image.", + "x-intellij-html-description": "skips TLS verification when pulling and pushing the image.", + "default": "false" + }, + "target": { + "type": "string", + "description": "Dockerfile target name to build.", + "x-intellij-html-description": "Dockerfile target name to build." + }, + "volumeMounts": { + "items": {}, + "type": "array", + "description": "volume mounts passed to kaniko pod.", + "x-intellij-html-description": "volume mounts passed to kaniko pod.", + "default": "[]" + } + }, + "preferredOrder": [ + "flags", + "dockerfile", + "target", + "buildArgs", + "env", + "initImage", + "image", + "cache", + "reproducible", + "skipTLS", + "volumeMounts" + ], + "additionalProperties": false, + "description": "describes an artifact built from a Dockerfile, with kaniko.", + "x-intellij-html-description": "describes an artifact built from a Dockerfile, with kaniko." + }, + "KanikoCache": { + "properties": { + "hostPath": { + "type": "string", + "description": "specifies a path on the host that is mounted to each pod as read only cache volume containing base images. If set, must exist on each node and prepopulated with kaniko-warmer.", + "x-intellij-html-description": "specifies a path on the host that is mounted to each pod as read only cache volume containing base images. If set, must exist on each node and prepopulated with kaniko-warmer." + }, + "repo": { + "type": "string", + "description": "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).", + "x-intellij-html-description": "a remote repository to store cached layers. If none is specified, one will be inferred from the image name. See Kaniko Caching." + } + }, + "preferredOrder": [ + "repo", + "hostPath" + ], + "additionalProperties": false, + "description": "configures Kaniko caching. If a cache is specified, Kaniko will use a remote cache which will speed up builds.", + "x-intellij-html-description": "configures Kaniko caching. If a cache is specified, Kaniko will use a remote cache which will speed up builds." + }, + "KubectlDeploy": { + "properties": { + "flags": { + "$ref": "#/definitions/KubectlFlags", + "description": "additional flags passed to `kubectl`.", + "x-intellij-html-description": "additional flags passed to kubectl." + }, + "manifests": { + "items": { + "type": "string" + }, + "type": "array", + "description": "the Kubernetes yaml or json manifests.", + "x-intellij-html-description": "the Kubernetes yaml or json manifests.", + "default": "[\"k8s/*.yaml\"]" + }, + "remoteManifests": { + "items": { + "type": "string" + }, + "type": "array", + "description": "Kubernetes manifests in remote clusters.", + "x-intellij-html-description": "Kubernetes manifests in remote clusters.", + "default": "[]" + } + }, + "preferredOrder": [ + "manifests", + "remoteManifests", + "flags" + ], + "additionalProperties": false, + "description": "*beta* uses a client side `kubectl apply` to deploy manifests. You'll need a `kubectl` CLI version installed that's compatible with your cluster.", + "x-intellij-html-description": "beta uses a client side kubectl apply to deploy manifests. You'll need a kubectl CLI version installed that's compatible with your cluster." + }, + "KubectlFlags": { + "properties": { + "apply": { + "items": { + "type": "string" + }, + "type": "array", + "description": "additional flags passed on creations (`kubectl apply`).", + "x-intellij-html-description": "additional flags passed on creations (kubectl apply).", + "default": "[]" + }, + "delete": { + "items": { + "type": "string" + }, + "type": "array", + "description": "additional flags passed on deletions (`kubectl delete`).", + "x-intellij-html-description": "additional flags passed on deletions (kubectl delete).", + "default": "[]" + }, + "disableValidation": { + "type": "boolean", + "description": "passes the `--validate=false` flag to supported `kubectl` commands when enabled.", + "x-intellij-html-description": "passes the --validate=false flag to supported kubectl commands when enabled.", + "default": "false" + }, + "global": { + "items": { + "type": "string" + }, + "type": "array", + "description": "additional flags passed on every command.", + "x-intellij-html-description": "additional flags passed on every command.", + "default": "[]" + } + }, + "preferredOrder": [ + "global", + "apply", + "delete", + "disableValidation" + ], + "additionalProperties": false, + "description": "additional flags passed on the command line to kubectl either on every command (Global), on creations (Apply) or deletions (Delete).", + "x-intellij-html-description": "additional flags passed on the command line to kubectl either on every command (Global), on creations (Apply) or deletions (Delete)." + }, + "KustomizeDeploy": { + "properties": { + "buildArgs": { + "items": { + "type": "string" + }, + "type": "array", + "description": "additional args passed to `kustomize build`.", + "x-intellij-html-description": "additional args passed to kustomize build.", + "default": "[]" + }, + "flags": { + "$ref": "#/definitions/KubectlFlags", + "description": "additional flags passed to `kubectl`.", + "x-intellij-html-description": "additional flags passed to kubectl." + }, + "paths": { + "items": { + "type": "string" + }, + "type": "array", + "description": "path to Kustomization files.", + "x-intellij-html-description": "path to Kustomization files.", + "default": "[\".\"]" + } + }, + "preferredOrder": [ + "paths", + "flags", + "buildArgs" + ], + "additionalProperties": false, + "description": "*beta* uses the `kustomize` CLI to \"patch\" a deployment for a target environment.", + "x-intellij-html-description": "beta uses the kustomize CLI to "patch" a deployment for a target environment." + }, + "LocalBuild": { + "properties": { + "concurrency": { + "type": "integer", + "description": "how many artifacts can be built concurrently. 0 means \"no-limit\".", + "x-intellij-html-description": "how many artifacts can be built concurrently. 0 means "no-limit".", + "default": "1" + }, + "push": { + "type": "boolean", + "description": "should images be pushed to a registry. If not specified, images are pushed only if the current Kubernetes context connects to a remote cluster.", + "x-intellij-html-description": "should images be pushed to a registry. If not specified, images are pushed only if the current Kubernetes context connects to a remote cluster." + }, + "useBuildkit": { + "type": "boolean", + "description": "use BuildKit to build Docker images.", + "x-intellij-html-description": "use BuildKit to build Docker images.", + "default": "false" + }, + "useDockerCLI": { + "type": "boolean", + "description": "use `docker` command-line interface instead of Docker Engine APIs.", + "x-intellij-html-description": "use docker command-line interface instead of Docker Engine APIs.", + "default": "false" + } + }, + "preferredOrder": [ + "push", + "useDockerCLI", + "useBuildkit", + "concurrency" + ], + "additionalProperties": false, + "description": "*beta* describes how to do a build on the local docker daemon and optionally push to a repository.", + "x-intellij-html-description": "beta describes how to do a build on the local docker daemon and optionally push to a repository." + }, + "LogsConfig": { + "properties": { + "prefix": { + "type": "string", + "description": "defines the prefix shown on each log line. Valid values are `container`: prefix logs lines with the name of the container. `podAndContainer`: prefix logs lines with the names of the pod and of the container. `auto`: same as `podAndContainer` except that the pod name is skipped if it's the same as the container name. `none`: don't add a prefix.", + "x-intellij-html-description": "defines the prefix shown on each log line. Valid values are container: prefix logs lines with the name of the container. podAndContainer: prefix logs lines with the names of the pod and of the container. auto: same as podAndContainer except that the pod name is skipped if it's the same as the container name. none: don't add a prefix.", + "default": "auto", + "enum": [ + "container", + "podAndContainer", + "auto", + "none" + ] + } + }, + "preferredOrder": [ + "prefix" + ], + "additionalProperties": false, + "description": "configures how container logs are printed as a result of a deployment.", + "x-intellij-html-description": "configures how container logs are printed as a result of a deployment." + }, + "Metadata": { + "properties": { + "name": { + "type": "string", + "description": "an identifier for the project.", + "x-intellij-html-description": "an identifier for the project." + } + }, + "preferredOrder": [ + "name" + ], + "additionalProperties": false, + "description": "holds an optional name of the project.", + "x-intellij-html-description": "holds an optional name of the project." + }, + "PortForwardResource": { + "properties": { + "address": { + "type": "string", + "description": "local address to bind to. Defaults to the loopback address 127.0.0.1.", + "x-intellij-html-description": "local address to bind to. Defaults to the loopback address 127.0.0.1." + }, + "localPort": { + "type": "integer", + "description": "local port to forward to. If the port is unavailable, Skaffold will choose a random open port to forward to. *Optional*.", + "x-intellij-html-description": "local port to forward to. If the port is unavailable, Skaffold will choose a random open port to forward to. Optional." + }, + "namespace": { + "type": "string", + "description": "namespace of the resource to port forward.", + "x-intellij-html-description": "namespace of the resource to port forward." + }, + "port": { + "type": "integer", + "description": "resource port that will be forwarded.", + "x-intellij-html-description": "resource port that will be forwarded." + }, + "resourceName": { + "type": "string", + "description": "name of the Kubernetes resource to port forward.", + "x-intellij-html-description": "name of the Kubernetes resource to port forward." + }, + "resourceType": { + "type": "string", + "description": "Kubernetes type that should be port forwarded. Acceptable resource types include: `Service`, `Pod` and Controller resource type that has a pod spec: `ReplicaSet`, `ReplicationController`, `Deployment`, `StatefulSet`, `DaemonSet`, `Job`, `CronJob`.", + "x-intellij-html-description": "Kubernetes type that should be port forwarded. Acceptable resource types include: Service, Pod and Controller resource type that has a pod spec: ReplicaSet, ReplicationController, Deployment, StatefulSet, DaemonSet, Job, CronJob." + } + }, + "preferredOrder": [ + "resourceType", + "resourceName", + "namespace", + "port", + "address", + "localPort" + ], + "additionalProperties": false, + "description": "describes a resource to port forward.", + "x-intellij-html-description": "describes a resource to port forward." + }, + "Profile": { + "required": [ + "name" + ], + "properties": { + "activation": { + "items": { + "$ref": "#/definitions/Activation" + }, + "type": "array", + "description": "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.", + "x-intellij-html-description": "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." + }, + "build": { + "$ref": "#/definitions/BuildConfig", + "description": "describes how images are built.", + "x-intellij-html-description": "describes how images are built." + }, + "deploy": { + "$ref": "#/definitions/DeployConfig", + "description": "describes how images are deployed.", + "x-intellij-html-description": "describes how images are deployed." + }, + "name": { + "type": "string", + "description": "a unique profile name.", + "x-intellij-html-description": "a unique profile name.", + "examples": [ + "profile-prod" + ] + }, + "patches": { + "items": { + "$ref": "#/definitions/JSONPatch" + }, + "type": "array", + "description": "patches applied to the configuration. Patches use the JSON patch notation.", + "x-intellij-html-description": "patches applied to the configuration. Patches use the JSON patch notation." + }, + "portForward": { + "items": { + "$ref": "#/definitions/PortForwardResource" + }, + "type": "array", + "description": "describes user defined resources to port-forward.", + "x-intellij-html-description": "describes user defined resources to port-forward." + }, + "test": { + "items": { + "$ref": "#/definitions/TestCase" + }, + "type": "array", + "description": "describes how images are tested.", + "x-intellij-html-description": "describes how images are tested." + } + }, + "preferredOrder": [ + "name", + "activation", + "patches", + "build", + "test", + "deploy", + "portForward" + ], + "additionalProperties": false, + "description": "used to override any `build`, `test` or `deploy` configuration.", + "x-intellij-html-description": "used to override any build, test or deploy configuration." + }, + "ResourceRequirement": { + "properties": { + "cpu": { + "type": "string", + "description": "the number cores to be used.", + "x-intellij-html-description": "the number cores to be used.", + "examples": [ + "2`, `2.0` or `200m" + ] + }, + "ephemeralStorage": { + "type": "string", + "description": "the amount of Ephemeral storage to allocate to the pod.", + "x-intellij-html-description": "the amount of Ephemeral storage to allocate to the pod.", + "examples": [ + "1Gi` or `1000Mi" + ] + }, + "memory": { + "type": "string", + "description": "the amount of memory to allocate to the pod.", + "x-intellij-html-description": "the amount of memory to allocate to the pod.", + "examples": [ + "1Gi` or `1000Mi" + ] + }, + "resourceStorage": { + "type": "string", + "description": "the amount of resource storage to allocate to the pod.", + "x-intellij-html-description": "the amount of resource storage to allocate to the pod.", + "examples": [ + "1Gi` or `1000Mi" + ] + } + }, + "preferredOrder": [ + "cpu", + "memory", + "ephemeralStorage", + "resourceStorage" + ], + "additionalProperties": false, + "description": "stores the CPU/Memory requirements for the pod.", + "x-intellij-html-description": "stores the CPU/Memory requirements for the pod." + }, + "ResourceRequirements": { + "properties": { + "limits": { + "$ref": "#/definitions/ResourceRequirement", + "description": "[resource limits](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container) for the Kaniko pod.", + "x-intellij-html-description": "resource limits for the Kaniko pod." + }, + "requests": { + "$ref": "#/definitions/ResourceRequirement", + "description": "[resource requests](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container) for the Kaniko pod.", + "x-intellij-html-description": "resource requests for the Kaniko pod." + } + }, + "preferredOrder": [ + "requests", + "limits" + ], + "additionalProperties": false, + "description": "describes the resource requirements for the kaniko pod.", + "x-intellij-html-description": "describes the resource requirements for the kaniko pod." + }, + "ResourceType": { + "type": "string", + "description": "describes the Kubernetes resource types used for port forwarding.", + "x-intellij-html-description": "describes the Kubernetes resource types used for port forwarding." + }, + "ShaTagger": { + "description": "*beta* tags images with their sha256 digest.", + "x-intellij-html-description": "beta tags images with their sha256 digest." + }, + "SkaffoldConfig": { + "required": [ + "apiVersion", + "kind" + ], + "properties": { + "apiVersion": { + "type": "string", + "description": "version of the configuration.", + "x-intellij-html-description": "version of the configuration." + }, + "build": { + "$ref": "#/definitions/BuildConfig", + "description": "describes how images are built.", + "x-intellij-html-description": "describes how images are built." + }, + "deploy": { + "$ref": "#/definitions/DeployConfig", + "description": "describes how images are deployed.", + "x-intellij-html-description": "describes how images are deployed." + }, + "kind": { + "type": "string", + "description": "always `Config`.", + "x-intellij-html-description": "always Config.", + "default": "Config" + }, + "metadata": { + "$ref": "#/definitions/Metadata", + "description": "holds additional information about the config.", + "x-intellij-html-description": "holds additional information about the config." + }, + "portForward": { + "items": { + "$ref": "#/definitions/PortForwardResource" + }, + "type": "array", + "description": "describes user defined resources to port-forward.", + "x-intellij-html-description": "describes user defined resources to port-forward." + }, + "profiles": { + "items": { + "$ref": "#/definitions/Profile" + }, + "type": "array", + "description": "*beta* can override be used to `build`, `test` or `deploy` configuration.", + "x-intellij-html-description": "beta can override be used to build, test or deploy configuration." + }, + "test": { + "items": { + "$ref": "#/definitions/TestCase" + }, + "type": "array", + "description": "describes how images are tested.", + "x-intellij-html-description": "describes how images are tested." + } + }, + "preferredOrder": [ + "apiVersion", + "kind", + "metadata", + "build", + "test", + "deploy", + "portForward", + "profiles" + ], + "additionalProperties": false, + "description": "holds the fields parsed from the Skaffold configuration file (skaffold.yaml).", + "x-intellij-html-description": "holds the fields parsed from the Skaffold configuration file (skaffold.yaml)." + }, + "Sync": { + "properties": { + "auto": { + "$ref": "#/definitions/Auto", + "description": "delegates discovery of sync rules to the build system. Only available for jib and buildpacks.", + "x-intellij-html-description": "delegates discovery of sync rules to the build system. Only available for jib and buildpacks." + }, + "infer": { + "items": { + "type": "string" + }, + "type": "array", + "description": "file patterns which may be synced into the container The container destination is inferred by the builder based on the instructions of a Dockerfile. Available for docker and kaniko artifacts and custom artifacts that declare dependencies on a dockerfile.", + "x-intellij-html-description": "file patterns which may be synced into the container The container destination is inferred by the builder based on the instructions of a Dockerfile. Available for docker and kaniko artifacts and custom artifacts that declare dependencies on a dockerfile.", + "default": "[]" + }, + "manual": { + "items": { + "$ref": "#/definitions/SyncRule" + }, + "type": "array", + "description": "manual sync rules indicating the source and destination.", + "x-intellij-html-description": "manual sync rules indicating the source and destination." + } + }, + "preferredOrder": [ + "manual", + "infer", + "auto" + ], + "additionalProperties": false, + "description": "*beta* specifies what files to sync into the container. This is a list of sync rules indicating the intent to sync for source files. If no files are listed, sync all the files and infer the destination.", + "x-intellij-html-description": "beta specifies what files to sync into the container. This is a list of sync rules indicating the intent to sync for source files. If no files are listed, sync all the files and infer the destination.", + "default": "infer: [\"**/*\"]" + }, + "SyncRule": { + "required": [ + "src", + "dest" + ], + "properties": { + "dest": { + "type": "string", + "description": "destination path in the container where the files should be synced to.", + "x-intellij-html-description": "destination path in the container where the files should be synced to.", + "examples": [ + "\"app/\"" + ] + }, + "src": { + "type": "string", + "description": "a glob pattern to match local paths against. Directories should be delimited by `/` on all platforms.", + "x-intellij-html-description": "a glob pattern to match local paths against. Directories should be delimited by / on all platforms.", + "examples": [ + "\"css/**/*.css\"" + ] + }, + "strip": { + "type": "string", + "description": "specifies the path prefix to remove from the source path when transplanting the files into the destination folder.", + "x-intellij-html-description": "specifies the path prefix to remove from the source path when transplanting the files into the destination folder.", + "examples": [ + "\"css/\"" + ] + } + }, + "preferredOrder": [ + "src", + "dest", + "strip" + ], + "additionalProperties": false, + "description": "specifies which local files to sync to remote folders.", + "x-intellij-html-description": "specifies which local files to sync to remote folders." + }, + "TagPolicy": { + "properties": { + "customTemplate": { + "$ref": "#/definitions/CustomTemplateTagger", + "description": "*beta* tags images with a configurable template string *composed of other taggers*.", + "x-intellij-html-description": "beta tags images with a configurable template string composed of other taggers." + }, + "dateTime": { + "$ref": "#/definitions/DateTimeTagger", + "description": "*beta* tags images with the build timestamp.", + "x-intellij-html-description": "beta tags images with the build timestamp." + }, + "envTemplate": { + "$ref": "#/definitions/EnvTemplateTagger", + "description": "*beta* tags images with a configurable template string.", + "x-intellij-html-description": "beta tags images with a configurable template string." + }, + "gitCommit": { + "$ref": "#/definitions/GitTagger", + "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." + }, + "sha256": { + "$ref": "#/definitions/ShaTagger", + "description": "*beta* tags images with their sha256 digest.", + "x-intellij-html-description": "beta tags images with their sha256 digest." + } + }, + "preferredOrder": [ + "gitCommit", + "sha256", + "envTemplate", + "dateTime", + "customTemplate" + ], + "additionalProperties": false, + "description": "contains all the configuration for the tagging step.", + "x-intellij-html-description": "contains all the configuration for the tagging step." + }, + "TaggerComponent": { + "anyOf": [ + { + "properties": { + "name": { + "type": "string", + "description": "an identifier for the component.", + "x-intellij-html-description": "an identifier for the component." + } + }, + "preferredOrder": [ + "name" + ], + "additionalProperties": false + }, + { + "properties": { + "gitCommit": { + "$ref": "#/definitions/GitTagger", + "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." + }, + "name": { + "type": "string", + "description": "an identifier for the component.", + "x-intellij-html-description": "an identifier for the component." + } + }, + "preferredOrder": [ + "name", + "gitCommit" + ], + "additionalProperties": false + }, + { + "properties": { + "name": { + "type": "string", + "description": "an identifier for the component.", + "x-intellij-html-description": "an identifier for the component." + }, + "sha256": { + "$ref": "#/definitions/ShaTagger", + "description": "*beta* tags images with their sha256 digest.", + "x-intellij-html-description": "beta tags images with their sha256 digest." + } + }, + "preferredOrder": [ + "name", + "sha256" + ], + "additionalProperties": false + }, + { + "properties": { + "envTemplate": { + "$ref": "#/definitions/EnvTemplateTagger", + "description": "*beta* tags images with a configurable template string.", + "x-intellij-html-description": "beta tags images with a configurable template string." + }, + "name": { + "type": "string", + "description": "an identifier for the component.", + "x-intellij-html-description": "an identifier for the component." + } + }, + "preferredOrder": [ + "name", + "envTemplate" + ], + "additionalProperties": false + }, + { + "properties": { + "dateTime": { + "$ref": "#/definitions/DateTimeTagger", + "description": "*beta* tags images with the build timestamp.", + "x-intellij-html-description": "beta tags images with the build timestamp." + }, + "name": { + "type": "string", + "description": "an identifier for the component.", + "x-intellij-html-description": "an identifier for the component." + } + }, + "preferredOrder": [ + "name", + "dateTime" + ], + "additionalProperties": false + }, + { + "properties": { + "customTemplate": { + "$ref": "#/definitions/CustomTemplateTagger", + "description": "*beta* tags images with a configurable template string *composed of other taggers*.", + "x-intellij-html-description": "beta tags images with a configurable template string composed of other taggers." + }, + "name": { + "type": "string", + "description": "an identifier for the component.", + "x-intellij-html-description": "an identifier for the component." + } + }, + "preferredOrder": [ + "name", + "customTemplate" + ], + "additionalProperties": false + } + ], + "description": "*beta* a component of CustomTemplateTagger.", + "x-intellij-html-description": "beta a component of CustomTemplateTagger." + }, + "TestCase": { + "required": [ + "image" + ], + "properties": { + "image": { + "type": "string", + "description": "artifact on which to run those tests.", + "x-intellij-html-description": "artifact on which to run those tests.", + "examples": [ + "gcr.io/k8s-skaffold/example" + ] + }, + "structureTests": { + "items": { + "type": "string" + }, + "type": "array", + "description": "the [Container Structure Tests](https://github.com/GoogleContainerTools/container-structure-test) to run on that artifact.", + "x-intellij-html-description": "the Container Structure Tests to run on that artifact.", + "default": "[]", + "examples": [ + "[\"./test/*\"]" + ] + } + }, + "preferredOrder": [ + "image", + "structureTests" + ], + "additionalProperties": false, + "description": "a list of structure tests to run on images that Skaffold builds.", + "x-intellij-html-description": "a list of structure tests to run on images that Skaffold builds." + } + } +} diff --git a/integration/examples/bazel/skaffold.yaml b/integration/examples/bazel/skaffold.yaml index a3a28920935..4bc1c9ce18c 100644 --- a/integration/examples/bazel/skaffold.yaml +++ b/integration/examples/bazel/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/buildpacks-java/skaffold.yaml b/integration/examples/buildpacks-java/skaffold.yaml index ed374ab601d..fec6bb42409 100644 --- a/integration/examples/buildpacks-java/skaffold.yaml +++ b/integration/examples/buildpacks-java/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/buildpacks-node/skaffold.yaml b/integration/examples/buildpacks-node/skaffold.yaml index 2b7f0951102..5100dc1abb7 100644 --- a/integration/examples/buildpacks-node/skaffold.yaml +++ b/integration/examples/buildpacks-node/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/buildpacks-python/skaffold.yaml b/integration/examples/buildpacks-python/skaffold.yaml index 5fbbafb9f54..3fedbd626cb 100644 --- a/integration/examples/buildpacks-python/skaffold.yaml +++ b/integration/examples/buildpacks-python/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/buildpacks/skaffold.yaml b/integration/examples/buildpacks/skaffold.yaml index d78c0a32950..77dd29ab38e 100644 --- a/integration/examples/buildpacks/skaffold.yaml +++ b/integration/examples/buildpacks/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/custom/skaffold.yaml b/integration/examples/custom/skaffold.yaml index 9fab8ee3963..498dfb1f189 100644 --- a/integration/examples/custom/skaffold.yaml +++ b/integration/examples/custom/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/gcb-kaniko/skaffold.yaml b/integration/examples/gcb-kaniko/skaffold.yaml index a6fd38405ce..1db45ad5549 100644 --- a/integration/examples/gcb-kaniko/skaffold.yaml +++ b/integration/examples/gcb-kaniko/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: googleCloudBuild: diff --git a/integration/examples/generate-pipeline/skaffold.yaml b/integration/examples/generate-pipeline/skaffold.yaml index 5716074437a..fdfaafcfced 100644 --- a/integration/examples/generate-pipeline/skaffold.yaml +++ b/integration/examples/generate-pipeline/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/getting-started-kustomize/skaffold.yaml b/integration/examples/getting-started-kustomize/skaffold.yaml index 14229de67ab..35a2919b26a 100644 --- a/integration/examples/getting-started-kustomize/skaffold.yaml +++ b/integration/examples/getting-started-kustomize/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config metadata: name: getting-started-kustomize diff --git a/integration/examples/getting-started/skaffold.yaml b/integration/examples/getting-started/skaffold.yaml index 36a019559aa..65a4f4fdfe6 100644 --- a/integration/examples/getting-started/skaffold.yaml +++ b/integration/examples/getting-started/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/google-cloud-build/skaffold.yaml b/integration/examples/google-cloud-build/skaffold.yaml index dc50e83541e..ff2f351855b 100644 --- a/integration/examples/google-cloud-build/skaffold.yaml +++ b/integration/examples/google-cloud-build/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: googleCloudBuild: diff --git a/integration/examples/helm-deployment-dependencies/skaffold.yaml b/integration/examples/helm-deployment-dependencies/skaffold.yaml index ffb340c5aa8..c3fc6cd06c7 100644 --- a/integration/examples/helm-deployment-dependencies/skaffold.yaml +++ b/integration/examples/helm-deployment-dependencies/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: tagPolicy: diff --git a/integration/examples/helm-deployment/skaffold.yaml b/integration/examples/helm-deployment/skaffold.yaml index ee904081d92..09baacb88b5 100644 --- a/integration/examples/helm-deployment/skaffold.yaml +++ b/integration/examples/helm-deployment/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/hot-reload/skaffold.yaml b/integration/examples/hot-reload/skaffold.yaml index e11cc149ac4..9a787fd98a3 100644 --- a/integration/examples/hot-reload/skaffold.yaml +++ b/integration/examples/hot-reload/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/jib-gradle/skaffold.yaml b/integration/examples/jib-gradle/skaffold.yaml index 11973a4ee48..75bd9a1be59 100644 --- a/integration/examples/jib-gradle/skaffold.yaml +++ b/integration/examples/jib-gradle/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/jib-multimodule/skaffold.yaml b/integration/examples/jib-multimodule/skaffold.yaml index 6f4e4284630..a671cc903d1 100644 --- a/integration/examples/jib-multimodule/skaffold.yaml +++ b/integration/examples/jib-multimodule/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/jib-sync/skaffold-gradle.yaml b/integration/examples/jib-sync/skaffold-gradle.yaml index 7250cccc165..ada2bcc348f 100644 --- a/integration/examples/jib-sync/skaffold-gradle.yaml +++ b/integration/examples/jib-sync/skaffold-gradle.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/jib-sync/skaffold-maven.yaml b/integration/examples/jib-sync/skaffold-maven.yaml index a1aff752bc5..422e5da3a8c 100644 --- a/integration/examples/jib-sync/skaffold-maven.yaml +++ b/integration/examples/jib-sync/skaffold-maven.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/jib/skaffold.yaml b/integration/examples/jib/skaffold.yaml index 87419a7533f..1a1707d2708 100644 --- a/integration/examples/jib/skaffold.yaml +++ b/integration/examples/jib/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/kaniko/skaffold.yaml b/integration/examples/kaniko/skaffold.yaml index f7d37aa9627..4c1badb0b35 100644 --- a/integration/examples/kaniko/skaffold.yaml +++ b/integration/examples/kaniko/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/kustomize/skaffold-kustomize-args.yaml b/integration/examples/kustomize/skaffold-kustomize-args.yaml index 3c475b101d7..bd6e9a21ae4 100644 --- a/integration/examples/kustomize/skaffold-kustomize-args.yaml +++ b/integration/examples/kustomize/skaffold-kustomize-args.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config deploy: kustomize: diff --git a/integration/examples/kustomize/skaffold.yaml b/integration/examples/kustomize/skaffold.yaml index 276e66ab73d..839cf213ad5 100644 --- a/integration/examples/kustomize/skaffold.yaml +++ b/integration/examples/kustomize/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config deploy: kustomize: {} diff --git a/integration/examples/microservices/skaffold.yaml b/integration/examples/microservices/skaffold.yaml index 0610fa1e5c6..7766da3b7b6 100644 --- a/integration/examples/microservices/skaffold.yaml +++ b/integration/examples/microservices/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/nodejs/skaffold.yaml b/integration/examples/nodejs/skaffold.yaml index 880aad983cc..aa18d1e6ca2 100644 --- a/integration/examples/nodejs/skaffold.yaml +++ b/integration/examples/nodejs/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: diff --git a/integration/examples/profile-patches/skaffold.yaml b/integration/examples/profile-patches/skaffold.yaml index 2193fb2909f..21f66598837 100644 --- a/integration/examples/profile-patches/skaffold.yaml +++ b/integration/examples/profile-patches/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: # only build and deploy "base-service" on main profile diff --git a/integration/examples/profiles/skaffold.yaml b/integration/examples/profiles/skaffold.yaml index 2d120284cc6..f0ef59403b0 100644 --- a/integration/examples/profiles/skaffold.yaml +++ b/integration/examples/profiles/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: # only build and deploy "world-service" on main profile diff --git a/integration/examples/react-reload/skaffold.yaml b/integration/examples/react-reload/skaffold.yaml index 00074bdf8be..1a108870e56 100644 --- a/integration/examples/react-reload/skaffold.yaml +++ b/integration/examples/react-reload/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/ruby/skaffold.yaml b/integration/examples/ruby/skaffold.yaml index cc716d78fb0..58d361f4c2b 100644 --- a/integration/examples/ruby/skaffold.yaml +++ b/integration/examples/ruby/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/structure-tests/skaffold.yaml b/integration/examples/structure-tests/skaffold.yaml index 5fafb7d386c..238d314d241 100644 --- a/integration/examples/structure-tests/skaffold.yaml +++ b/integration/examples/structure-tests/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 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 edfa4fdee9d..694a193fcdb 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/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/examples/templated-fields/skaffold.yaml b/integration/examples/templated-fields/skaffold.yaml index 91c54e99358..aa2b6ce0778 100644 --- a/integration/examples/templated-fields/skaffold.yaml +++ b/integration/examples/templated-fields/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config metadata: name: my-app diff --git a/integration/testdata/build/skaffold.yaml b/integration/testdata/build/skaffold.yaml index 3b3cbca648c..292a367546a 100644 --- a/integration/testdata/build/skaffold.yaml +++ b/integration/testdata/build/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: local: diff --git a/integration/testdata/debug/skaffold.yaml b/integration/testdata/debug/skaffold.yaml index 9405bec1111..f4637d6f9a2 100644 --- a/integration/testdata/debug/skaffold.yaml +++ b/integration/testdata/debug/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/testdata/deploy-multiple/skaffold.yaml b/integration/testdata/deploy-multiple/skaffold.yaml index 2759fd99ea6..5ad3944ac59 100644 --- a/integration/testdata/deploy-multiple/skaffold.yaml +++ b/integration/testdata/deploy-multiple/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/testdata/dev/skaffold.yaml b/integration/testdata/dev/skaffold.yaml index 9342dd5df74..03e90806daf 100644 --- a/integration/testdata/dev/skaffold.yaml +++ b/integration/testdata/dev/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/testdata/gke_loadbalancer/skaffold.yaml b/integration/testdata/gke_loadbalancer/skaffold.yaml index 091deffac45..3b0a2f342d8 100644 --- a/integration/testdata/gke_loadbalancer/skaffold.yaml +++ b/integration/testdata/gke_loadbalancer/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/testdata/hello/skaffold.yaml b/integration/testdata/hello/skaffold.yaml index 5cac15ddb83..6df607a33e3 100644 --- a/integration/testdata/hello/skaffold.yaml +++ b/integration/testdata/hello/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/testdata/init/compose/skaffold.yaml b/integration/testdata/init/compose/skaffold.yaml index 1f8e065b5d1..7fa20a070f6 100644 --- a/integration/testdata/init/compose/skaffold.yaml +++ b/integration/testdata/init/compose/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config metadata: name: compose diff --git a/integration/testdata/jib/skaffold.yaml b/integration/testdata/jib/skaffold.yaml index 631c39b3960..ab9187c71db 100644 --- a/integration/testdata/jib/skaffold.yaml +++ b/integration/testdata/jib/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/testdata/kaniko-explicit-repo/skaffold.yaml b/integration/testdata/kaniko-explicit-repo/skaffold.yaml index b351d42f35c..46441874460 100644 --- a/integration/testdata/kaniko-explicit-repo/skaffold.yaml +++ b/integration/testdata/kaniko-explicit-repo/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/testdata/kaniko-insecure-registry/app/skaffold.yaml b/integration/testdata/kaniko-insecure-registry/app/skaffold.yaml index 36a019559aa..65a4f4fdfe6 100644 --- a/integration/testdata/kaniko-insecure-registry/app/skaffold.yaml +++ b/integration/testdata/kaniko-insecure-registry/app/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/testdata/kaniko-insecure-registry/skaffold.yaml b/integration/testdata/kaniko-insecure-registry/skaffold.yaml index a486fc68e77..107b9581131 100644 --- a/integration/testdata/kaniko-insecure-registry/skaffold.yaml +++ b/integration/testdata/kaniko-insecure-registry/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config profiles: - name: build-artifact diff --git a/integration/testdata/kaniko-microservices/skaffold.yaml b/integration/testdata/kaniko-microservices/skaffold.yaml index 6fd133352d4..efcb025994d 100644 --- a/integration/testdata/kaniko-microservices/skaffold.yaml +++ b/integration/testdata/kaniko-microservices/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/testdata/kaniko-sub-folder/skaffold.yaml b/integration/testdata/kaniko-sub-folder/skaffold.yaml index 25c760c23a8..e7789728442 100644 --- a/integration/testdata/kaniko-sub-folder/skaffold.yaml +++ b/integration/testdata/kaniko-sub-folder/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/testdata/kaniko-target/skaffold.yaml b/integration/testdata/kaniko-target/skaffold.yaml index fef7887e06f..32e9305dc1e 100644 --- a/integration/testdata/kaniko-target/skaffold.yaml +++ b/integration/testdata/kaniko-target/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/integration/testdata/tagPolicy/skaffold.yaml b/integration/testdata/tagPolicy/skaffold.yaml index b89bddf7f5a..eee3732fda0 100644 --- a/integration/testdata/tagPolicy/skaffold.yaml +++ b/integration/testdata/tagPolicy/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/pkg/skaffold/initializer/testdata/init/allcli/skaffold.yaml b/pkg/skaffold/initializer/testdata/init/allcli/skaffold.yaml index dbfc59f4d03..0574309c5e5 100644 --- a/pkg/skaffold/initializer/testdata/init/allcli/skaffold.yaml +++ b/pkg/skaffold/initializer/testdata/init/allcli/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config metadata: name: allcli diff --git a/pkg/skaffold/initializer/testdata/init/getting-started-kustomize/skaffold.yaml b/pkg/skaffold/initializer/testdata/init/getting-started-kustomize/skaffold.yaml index bf2c567f112..f5b6a689014 100644 --- a/pkg/skaffold/initializer/testdata/init/getting-started-kustomize/skaffold.yaml +++ b/pkg/skaffold/initializer/testdata/init/getting-started-kustomize/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config metadata: name: getting-started-kustomize diff --git a/pkg/skaffold/initializer/testdata/init/hello-no-manifest/skaffold.yaml b/pkg/skaffold/initializer/testdata/init/hello-no-manifest/skaffold.yaml index b2965066df9..86ec6b96667 100644 --- a/pkg/skaffold/initializer/testdata/init/hello-no-manifest/skaffold.yaml +++ b/pkg/skaffold/initializer/testdata/init/hello-no-manifest/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config metadata: name: hello diff --git a/pkg/skaffold/initializer/testdata/init/hello/skaffold.yaml b/pkg/skaffold/initializer/testdata/init/hello/skaffold.yaml index b2965066df9..86ec6b96667 100644 --- a/pkg/skaffold/initializer/testdata/init/hello/skaffold.yaml +++ b/pkg/skaffold/initializer/testdata/init/hello/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config metadata: name: hello diff --git a/pkg/skaffold/initializer/testdata/init/ignore-tags/skaffold.yaml b/pkg/skaffold/initializer/testdata/init/ignore-tags/skaffold.yaml index 728479c5f30..cffa038bd49 100644 --- a/pkg/skaffold/initializer/testdata/init/ignore-tags/skaffold.yaml +++ b/pkg/skaffold/initializer/testdata/init/ignore-tags/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config metadata: name: ignore-tags diff --git a/pkg/skaffold/initializer/testdata/init/microservices/skaffold.yaml b/pkg/skaffold/initializer/testdata/init/microservices/skaffold.yaml index 9169e71ef68..e393272f77e 100644 --- a/pkg/skaffold/initializer/testdata/init/microservices/skaffold.yaml +++ b/pkg/skaffold/initializer/testdata/init/microservices/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config metadata: name: microservices diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index d5cc062d6a1..b78f113550f 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -22,8 +22,8 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util" ) -// !!! WARNING !!! This config version is already released, please DO NOT MODIFY the structs in this file. -const Version string = "skaffold/v2beta6" +// This config version is not yet released, it is SAFE TO MODIFY the structs in this file. +const Version string = "skaffold/v2beta7" // NewSkaffoldConfig creates a SkaffoldConfig func NewSkaffoldConfig() util.VersionedConfig { diff --git a/pkg/skaffold/schema/v2beta5/upgrade.go b/pkg/skaffold/schema/v2beta5/upgrade.go index b11e23c2991..724998287e1 100755 --- a/pkg/skaffold/schema/v2beta5/upgrade.go +++ b/pkg/skaffold/schema/v2beta5/upgrade.go @@ -17,8 +17,8 @@ limitations under the License. package v2beta5 import ( - next "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util" + next "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v2beta6" pkgutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" ) diff --git a/pkg/skaffold/schema/v2beta5/upgrade_test.go b/pkg/skaffold/schema/v2beta5/upgrade_test.go index d8a2aed71ce..6db5d0e29db 100755 --- a/pkg/skaffold/schema/v2beta5/upgrade_test.go +++ b/pkg/skaffold/schema/v2beta5/upgrade_test.go @@ -21,7 +21,7 @@ import ( yaml "gopkg.in/yaml.v2" - next "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + next "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v2beta6" "github.com/GoogleContainerTools/skaffold/testutil" ) diff --git a/pkg/skaffold/schema/v2beta6/config.go b/pkg/skaffold/schema/v2beta6/config.go new file mode 100755 index 00000000000..ab28da23517 --- /dev/null +++ b/pkg/skaffold/schema/v2beta6/config.go @@ -0,0 +1,960 @@ +/* +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 v2beta6 + +import ( + v1 "k8s.io/api/core/v1" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util" +) + +// !!! WARNING !!! This config version is already released, please DO NOT MODIFY the structs in this file. +const Version string = "skaffold/v2beta6" + +// 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"` + + // Metadata holds additional information about the config. + Metadata Metadata `yaml:"metadata,omitempty"` + + // 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"` +} + +// Metadata holds an optional name of the project. +type Metadata struct { + // Name is an identifier for the project. + Name string `yaml:"name,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"` + + // PortForward describes user defined resources to port-forward. + PortForward []*PortForwardResource `yaml:"portForward,omitempty"` +} + +func (c *SkaffoldConfig) GetVersion() string { + return c.APIVersion +} + +// ResourceType describes the Kubernetes resource types used for port forwarding. +type ResourceType string + +// PortForwardResource describes a resource to port forward. +type PortForwardResource struct { + // Type is the Kubernetes type that should be port forwarded. + // Acceptable resource types include: `Service`, `Pod` and Controller resource type that has a pod spec: `ReplicaSet`, `ReplicationController`, `Deployment`, `StatefulSet`, `DaemonSet`, `Job`, `CronJob`. + Type ResourceType `yaml:"resourceType,omitempty"` + + // Name is the name of the Kubernetes resource to port forward. + Name string `yaml:"resourceName,omitempty"` + + // Namespace is the namespace of the resource to port forward. + Namespace string `yaml:"namespace,omitempty"` + + // Port is the resource port that will be forwarded. + Port int `yaml:"port,omitempty"` + + // Address is the local address to bind to. Defaults to the loopback address 127.0.0.1. + Address string `yaml:"address,omitempty"` + + // LocalPort is the local port to forward to. If the port is unavailable, Skaffold will choose a random open port to forward to. *Optional*. + LocalPort int `yaml:"localPort,omitempty"` +} + +// 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"` + + // CustomTemplateTagger *beta* tags images with a configurable template string *composed of other taggers*. + CustomTemplateTagger *CustomTemplateTagger `yaml:"customTemplate,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. + // `TreeSha`: use the full tree hash of the artifact workingdir. + // `AbbrevTreeSha`: use the abbreviated tree hash of the artifact workingdir. + Variant string `yaml:"variant,omitempty"` + + // Prefix adds a fixed prefix to the tag. + Prefix string `yaml:"prefix,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. + // For example: `{{.RELEASE}}`. + 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"` +} + +// CustomTemplateTagger *beta* tags images with a configurable template string. +type CustomTemplateTagger 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 provided components with those variables injected. + // For example: `{{.DATE}}` where DATE references a TaggerComponent. + Template string `yaml:"template,omitempty" yamltags:"required"` + + // Components lists TaggerComponents that the template (see field above) can be executed against. + Components []TaggerComponent `yaml:"components,omitempty"` +} + +// TaggerComponent *beta* is a component of CustomTemplateTagger. +type TaggerComponent struct { + // Name is an identifier for the component. + Name string `yaml:"name,omitempty"` + + // Component is a tagging strategy to be used in CustomTemplateTagger. + Component TagPolicy `yaml:",inline" yamltags:"skipTrim"` +} + +// 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"` + + // Concurrency is how many artifacts can be built concurrently. 0 means "no-limit". + // Defaults to `1`. + Concurrency *int `yaml:"concurrency,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"` + + // Logging specifies the logging mode. + // Valid modes are: + // `LOGGING_UNSPECIFIED`: The service determines the logging mode. + // `LEGACY`: Stackdriver logging and Cloud Storage logging are enabled (default). + // `GCS_ONLY`: Only Cloud Storage logging is enabled. + // See [Cloud Build Reference](https://cloud.google.com/cloud-build/docs/api/reference/rest/v1/projects.builds#loggingmode). + Logging string `yaml:"logging,omitempty"` + + // LogStreamingOption specifies the behavior when writing build logs to Google Cloud Storage. + // Valid options are: + // `STREAM_DEFAULT`: Service may automatically determine build log streaming behavior. + // `STREAM_ON`: Build logs should be streamed to Google Cloud Storage. + // `STREAM_OFF`: Build logs should not be streamed to Google Cloud Storage; they will be written when the build is completed. + // See [Cloud Build Reference](https://cloud.google.com/cloud-build/docs/api/reference/rest/v1/projects.builds#logstreamingoption). + LogStreamingOption string `yaml:"logStreamingOption,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"` + + // KanikoImage is the image that runs a Kaniko build. + // See [Cloud Builders](https://cloud.google.com/cloud-build/docs/cloud-builders). + // Defaults to `gcr.io/kaniko-project/executor`. + KanikoImage string `yaml:"kanikoImage,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"` + + // PackImage is the image that runs a Cloud Native Buildpacks build. + // See [Cloud Builders](https://cloud.google.com/cloud-build/docs/cloud-builders). + // Defaults to `gcr.io/k8s-skaffold/pack`. + PackImage string `yaml:"packImage,omitempty"` + + // Concurrency is how many artifacts can be built concurrently. 0 means "no-limit". + // Defaults to `0`. + Concurrency int `yaml:"concurrency,omitempty"` + + // WorkerPool configures a pool of workers to run the build. + WorkerPool string `yaml:"workerPool,omitempty"` +} + +// 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"` + // HostPath specifies a path on the host that is mounted to each pod as read only cache volume containing base images. + // If set, must exist on each node and prepopulated with kaniko-warmer. + HostPath string `yaml:"hostPath,omitempty"` +} + +// ClusterDetails *beta* describes how to do an on-cluster build. +type ClusterDetails struct { + // HTTPProxy for kaniko pod. + HTTPProxy string `yaml:"HTTP_PROXY,omitempty"` + + // HTTPSProxy for kaniko pod. + HTTPSProxy string `yaml:"HTTPS_PROXY,omitempty"` + + // PullSecretPath is the path to the Google Cloud service account secret key file. + PullSecretPath string `yaml:"pullSecretPath,omitempty"` + + // PullSecretName is the name of the Kubernetes secret for pulling base images + // and pushing the final image. If given, the secret needs to contain the Google Cloud + // service account secret key under the key `kaniko-secret`. + // Defaults to `kaniko-secret`. + PullSecretName string `yaml:"pullSecretName,omitempty"` + + // PullSecretMountPath is the path the pull secret will be mounted at within the running container. + PullSecretMountPath string `yaml:"pullSecretMountPath,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"` + + // ServiceAccountName describes the Kubernetes service account to use for the pod. + // Defaults to 'default'. + ServiceAccountName string `yaml:"serviceAccount,omitempty"` + + // Tolerations describes the Kubernetes tolerations for the pod. + Tolerations []v1.Toleration `yaml:"tolerations,omitempty"` + + // Annotations describes the Kubernetes annotations for the pod. + Annotations map[string]string `yaml:"annotations,omitempty"` + + // RunAsUser defines the UID to request for running the container. + // If omitted, no SeurityContext will be specified for the pod and will therefore be inherited + // from the service account. + RunAsUser *int64 `yaml:"runAsUser,omitempty"` + + // Resources define the resource requirements for the kaniko pod. + Resources *ResourceRequirements `yaml:"resources,omitempty"` + + // Concurrency is how many artifacts can be built concurrently. 0 means "no-limit". + // Defaults to `0`. + Concurrency int `yaml:"concurrency,omitempty"` + + // Volumes defines container mounts for ConfigMap and Secret resources. + Volumes []v1.Volume `yaml:"volumes,omitempty"` + + // RandomPullSecret adds a random UUID postfix to the default name of the pull secret to facilitate parallel builds, e.g. kaniko-secretdocker-cfgfd154022-c761-416f-8eb3-cf8258450b85. + RandomPullSecret bool `yaml:"randomPullSecret,omitempty"` + + // RandomDockerConfigSecret adds a random UUID postfix to the default name of the docker secret to facilitate parallel builds, e.g. docker-cfgfd154022-c761-416f-8eb3-cf8258450b85. + RandomDockerConfigSecret bool `yaml:"randomDockerConfigSecret,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 contains the `config.json` Docker configuration. + // Note that the expected secret type is not 'kubernetes.io/dockerconfigjson' but 'Opaque'. + 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"` + + // EphemeralStorage the amount of Ephemeral storage to allocate to the pod. + // For example: `1Gi` or `1000Mi`. + EphemeralStorage string `yaml:"ephemeralStorage,omitempty"` + + // ResourceStorage the amount of resource storage to allocate to the pod. + // For example: `1Gi` or `1000Mi`. + ResourceStorage string `yaml:"resourceStorage,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"` + + // StatusCheckDeadlineSeconds *beta* is the deadline for deployments to stabilize in seconds. + StatusCheckDeadlineSeconds int `yaml:"statusCheckDeadlineSeconds,omitempty"` + + // KubeContext is the Kubernetes context that Skaffold should deploy to. + // For example: `minikube`. + KubeContext string `yaml:"kubeContext,omitempty"` + + // Logs configures how container logs are printed as a result of a deployment. + Logs LogsConfig `yaml:"logs,omitempty"` +} + +// DeployType contains the specific implementation and parameters needed +// for the deploy step. All three deployer types can be used at the same +// time for hybrid workflows. +type DeployType struct { + // HelmDeploy *beta* uses the `helm` CLI to apply the charts to the cluster. + HelmDeploy *HelmDeploy `yaml:"helm,omitempty"` + + // 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"` + + // KustomizeDeploy *beta* uses the `kustomize` CLI to "patch" a deployment for a target environment. + KustomizeDeploy *KustomizeDeploy `yaml:"kustomize,omitempty"` +} + +// 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"` + + // DisableValidation passes the `--validate=false` flag to supported + // `kubectl` commands when enabled. + DisableValidation bool `yaml:"disableValidation,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 { + // KustomizePaths is the path to Kustomization files. + // Defaults to `["."]`. + KustomizePaths []string `yaml:"paths,omitempty"` + + // Flags are additional flags passed to `kubectl`. + Flags KubectlFlags `yaml:"flags,omitempty"` + + // BuildArgs are additional args passed to `kustomize build`. + BuildArgs []string `yaml:"buildArgs,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"` + + // ArtifactOverrides are key value pairs. + // If present, Skaffold will send `--set-string` flag to Helm CLI and append all pairs after the flag. + ArtifactOverrides util.FlatMap `yaml:"artifactOverrides,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 util.FlatMap `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 util.FlatMap `yaml:"setValueTemplates,omitempty"` + + // SetFiles are key-value pairs. + // If present, Skaffold will send `--set-file` flag to Helm CLI and append all pairs after the flag. + SetFiles map[string]string `yaml:"setFiles,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 + // when upgrading a new version of a chart in subsequent dev loop deploy. + // Defaults to `false`. + RecreatePods bool `yaml:"recreatePods,omitempty"` + + // SkipBuildDependencies should build dependencies be skipped. + // Ignored when `remote: true`. + SkipBuildDependencies bool `yaml:"skipBuildDependencies,omitempty"` + + // UseHelmSecrets instructs skaffold to use secrets plugin on deployment. + UseHelmSecrets bool `yaml:"useHelmSecrets,omitempty"` + + // Remote specifies whether the chart path is remote, or exists on the host filesystem. + Remote bool `yaml:"remote,omitempty"` + + // UpgradeOnChange specifies whether to upgrade helm chart on code changes. + // Default is `true` when helm chart is local (`remote: false`). + // Default is `false` if `remote: true`. + UpgradeOnChange *bool `yaml:"upgradeOnChange,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 { + // ExplicitRegistry separates `image.registry` to the image config syntax. Useful for some charts e.g. `postgresql`. + ExplicitRegistry bool `yaml:"explicitRegistry,omitempty"` +} + +// LogsConfig configures how container logs are printed as a result of a deployment. +type LogsConfig struct { + // Prefix defines the prefix shown on each log line. Valid values are + // `container`: prefix logs lines with the name of the container. + // `podAndContainer`: prefix logs lines with the names of the pod and of the container. + // `auto`: same as `podAndContainer` except that the pod name is skipped if it's the same as the container name. + // `none`: don't add a prefix. + // Defaults to `auto`. + Prefix string `yaml:"prefix,omitempty"` +} + +// 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 *beta* lists local files synced to pods instead + // of triggering an image build when modified. + // If no files are listed, sync all the files and infer the destination. + // Defaults to `infer: ["**/*"]`. + Sync *Sync `yaml:"sync,omitempty"` + + // ArtifactType describes how to build an artifact. + ArtifactType `yaml:",inline"` +} + +// Sync *beta* specifies what files to sync into the container. +// This is a list of sync rules indicating the intent to sync for source files. +// If no files are listed, sync all the files and infer the destination. +// Defaults to `infer: ["**/*"]`. +type Sync struct { + // Manual lists manual sync rules indicating the source and destination. + Manual []*SyncRule `yaml:"manual,omitempty" yamltags:"oneOf=sync"` + + // Infer lists file patterns which may be synced into the container + // The container destination is inferred by the builder + // based on the instructions of a Dockerfile. + // Available for docker and kaniko artifacts and custom + // artifacts that declare dependencies on a dockerfile. + Infer []string `yaml:"infer,omitempty" yamltags:"oneOf=sync"` + + // Auto delegates discovery of sync rules to the build system. + // Only available for jib and buildpacks. + Auto *Auto `yaml:"auto,omitempty" yamltags:"oneOf=sync"` +} + +// SyncRule specifies which local files to sync to remote folders. +type SyncRule struct { + // Src is a glob pattern to match local paths against. + // Directories should be delimited by `/` on all platforms. + // For example: `"css/**/*.css"`. + Src string `yaml:"src,omitempty" yamltags:"required"` + + // Dest is the destination path in the container where the files should be synced to. + // For example: `"app/"` + Dest string `yaml:"dest,omitempty" yamltags:"required"` + + // Strip specifies the path prefix to remove from the source path when + // transplanting the files into the destination folder. + // For example: `"css/"` + Strip string `yaml:"strip,omitempty"` +} + +// Auto cannot be customized. +type Auto struct{} + +// Profile is 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"` + + // 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"` + + // Patches lists patches applied to the configuration. + // Patches use the JSON patch notation. + Patches []JSONPatch `yaml:"patches,omitempty"` + + // Pipeline contains the definitions to replace the default skaffold pipeline. + Pipeline `yaml:",inline"` +} + +// 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=pattern` pair. The profile is auto-activated if an Environment + // Variable `key` matches the pattern. If the pattern starts with `!`, activation + // happens if the remaining pattern is _not_ matched. The pattern matches if the + // Environment Variable value is exactly `pattern`, or the regex `pattern` is + // found in it. An empty `pattern` (e.g. `env: "key="`) always only matches if + // the Environment Variable is undefined or empty. + // 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"` + + // JibArtifact builds images using the + // [Jib plugins for Maven or Gradle](https://github.com/GoogleContainerTools/jib/). + JibArtifact *JibArtifact `yaml:"jib,omitempty" yamltags:"oneOf=artifact"` + + // KanikoArtifact builds images using [kaniko](https://github.com/GoogleContainerTools/kaniko). + KanikoArtifact *KanikoArtifact `yaml:"kaniko,omitempty" yamltags:"oneOf=artifact"` + + // BuildpackArtifact builds images using [Cloud Native Buildpacks](https://buildpacks.io/). + BuildpackArtifact *BuildpackArtifact `yaml:"buildpacks,omitempty" yamltags:"oneOf=artifact"` + + // CustomArtifact *beta* builds images using a custom build script written by the user. + CustomArtifact *CustomArtifact `yaml:"custom,omitempty" yamltags:"oneOf=artifact"` +} + +// BuildpackArtifact *alpha* describes an artifact built using [Cloud Native Buildpacks](https://buildpacks.io/). +// It can be used to build images out of project's sources without any additional configuration. +type BuildpackArtifact struct { + // Builder is the builder image used. + Builder string `yaml:"builder" yamltags:"required"` + + // RunImage overrides the stack's default run image. + RunImage string `yaml:"runImage,omitempty"` + + // Env are environment variables, in the `key=value` form, passed to the build. + // Values can use the go template syntax. + // For example: `["key1=value1", "key2=value2", "key3={{.ENV_VARIABLE}}"]`. + Env []string `yaml:"env,omitempty"` + + // Buildpacks is a list of strings, where each string is a specific buildpack to use with the builder. + // If you specify buildpacks the builder image automatic detection will be ignored. These buildpacks will be used to build the Image from your source code. + // Order matters. + Buildpacks []string `yaml:"buildpacks,omitempty"` + + // TrustBuilder indicates that the builder should be trusted. + TrustBuilder bool `yaml:"trustBuilder,omitempty"` + + // ProjectDescriptor is the path to the project descriptor file. + // Defaults to `project.toml` if it exists. + ProjectDescriptor string `yaml:"projectDescriptor,omitempty"` + + // Dependencies are the file dependencies that skaffold should watch for both rebuilding and file syncing for this artifact. + Dependencies *BuildpackDependencies `yaml:"dependencies,omitempty"` +} + +// BuildpackDependencies *alpha* is used to specify dependencies for an artifact built by buildpacks. +type BuildpackDependencies struct { + // 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 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"` +} + +// CustomArtifact *beta* 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 is the command executed to build the image. + BuildCommand string `yaml:"buildCommand,omitempty"` + // Dependencies are the file dependencies that skaffold should watch for both rebuilding and file syncing for this artifact. + Dependencies *CustomDependencies `yaml:"dependencies,omitempty"` +} + +// CustomDependencies *beta* is used to specify dependencies for an artifact built by a custom build script. +// Either `dockerfile` or `paths` should be specified for file watching to work as expected. +type CustomDependencies struct { + // Dockerfile should be set if the artifact is built from a Dockerfile, from which skaffold can determine dependencies. + Dockerfile *DockerfileDependency `yaml:"dockerfile,omitempty" yamltags:"oneOf=dependency"` + + // Command represents a custom command that skaffold executes to obtain dependencies. The output of this command *must* be a valid JSON array. + Command string `yaml:"command,omitempty" yamltags:"oneOf=dependency"` + + // 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 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"` +} + +// DockerfileDependency *beta* is used to specify a custom build artifact that is built from a Dockerfile. This allows skaffold to determine dependencies from the Dockerfile. +type DockerfileDependency struct { + // Path locates the Dockerfile relative to workspace. + Path string `yaml:"path,omitempty"` + + // BuildArgs are key/value pairs used to resolve values of `ARG` instructions in a Dockerfile. + // Values can be constants or environment variables via the go template syntax. + // For example: `{"key1": "value1", "key2": "value2", "key3": "'{{.ENV_VARIABLE}}'"}`. + BuildArgs map[string]*string `yaml:"buildArgs,omitempty"` +} + +// KanikoArtifact 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. + // It also accepts environment variables via the go template syntax. + // For example: `{"key1": "value1", "key2": "value2", "key3": "'{{.ENV_VARIABLE}}'"}`. + BuildArgs map[string]*string `yaml:"buildArgs,omitempty"` + + // Env are environment variables passed to the kaniko pod. + Env []v1.EnvVar `yaml:"env,omitempty"` + + // InitImage is the image used to run init container which mounts kaniko context. + InitImage string `yaml:"initImage,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"` + + // Reproducible is used to strip timestamps out of the built image. + Reproducible bool `yaml:"reproducible,omitempty"` + + // SkipTLS skips TLS verification when pulling and pushing the image. + SkipTLS bool `yaml:"skipTLS,omitempty"` + + // VolumeMounts are volume mounts passed to kaniko pod. + VolumeMounts []v1.VolumeMount `yaml:"volumeMounts,omitempty"` +} + +// DockerArtifact 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"` + + // NetworkMode is passed through to docker and overrides the + // network configuration of docker builder. If unset, use whatever + // is configured in the underlying docker daemon. Valid modes are + // `host`: use the host's networking stack. + // `bridge`: use the bridged network configuration. + // `none`: no networking in the container. + NetworkMode string `yaml:"network,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"` + + // NoCache used to pass in --no-cache to docker build to prevent caching. + NoCache bool `yaml:"noCache,omitempty"` +} + +// BazelArtifact 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"` +} + +// JibArtifact builds images using the +// [Jib plugins for Maven and Gradle](https://github.com/GoogleContainerTools/jib/). +type JibArtifact struct { + // Project selects which sub-project to build for multi-module builds. + Project string `yaml:"project,omitempty"` + + // Flags are additional build flags passed to the builder. + // For example: `["--no-build-cache"]`. + Flags []string `yaml:"args,omitempty"` + + // Type the Jib builder type; normally determined automatically. Valid types are + // `maven`: for Maven. + // `gradle`: for Gradle. + Type string `yaml:"type,omitempty"` +} diff --git a/pkg/skaffold/schema/v2beta6/upgrade.go b/pkg/skaffold/schema/v2beta6/upgrade.go new file mode 100755 index 00000000000..60bf5c0b39b --- /dev/null +++ b/pkg/skaffold/schema/v2beta6/upgrade.go @@ -0,0 +1,40 @@ +/* +Copyright 2020 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 v2beta6 + +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" +) + +// Upgrade upgrades a configuration to the next version. +// Config changes from v2beta6 to v2beta7 +// 1. Additions: +// New structs CustomTemplateTagger and TaggerComponent used by TagPolicy. +func (c *SkaffoldConfig) Upgrade() (util.VersionedConfig, error) { + var newConfig next.SkaffoldConfig + pkgutil.CloneThroughJSON(c, &newConfig) + newConfig.APIVersion = next.Version + + err := util.UpgradePipelines(c, &newConfig, upgradeOnePipeline) + return &newConfig, err +} + +func upgradeOnePipeline(_, _ interface{}) error { + return nil +} diff --git a/pkg/skaffold/schema/v2beta6/upgrade_test.go b/pkg/skaffold/schema/v2beta6/upgrade_test.go new file mode 100755 index 00000000000..156d9b19be2 --- /dev/null +++ b/pkg/skaffold/schema/v2beta6/upgrade_test.go @@ -0,0 +1,182 @@ +/* +Copyright 2020 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 v2beta6 + +import ( + "testing" + + yaml "gopkg.in/yaml.v2" + + next "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/GoogleContainerTools/skaffold/testutil" +) + +func TestUpgrade(t *testing.T) { + yaml := `apiVersion: skaffold/v2beta6 +kind: Config +build: + artifacts: + - image: gcr.io/k8s-skaffold/skaffold-example + docker: + dockerfile: path/to/Dockerfile + - image: gcr.io/k8s-skaffold/bazel + bazel: + target: //mytarget + - image: gcr.io/k8s-skaffold/jib-maven + jib: + args: ['-v', '--activate-profiles', 'prof'] + project: dir + - image: gcr.io/k8s-skaffold/jib-gradle + jib: + args: ['-v'] + - image: gcr.io/k8s-skaffold/buildpacks + buildpacks: + builder: gcr.io/buildpacks/builder:v1 + googleCloudBuild: + projectId: test-project +test: + - image: gcr.io/k8s-skaffold/skaffold-example + structureTests: + - ./test/* +deploy: + kubectl: + manifests: + - k8s-* + kustomize: + paths: + - kustomization-main +profiles: + - name: test profile + build: + artifacts: + - image: gcr.io/k8s-skaffold/skaffold-example + kaniko: + cache: {} + cluster: + pullSecretName: e2esecret + pullSecretPath: secret.json + namespace: default + test: + - image: gcr.io/k8s-skaffold/skaffold-example + structureTests: + - ./test/* + deploy: + kubectl: + manifests: + - k8s-* + kustomize: + paths: + - kustomization-test + - name: test local + build: + artifacts: + - image: gcr.io/k8s-skaffold/skaffold-example + docker: + dockerfile: path/to/Dockerfile + local: + push: false + deploy: + kubectl: + manifests: + - k8s-* + kustomize: {} +` + expected := `apiVersion: skaffold/v2beta7 +kind: Config +build: + artifacts: + - image: gcr.io/k8s-skaffold/skaffold-example + docker: + dockerfile: path/to/Dockerfile + - image: gcr.io/k8s-skaffold/bazel + bazel: + target: //mytarget + - image: gcr.io/k8s-skaffold/jib-maven + jib: + args: ['-v', '--activate-profiles', 'prof'] + project: dir + - image: gcr.io/k8s-skaffold/jib-gradle + jib: + args: ['-v'] + - image: gcr.io/k8s-skaffold/buildpacks + buildpacks: + builder: gcr.io/buildpacks/builder:v1 + googleCloudBuild: + projectId: test-project +test: + - image: gcr.io/k8s-skaffold/skaffold-example + structureTests: + - ./test/* +deploy: + kubectl: + manifests: + - k8s-* + kustomize: + paths: + - kustomization-main +profiles: + - name: test profile + build: + artifacts: + - image: gcr.io/k8s-skaffold/skaffold-example + kaniko: + cache: {} + cluster: + pullSecretName: e2esecret + pullSecretPath: secret.json + namespace: default + test: + - image: gcr.io/k8s-skaffold/skaffold-example + structureTests: + - ./test/* + deploy: + kubectl: + manifests: + - k8s-* + kustomize: + paths: + - kustomization-test + - name: test local + build: + artifacts: + - image: gcr.io/k8s-skaffold/skaffold-example + docker: + dockerfile: path/to/Dockerfile + local: + push: false + deploy: + kubectl: + manifests: + - k8s-* + kustomize: {} +` + 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 f2403e6ba67..cb7ef316a93 100644 --- a/pkg/skaffold/schema/versions.go +++ b/pkg/skaffold/schema/versions.go @@ -60,6 +60,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v2beta3" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v2beta4" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v2beta5" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v2beta6" misc "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/yaml" ) @@ -101,6 +102,7 @@ var SchemaVersions = Versions{ {v2beta3.Version, v2beta3.NewSkaffoldConfig}, {v2beta4.Version, v2beta4.NewSkaffoldConfig}, {v2beta5.Version, v2beta5.NewSkaffoldConfig}, + {v2beta6.Version, v2beta6.NewSkaffoldConfig}, {latest.Version, latest.NewSkaffoldConfig}, } From da1dc6d313a2bdfbd91518581ee0696c7cee25af Mon Sep 17 00:00:00 2001 From: Appu Goundan Date: Tue, 11 Aug 2020 12:43:07 -0400 Subject: [PATCH 071/138] Remove unused goose.yaml --- integration/examples/jib-sync/goose.yaml | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 integration/examples/jib-sync/goose.yaml diff --git a/integration/examples/jib-sync/goose.yaml b/integration/examples/jib-sync/goose.yaml deleted file mode 100644 index 24f4fefd77e..00000000000 --- a/integration/examples/jib-sync/goose.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: skaffold/v2beta5 -kind: Config -build: - artifacts: - - image: test-file-sync - jib: - type: must-use-profile - sync: {} - -profiles: -- name: maven - patches: - - op: add - path: /build/artifacts/0/jib/args - value: - - --no-transfer-progress - - op: replace - path: /build/artifacts/0/jib/type - value: maven -- name: gradle - patches: - - op: replace - path: /build/artifacts/0/jib/type - value: gradle From cc48b763f73ba621cac4905ca0a16a8b363a85d0 Mon Sep 17 00:00:00 2001 From: Gaurav <39389231+gsquared94@users.noreply.github.com> Date: Tue, 11 Aug 2020 22:25:13 +0530 Subject: [PATCH 072/138] Update pkg/skaffold/schema/v2beta6/upgrade.go Co-authored-by: Nick Kubala --- pkg/skaffold/schema/v2beta6/upgrade.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/skaffold/schema/v2beta6/upgrade.go b/pkg/skaffold/schema/v2beta6/upgrade.go index 60bf5c0b39b..e69167e5123 100755 --- a/pkg/skaffold/schema/v2beta6/upgrade.go +++ b/pkg/skaffold/schema/v2beta6/upgrade.go @@ -24,8 +24,6 @@ import ( // Upgrade upgrades a configuration to the next version. // Config changes from v2beta6 to v2beta7 -// 1. Additions: -// New structs CustomTemplateTagger and TaggerComponent used by TagPolicy. func (c *SkaffoldConfig) Upgrade() (util.VersionedConfig, error) { var newConfig next.SkaffoldConfig pkgutil.CloneThroughJSON(c, &newConfig) From 5a5c6030bb6eaa052f34f2eefa2ea9c227b55341 Mon Sep 17 00:00:00 2001 From: Mridula <66699525+mpeddada1@users.noreply.github.com> Date: Tue, 11 Aug 2020 16:38:12 -0400 Subject: [PATCH 073/138] Update jib to 2.5.x (#4639) --- examples/jib-gradle/build.gradle | 2 +- examples/jib-multimodule/pom.xml | 2 +- examples/jib-sync/build.gradle | 2 +- examples/jib-sync/pom.xml | 2 +- examples/jib/pom.xml | 2 +- integration/examples/jib-gradle/build.gradle | 2 +- integration/examples/jib-multimodule/pom.xml | 2 +- integration/examples/jib-sync/build.gradle | 2 +- integration/examples/jib-sync/pom.xml | 2 +- integration/examples/jib/pom.xml | 2 +- integration/testdata/debug/java/pom.xml | 2 +- integration/testdata/jib/pom.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/jib-gradle/build.gradle b/examples/jib-gradle/build.gradle index 25985e15316..9389592aa91 100644 --- a/examples/jib-gradle/build.gradle +++ b/examples/jib-gradle/build.gradle @@ -3,7 +3,7 @@ plugins { id 'groovy' id 'io.spring.dependency-management' version '1.0.6.RELEASE' id 'net.ltgt.apt-idea' version '0.18' - id 'com.google.cloud.tools.jib' version '2.4.0' + id 'com.google.cloud.tools.jib' version '2.5.0' } version '0.1' diff --git a/examples/jib-multimodule/pom.xml b/examples/jib-multimodule/pom.xml index 15a43428ca7..b0a1e8e397f 100644 --- a/examples/jib-multimodule/pom.xml +++ b/examples/jib-multimodule/pom.xml @@ -18,7 +18,7 @@ 1.8 - 2.4.0 + 2.5.2 diff --git a/examples/jib-sync/build.gradle b/examples/jib-sync/build.gradle index 56065319eb5..978e6403334 100644 --- a/examples/jib-sync/build.gradle +++ b/examples/jib-sync/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java' id 'org.springframework.boot' version '2.0.5.RELEASE' id 'io.spring.dependency-management' version '1.0.7.RELEASE' - id 'com.google.cloud.tools.jib' version '2.4.0' + id 'com.google.cloud.tools.jib' version '2.5.0' } repositories { diff --git a/examples/jib-sync/pom.xml b/examples/jib-sync/pom.xml index add93f7eb3f..59219a328f9 100644 --- a/examples/jib-sync/pom.xml +++ b/examples/jib-sync/pom.xml @@ -9,7 +9,7 @@ 1.8 - 2.4.0 + 2.5.2 diff --git a/examples/jib/pom.xml b/examples/jib/pom.xml index b86baec3919..a0cb95c2d01 100644 --- a/examples/jib/pom.xml +++ b/examples/jib/pom.xml @@ -9,7 +9,7 @@ 1.8 - 2.4.0 + 2.5.2 diff --git a/integration/examples/jib-gradle/build.gradle b/integration/examples/jib-gradle/build.gradle index 25985e15316..9389592aa91 100644 --- a/integration/examples/jib-gradle/build.gradle +++ b/integration/examples/jib-gradle/build.gradle @@ -3,7 +3,7 @@ plugins { id 'groovy' id 'io.spring.dependency-management' version '1.0.6.RELEASE' id 'net.ltgt.apt-idea' version '0.18' - id 'com.google.cloud.tools.jib' version '2.4.0' + id 'com.google.cloud.tools.jib' version '2.5.0' } version '0.1' diff --git a/integration/examples/jib-multimodule/pom.xml b/integration/examples/jib-multimodule/pom.xml index 15a43428ca7..b0a1e8e397f 100644 --- a/integration/examples/jib-multimodule/pom.xml +++ b/integration/examples/jib-multimodule/pom.xml @@ -18,7 +18,7 @@ 1.8 - 2.4.0 + 2.5.2 diff --git a/integration/examples/jib-sync/build.gradle b/integration/examples/jib-sync/build.gradle index 56065319eb5..978e6403334 100644 --- a/integration/examples/jib-sync/build.gradle +++ b/integration/examples/jib-sync/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java' id 'org.springframework.boot' version '2.0.5.RELEASE' id 'io.spring.dependency-management' version '1.0.7.RELEASE' - id 'com.google.cloud.tools.jib' version '2.4.0' + id 'com.google.cloud.tools.jib' version '2.5.0' } repositories { diff --git a/integration/examples/jib-sync/pom.xml b/integration/examples/jib-sync/pom.xml index add93f7eb3f..59219a328f9 100644 --- a/integration/examples/jib-sync/pom.xml +++ b/integration/examples/jib-sync/pom.xml @@ -9,7 +9,7 @@ 1.8 - 2.4.0 + 2.5.2 diff --git a/integration/examples/jib/pom.xml b/integration/examples/jib/pom.xml index b86baec3919..a0cb95c2d01 100644 --- a/integration/examples/jib/pom.xml +++ b/integration/examples/jib/pom.xml @@ -9,7 +9,7 @@ 1.8 - 2.4.0 + 2.5.2 diff --git a/integration/testdata/debug/java/pom.xml b/integration/testdata/debug/java/pom.xml index 878f93a334e..e3aa348f2c7 100644 --- a/integration/testdata/debug/java/pom.xml +++ b/integration/testdata/debug/java/pom.xml @@ -8,7 +8,7 @@ Simple Java server with Skaffold and Jib - 2.4.0 + 2.5.2 1.8 1.8 diff --git a/integration/testdata/jib/pom.xml b/integration/testdata/jib/pom.xml index 8c372b5daca..5ed13a49ac5 100644 --- a/integration/testdata/jib/pom.xml +++ b/integration/testdata/jib/pom.xml @@ -8,7 +8,7 @@ Simple Java server with Skaffold and Jib - 2.4.0 + 2.5.2 1.8 1.8 From a33102f83098bff7d64dcb91245a954e73f09a6f Mon Sep 17 00:00:00 2001 From: Brian de Alwis Date: Wed, 12 Aug 2020 22:54:47 -0400 Subject: [PATCH 074/138] Send update-check message to stderr (#4655) --- cmd/skaffold/app/cmd/cmd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/skaffold/app/cmd/cmd.go b/cmd/skaffold/app/cmd/cmd.go index 61c76b47dcd..6a95a96f9b1 100644 --- a/cmd/skaffold/app/cmd/cmd.go +++ b/cmd/skaffold/app/cmd/cmd.go @@ -113,7 +113,7 @@ func NewSkaffoldCommand(out, err io.Writer) *cobra.Command { PersistentPostRun: func(cmd *cobra.Command, args []string) { select { case msg := <-updateMsg: - fmt.Fprintf(cmd.OutOrStdout(), "%s\n", msg) + fmt.Fprintf(cmd.OutOrStderr(), "%s\n", msg) default: } // check if survey prompt needs to be displayed From 6994b45a9a02daf03deb739413071adc5bdbcb47 Mon Sep 17 00:00:00 2001 From: Brian de Alwis Date: Thu, 13 Aug 2020 00:50:02 -0400 Subject: [PATCH 075/138] Enable auto sync mode for Buildpacks artifacts (#4656) --- docs/content/en/docs/pipeline-stages/filesync.md | 8 ++++++-- integration/examples/buildpacks-java/skaffold.yaml | 2 -- integration/examples/buildpacks-node/skaffold.yaml | 2 -- integration/examples/buildpacks/skaffold.yaml | 2 -- pkg/skaffold/schema/defaults/defaults.go | 2 ++ pkg/skaffold/schema/defaults/defaults_test.go | 12 ++++++++++++ 6 files changed, 20 insertions(+), 8 deletions(-) diff --git a/docs/content/en/docs/pipeline-stages/filesync.md b/docs/content/en/docs/pipeline-stages/filesync.md index e16f7f4acfd..4f2fe7618fd 100644 --- a/docs/content/en/docs/pipeline-stages/filesync.md +++ b/docs/content/en/docs/pipeline-stages/filesync.md @@ -20,7 +20,8 @@ Multiple types of sync are supported by Skaffold: This is supported by docker and kaniko artifacts and also for custom artifacts that declare a dependency on a Dockerfile. - + `auto`: Skaffold automatically configures the sync. This is only supported by Jib and Buildpacks artifacts. ++ `auto`: Skaffold automatically configures the sync. This mode is only supported by Jib and Buildpacks artifacts. + Auto sync mode is enabled by default for Buildpacks artifacts. ### Manual sync mode @@ -77,11 +78,14 @@ File deletion will always cause a complete rebuild. ### Auto sync mode +In auto sync mode, Skaffold automatically generates sync rules for known file types. Changes to other file types will +result in a complete rebuild. + #### Buildpacks Skaffold requires special collaboration from the Buildpacks for the `auto` sync to work. The [gcr.io/buildpacks/builder:v1](https://github.com/GoogleCloudPlatform/buildpacks) supports Skaffold -out of the box, currently for Go and NodeJS. +out of the box, currently for Go, NodeJS, and Java. Cloud Native Buildpacks set a `io.buildpacks.build.metadata` label on the images they create. This labels points to json description of the [Bill-of-Materials, aka BOM](https://github.com/buildpacks/spec/blob/master/buildpack.md#bill-of-materials-toml) of the build. diff --git a/integration/examples/buildpacks-java/skaffold.yaml b/integration/examples/buildpacks-java/skaffold.yaml index fec6bb42409..04237cad528 100644 --- a/integration/examples/buildpacks-java/skaffold.yaml +++ b/integration/examples/buildpacks-java/skaffold.yaml @@ -7,8 +7,6 @@ build: builder: "gcr.io/buildpacks/builder:v1" env: - GOOGLE_RUNTIME_VERSION=8 - sync: - auto: {} profiles: - name: gcb build: diff --git a/integration/examples/buildpacks-node/skaffold.yaml b/integration/examples/buildpacks-node/skaffold.yaml index 5100dc1abb7..5f34b414c89 100644 --- a/integration/examples/buildpacks-node/skaffold.yaml +++ b/integration/examples/buildpacks-node/skaffold.yaml @@ -5,5 +5,3 @@ build: - image: skaffold-buildpacks-node buildpacks: builder: "gcr.io/buildpacks/builder:v1" - sync: - auto: {} diff --git a/integration/examples/buildpacks/skaffold.yaml b/integration/examples/buildpacks/skaffold.yaml index 77dd29ab38e..f5ee9475dee 100644 --- a/integration/examples/buildpacks/skaffold.yaml +++ b/integration/examples/buildpacks/skaffold.yaml @@ -7,8 +7,6 @@ build: builder: "gcr.io/buildpacks/builder:v1" env: - GOPROXY={{.GOPROXY}} - sync: - auto: {} profiles: - name: gcb build: diff --git a/pkg/skaffold/schema/defaults/defaults.go b/pkg/skaffold/schema/defaults/defaults.go index 15a4020bb69..2ed376e0e7e 100644 --- a/pkg/skaffold/schema/defaults/defaults.go +++ b/pkg/skaffold/schema/defaults/defaults.go @@ -235,6 +235,8 @@ func setDefaultSync(a *latest.Artifact) { a.Sync.Infer = []string{"**/*"} } } + } else if a.BuildpackArtifact != nil { + a.Sync = &latest.Sync{Auto: &latest.Auto{}} } } diff --git a/pkg/skaffold/schema/defaults/defaults_test.go b/pkg/skaffold/schema/defaults/defaults_test.go index a07e5479f50..e341f4332df 100644 --- a/pkg/skaffold/schema/defaults/defaults_test.go +++ b/pkg/skaffold/schema/defaults/defaults_test.go @@ -65,6 +65,12 @@ func TestSetDefaults(t *testing.T) { }, Sync: &latest.Sync{}, }, + { + ImageName: "sixth", + ArtifactType: latest.ArtifactType{ + BuildpackArtifact: &latest.BuildpackArtifact{}, + }, + }, }, }, }, @@ -94,6 +100,12 @@ func TestSetDefaults(t *testing.T) { testutil.CheckDeepEqual(t, "fifth", cfg.Build.Artifacts[4].ImageName) testutil.CheckDeepEqual(t, &latest.Auto{}, cfg.Build.Artifacts[4].Sync.Auto) + + testutil.CheckDeepEqual(t, "sixth", cfg.Build.Artifacts[5].ImageName) + testutil.CheckDeepEqual(t, []string{"."}, cfg.Build.Artifacts[5].BuildpackArtifact.Dependencies.Paths) + testutil.CheckDeepEqual(t, []string(nil), cfg.Build.Artifacts[5].BuildpackArtifact.Dependencies.Ignore) + testutil.CheckDeepEqual(t, "project.toml", cfg.Build.Artifacts[5].BuildpackArtifact.ProjectDescriptor) + testutil.CheckDeepEqual(t, &latest.Auto{}, cfg.Build.Artifacts[5].Sync.Auto) } func TestSetDefaultsOnCluster(t *testing.T) { From 0d7561abc8358e2b5fe95396de695ee6b2c2749c Mon Sep 17 00:00:00 2001 From: Brian C Date: Thu, 13 Aug 2020 21:55:24 -0700 Subject: [PATCH 076/138] Add support for reloading kustomize components. --- pkg/skaffold/deploy/kustomize.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/skaffold/deploy/kustomize.go b/pkg/skaffold/deploy/kustomize.go index d0d93c87b18..9ba8f4bec8f 100644 --- a/pkg/skaffold/deploy/kustomize.go +++ b/pkg/skaffold/deploy/kustomize.go @@ -49,6 +49,7 @@ var ( // kustomization is the content of a kustomization.yaml file. type kustomization struct { + Components []string `yaml:"components"` Bases []string `yaml:"bases"` Resources []string `yaml:"resources"` Patches []patchWrapper `yaml:"patches"` @@ -257,6 +258,7 @@ func dependenciesForKustomization(dir string) ([]string, error) { deps = append(deps, path) candidates := append(content.Bases, content.Resources...) + candidates = append(candidates, content.Components...) for _, candidate := range candidates { // If the file doesn't exist locally, we can assume it's a remote file and From 4ef6167400e9f3ce95c022df045be932a7f84b5d Mon Sep 17 00:00:00 2001 From: Brian C Date: Thu, 13 Aug 2020 22:04:19 -0700 Subject: [PATCH 077/138] Fixing whitespace -- my editor had expandtabs set. --- pkg/skaffold/deploy/kustomize.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/skaffold/deploy/kustomize.go b/pkg/skaffold/deploy/kustomize.go index 9ba8f4bec8f..0fcdcd43b03 100644 --- a/pkg/skaffold/deploy/kustomize.go +++ b/pkg/skaffold/deploy/kustomize.go @@ -49,7 +49,7 @@ var ( // kustomization is the content of a kustomization.yaml file. type kustomization struct { - Components []string `yaml:"components"` + Components []string `yaml:"components"` Bases []string `yaml:"bases"` Resources []string `yaml:"resources"` Patches []patchWrapper `yaml:"patches"` @@ -258,7 +258,7 @@ func dependenciesForKustomization(dir string) ([]string, error) { deps = append(deps, path) candidates := append(content.Bases, content.Resources...) - candidates = append(candidates, content.Components...) + candidates = append(candidates, content.Components...) for _, candidate := range candidates { // If the file doesn't exist locally, we can assume it's a remote file and From e0b19ef95c226abbcf37c5b75db4ca485819f21c Mon Sep 17 00:00:00 2001 From: gsquared94 Date: Sun, 16 Aug 2020 18:38:54 +0530 Subject: [PATCH 078/138] Enable watch trigger only when either one of autoBuild, autoSync or autoDeploy is active --- pkg/skaffold/runner/intent.go | 6 ++ pkg/skaffold/runner/new.go | 9 +-- pkg/skaffold/trigger/triggers.go | 51 ++++++++++--- pkg/skaffold/trigger/triggers_test.go | 102 +++++++++++++++++++++----- 4 files changed, 137 insertions(+), 31 deletions(-) diff --git a/pkg/skaffold/runner/intent.go b/pkg/skaffold/runner/intent.go index 094bcb0345e..e94120d430b 100644 --- a/pkg/skaffold/runner/intent.go +++ b/pkg/skaffold/runner/intent.go @@ -117,3 +117,9 @@ func (i *intents) GetIntents() (bool, bool, bool) { defer i.lock.Unlock() return i.build, i.sync, i.deploy } + +func (i *intents) IsAnyAutoEnabled() bool { + i.lock.Lock() + defer i.lock.Unlock() + return i.autoBuild || i.autoSync || i.autoDeploy +} diff --git a/pkg/skaffold/runner/new.go b/pkg/skaffold/runner/new.go index 9a21571e4a3..a54dd7cad9e 100644 --- a/pkg/skaffold/runner/new.go +++ b/pkg/skaffold/runner/new.go @@ -84,16 +84,15 @@ func NewForConfig(runCtx *runcontext.RunContext) (*SkaffoldRunner, error) { deployer = WithNotification(deployer) } - trigger, err := trigger.NewTrigger(runCtx) - if err != nil { - return nil, fmt.Errorf("creating watch trigger: %w", err) - } - event.InitializeState(runCtx.Pipeline(), runCtx.GetKubeContext(), runCtx.AutoBuild(), runCtx.AutoDeploy(), runCtx.AutoSync()) event.LogMetaEvent() monitor := filemon.NewMonitor() intents, intentChan := setupIntents(runCtx) + trigger, err := trigger.NewTrigger(runCtx, intents.IsAnyAutoEnabled) + if err != nil { + return nil, fmt.Errorf("creating watch trigger: %w", err) + } return &SkaffoldRunner{ builder: builder, diff --git a/pkg/skaffold/trigger/triggers.go b/pkg/skaffold/trigger/triggers.go index 9c9cb6eea25..8f33213e2d6 100644 --- a/pkg/skaffold/trigger/triggers.go +++ b/pkg/skaffold/trigger/triggers.go @@ -42,22 +42,25 @@ type Trigger interface { } // NewTrigger creates a new trigger. -func NewTrigger(runCtx *runcontext.RunContext) (Trigger, error) { +func NewTrigger(runCtx *runcontext.RunContext, isActive func() bool) (Trigger, error) { switch strings.ToLower(runCtx.Trigger()) { case "polling": return &pollTrigger{ Interval: time.Duration(runCtx.WatchPollInterval()) * time.Millisecond, + isActive: isActive, }, nil case "notify": - return newFSNotifyTrigger(runCtx), nil + return newFSNotifyTrigger(runCtx, isActive), nil case "manual": - return &manualTrigger{}, nil + return &manualTrigger{ + isActive: isActive, + }, nil default: return nil, fmt.Errorf("unsupported trigger: %s", runCtx.Trigger()) } } -func newFSNotifyTrigger(runCtx *runcontext.RunContext) *fsNotifyTrigger { +func newFSNotifyTrigger(runCtx *runcontext.RunContext, isActive func() bool) *fsNotifyTrigger { workspaces := map[string]struct{}{} for _, a := range runCtx.Pipeline().Build.Artifacts { workspaces[a.Workspace] = struct{}{} @@ -65,12 +68,14 @@ func newFSNotifyTrigger(runCtx *runcontext.RunContext) *fsNotifyTrigger { return &fsNotifyTrigger{ Interval: time.Duration(runCtx.WatchPollInterval()) * time.Millisecond, workspaces: workspaces, + isActive: isActive, } } // pollTrigger watches for changes on a given interval of time. type pollTrigger struct { Interval time.Duration + isActive func() bool } // Debounce tells the watcher to debounce rapid sequence of changes. @@ -79,10 +84,14 @@ func (t *pollTrigger) Debounce() bool { } func (t *pollTrigger) LogWatchToUser(out io.Writer) { - color.Yellow.Fprintf(out, "Watching for changes every %v...\n", t.Interval) + if t.isActive() { + color.Yellow.Fprintf(out, "Watching for changes every %v...\n", t.Interval) + } else { + color.Yellow.Fprintln(out, "Not watching for changes...") + } } -// Start starts a timer. +// Start starts a timer if active. func (t *pollTrigger) Start(ctx context.Context) (<-chan bool, error) { trigger := make(chan bool) @@ -91,6 +100,9 @@ func (t *pollTrigger) Start(ctx context.Context) (<-chan bool, error) { for { select { case <-ticker.C: + if !t.isActive() { + continue + } trigger <- true case <-ctx.Done(): ticker.Stop() @@ -103,7 +115,9 @@ func (t *pollTrigger) Start(ctx context.Context) (<-chan bool, error) { } // manualTrigger watches for changes when the user presses a key. -type manualTrigger struct{} +type manualTrigger struct { + isActive func() bool +} // Debounce tells the watcher to not debounce rapid sequence of changes. func (t *manualTrigger) Debounce() bool { @@ -111,7 +125,11 @@ func (t *manualTrigger) Debounce() bool { } func (t *manualTrigger) LogWatchToUser(out io.Writer) { - color.Yellow.Fprintln(out, "Press any key to rebuild/redeploy the changes") + if t.isActive() { + color.Yellow.Fprintln(out, "Press any key to rebuild/redeploy the changes") + } else { + color.Yellow.Fprintln(out, "Not watching for changes...") + } } // Start starts listening to pressed keys. @@ -136,6 +154,11 @@ func (t *manualTrigger) Start(ctx context.Context) (<-chan bool, error) { if atomic.LoadInt32(&stopped) == 1 { return } + + // Ignore if trigger is inactive + if !t.isActive() { + continue + } trigger <- true } }() @@ -147,6 +170,7 @@ func (t *manualTrigger) Start(ctx context.Context) (<-chan bool, error) { type fsNotifyTrigger struct { Interval time.Duration workspaces map[string]struct{} + isActive func() bool } // Debounce tells the watcher to not debounce rapid sequence of changes. @@ -156,7 +180,11 @@ func (t *fsNotifyTrigger) Debounce() bool { } func (t *fsNotifyTrigger) LogWatchToUser(out io.Writer) { - color.Yellow.Fprintln(out, "Watching for changes...") + if t.isActive() { + color.Yellow.Fprintln(out, "Watching for changes...") + } else { + color.Yellow.Fprintln(out, "Not watching for changes...") + } } // Start listening for file system changes @@ -197,6 +225,11 @@ func (t *fsNotifyTrigger) Start(ctx context.Context) (<-chan bool, error) { for { select { case e := <-c: + + // Ignore detected changes if not active + if !t.isActive() { + continue + } logrus.Debugln("Change detected", e) // Wait t.interval before triggering. diff --git a/pkg/skaffold/trigger/triggers_test.go b/pkg/skaffold/trigger/triggers_test.go index 2bb727c3db3..8fc6ff275e1 100644 --- a/pkg/skaffold/trigger/triggers_test.go +++ b/pkg/skaffold/trigger/triggers_test.go @@ -84,10 +84,10 @@ func TestNewTrigger(t *testing.T) { }, } - got, err := NewTrigger(runCtx) + got, err := NewTrigger(runCtx, nil) t.CheckError(test.shouldErr, err) if !test.shouldErr { - t.CheckDeepEqual(test.expected, got, cmp.AllowUnexported(fsNotifyTrigger{})) + t.CheckDeepEqual(test.expected, got, cmp.AllowUnexported(fsNotifyTrigger{}), cmp.AllowUnexported(manualTrigger{}), cmp.AllowUnexported(pollTrigger{})) } }) } @@ -100,13 +100,36 @@ func TestPollTrigger_Debounce(t *testing.T) { } func TestPollTrigger_LogWatchToUser(t *testing.T) { - out := new(bytes.Buffer) + tests := []struct { + description string + isActive bool + expected string + }{ + { + description: "active polling trigger", + isActive: true, + expected: "Watching for changes every 10ns...\n", + }, + { + description: "inactive polling trigger", + isActive: false, + expected: "Not watching for changes...\n", + }, + } + for _, test := range tests { + out := new(bytes.Buffer) - trigger := &pollTrigger{Interval: 10} - trigger.LogWatchToUser(out) + trigger := &pollTrigger{ + Interval: 10, + isActive: func() bool { + return test.isActive + }, + } + trigger.LogWatchToUser(out) - got, want := out.String(), "Watching for changes every 10ns...\n" - testutil.CheckDeepEqual(t, want, got) + got, want := out.String(), test.expected + testutil.CheckDeepEqual(t, want, got) + } } func TestNotifyTrigger_Debounce(t *testing.T) { @@ -116,13 +139,36 @@ func TestNotifyTrigger_Debounce(t *testing.T) { } func TestNotifyTrigger_LogWatchToUser(t *testing.T) { - out := new(bytes.Buffer) + tests := []struct { + description string + isActive bool + expected string + }{ + { + description: "active notify trigger", + isActive: true, + expected: "Watching for changes...\n", + }, + { + description: "inactive notify trigger", + isActive: false, + expected: "Not watching for changes...\n", + }, + } + for _, test := range tests { + out := new(bytes.Buffer) - trigger := &fsNotifyTrigger{Interval: 10} - trigger.LogWatchToUser(out) + trigger := &fsNotifyTrigger{ + Interval: 10, + isActive: func() bool { + return test.isActive + }, + } + trigger.LogWatchToUser(out) - got, want := out.String(), "Watching for changes...\n" - testutil.CheckDeepEqual(t, want, got) + got, want := out.String(), test.expected + testutil.CheckDeepEqual(t, want, got) + } } func TestManualTrigger_Debounce(t *testing.T) { @@ -132,11 +178,33 @@ func TestManualTrigger_Debounce(t *testing.T) { } func TestManualTrigger_LogWatchToUser(t *testing.T) { - out := new(bytes.Buffer) + tests := []struct { + description string + isActive bool + expected string + }{ + { + description: "active manual trigger", + isActive: true, + expected: "Press any key to rebuild/redeploy the changes\n", + }, + { + description: "inactive manual trigger", + isActive: false, + expected: "Not watching for changes...\n", + }, + } + for _, test := range tests { + out := new(bytes.Buffer) - trigger := &manualTrigger{} - trigger.LogWatchToUser(out) + trigger := &manualTrigger{ + isActive: func() bool { + return test.isActive + }, + } + trigger.LogWatchToUser(out) - got, want := out.String(), "Press any key to rebuild/redeploy the changes\n" - testutil.CheckDeepEqual(t, want, got) + got, want := out.String(), test.expected + testutil.CheckDeepEqual(t, want, got) + } } From eda9faf5394e8c55b0401a93311dddca9ddaa78a Mon Sep 17 00:00:00 2001 From: gsquared94 Date: Mon, 17 Aug 2020 23:07:08 +0530 Subject: [PATCH 079/138] rearrange comments. --- pkg/skaffold/trigger/triggers.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/skaffold/trigger/triggers.go b/pkg/skaffold/trigger/triggers.go index 8f33213e2d6..7af9870dfb6 100644 --- a/pkg/skaffold/trigger/triggers.go +++ b/pkg/skaffold/trigger/triggers.go @@ -91,7 +91,7 @@ func (t *pollTrigger) LogWatchToUser(out io.Writer) { } } -// Start starts a timer if active. +// Start starts a timer. func (t *pollTrigger) Start(ctx context.Context) (<-chan bool, error) { trigger := make(chan bool) @@ -100,6 +100,8 @@ func (t *pollTrigger) Start(ctx context.Context) (<-chan bool, error) { for { select { case <-ticker.C: + + // Ignore if trigger is inactive if !t.isActive() { continue } From e4166b6498defd4b3048b7d1f60119bda1ec47c5 Mon Sep 17 00:00:00 2001 From: Felix Date: Mon, 17 Aug 2020 15:08:53 -0400 Subject: [PATCH 080/138] kpt deployer implementation --- pkg/skaffold/deploy/kpt.go | 49 +++++++++++++++++++++++++++++++++ pkg/skaffold/deploy/kpt_test.go | 17 ++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 pkg/skaffold/deploy/kpt.go create mode 100644 pkg/skaffold/deploy/kpt_test.go diff --git a/pkg/skaffold/deploy/kpt.go b/pkg/skaffold/deploy/kpt.go new file mode 100644 index 00000000000..87c633a589f --- /dev/null +++ b/pkg/skaffold/deploy/kpt.go @@ -0,0 +1,49 @@ +/* +Copyright 2020 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 deploy + +import ( + "context" + "io" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" +) + +// KptDeployer deploys workflows with kpt CLI +type KptDeployer struct { +} + +func NewKptDeployer(runCtx *runcontext.RunContext, labels map[string]string) *KptDeployer { + return &KptDeployer{} +} + +func (k *KptDeployer) Deploy(ctx context.Context, out io.Writer, builds []build.Artifact) ([]string, error) { + return nil, nil +} + +func (k *KptDeployer) Dependencies() ([]string, error) { + return nil, nil +} + +func (k *KptDeployer) Cleanup(ctx context.Context, out io.Writer) error { + return nil +} + +func (k *KptDeployer) Render(ctx context.Context, out io.Writer, builds []build.Artifact, offline bool, filepath string) error { + return nil +} diff --git a/pkg/skaffold/deploy/kpt_test.go b/pkg/skaffold/deploy/kpt_test.go new file mode 100644 index 00000000000..27bdac348ae --- /dev/null +++ b/pkg/skaffold/deploy/kpt_test.go @@ -0,0 +1,17 @@ +/* +Copyright 2020 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 deploy From 4fcca14e6bb3670eb77573f421b0def733bb28c4 Mon Sep 17 00:00:00 2001 From: Nick Kubala Date: Mon, 17 Aug 2020 13:20:45 -0700 Subject: [PATCH 081/138] Return deployment status code when status check can't retrieve pods from cluster --- pkg/skaffold/deploy/resource/deployment.go | 3 +++ pkg/skaffold/deploy/status_check_test.go | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/pkg/skaffold/deploy/resource/deployment.go b/pkg/skaffold/deploy/resource/deployment.go index c5ec5d40de5..f9b068cd1e8 100644 --- a/pkg/skaffold/deploy/resource/deployment.go +++ b/pkg/skaffold/deploy/resource/deployment.go @@ -268,6 +268,9 @@ func (d *Deployment) fetchPods(ctx context.Context) error { // Return first pod status in error. // TODO: should we return all distinct error codes in future? func (d *Deployment) FirstPodErrOccurred() proto.StatusCode { + if len(d.pods) == 0 { + return d.Status().ActionableError().ErrCode + } for _, p := range d.pods { if s := p.ActionableError().ErrCode; s != proto.StatusCode_STATUSCHECK_SUCCESS { return s diff --git a/pkg/skaffold/deploy/status_check_test.go b/pkg/skaffold/deploy/status_check_test.go index 79ca0fd1549..6a3f798c160 100644 --- a/pkg/skaffold/deploy/status_check_test.go +++ b/pkg/skaffold/deploy/status_check_test.go @@ -275,6 +275,18 @@ func TestGetDeployStatus(t *testing.T) { description: "0 deployments", counter: &counter{}, }, + { + description: "unable to retrieve pods for deployment", + counter: &counter{total: 1, failed: 1}, + deployments: []*resource.Deployment{ + withStatus( + resource.NewDeployment("deployment", "test", 1), + proto.ActionableErr{ErrCode: proto.StatusCode_STATUSCHECK_DEPLOYMENT_FETCH_ERR}, + ), + }, + shouldErr: true, + expectedCode: proto.StatusCode_STATUSCHECK_DEPLOYMENT_FETCH_ERR, + }, } for _, test := range tests { From 5c607561ae345d46237f8d016326c32939353c64 Mon Sep 17 00:00:00 2001 From: Felix Date: Mon, 17 Aug 2020 17:04:14 -0400 Subject: [PATCH 082/138] skeleton tests --- pkg/skaffold/deploy/kpt_test.go | 79 +++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/pkg/skaffold/deploy/kpt_test.go b/pkg/skaffold/deploy/kpt_test.go index 27bdac348ae..0762cd3caad 100644 --- a/pkg/skaffold/deploy/kpt_test.go +++ b/pkg/skaffold/deploy/kpt_test.go @@ -15,3 +15,82 @@ limitations under the License. */ package deploy + +import ( + "testing" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" + "github.com/GoogleContainerTools/skaffold/testutil" +) + +func TestKpt_Deploy(t *testing.T) { + tests := []struct { + description string + expected []string + shouldErr bool + }{ + { + description: "nil", + }, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + k := NewKptDeployer(&runcontext.RunContext{}, nil) + res, err := k.Deploy(nil, nil, nil) + t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expected, res) + }) + } +} + +func TestKpt_Dependencies(t *testing.T) { + tests := []struct { + description string + expected []string + shouldErr bool + }{ + { + description: "nil", + }, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + k := NewKptDeployer(&runcontext.RunContext{}, nil) + res, err := k.Dependencies() + t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expected, res) + }) + } +} + +func TestKpt_Cleanup(t *testing.T) { + tests := []struct { + description string + shouldErr bool + }{ + { + description: "nil", + }, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + k := NewKptDeployer(&runcontext.RunContext{}, nil) + err := k.Cleanup(nil, nil) + t.CheckError(test.shouldErr, err) + }) + } +} + +func TestKpt_Render(t *testing.T) { + tests := []struct { + description string + shouldErr bool + }{ + {}, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + k := NewKptDeployer(&runcontext.RunContext{}, nil) + err := k.Render(nil, nil, nil, false, "") + t.CheckError(test.shouldErr, err) + }) + } +} From f0142515b5171aaa9e267fac346966247078d287 Mon Sep 17 00:00:00 2001 From: Felix Date: Mon, 17 Aug 2020 17:23:27 -0400 Subject: [PATCH 083/138] fix linter --- pkg/skaffold/deploy/kpt_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/skaffold/deploy/kpt_test.go b/pkg/skaffold/deploy/kpt_test.go index 0762cd3caad..2e30177920a 100644 --- a/pkg/skaffold/deploy/kpt_test.go +++ b/pkg/skaffold/deploy/kpt_test.go @@ -17,6 +17,7 @@ limitations under the License. package deploy import ( + "context" "testing" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" @@ -36,7 +37,7 @@ func TestKpt_Deploy(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { k := NewKptDeployer(&runcontext.RunContext{}, nil) - res, err := k.Deploy(nil, nil, nil) + res, err := k.Deploy(context.Background(), nil, nil) t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expected, res) }) } @@ -73,7 +74,7 @@ func TestKpt_Cleanup(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { k := NewKptDeployer(&runcontext.RunContext{}, nil) - err := k.Cleanup(nil, nil) + err := k.Cleanup(context.Background(), nil) t.CheckError(test.shouldErr, err) }) } @@ -89,7 +90,7 @@ func TestKpt_Render(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { k := NewKptDeployer(&runcontext.RunContext{}, nil) - err := k.Render(nil, nil, nil, false, "") + err := k.Render(context.Background(), nil, nil, false, "") t.CheckError(test.shouldErr, err) }) } From 98dd69cad81de724b89c0206a8ff29445ab51964 Mon Sep 17 00:00:00 2001 From: Nick Kubala Date: Mon, 17 Aug 2020 15:41:14 -0700 Subject: [PATCH 084/138] Update maintainers and add kpt codeowners --- .github/CODEOWNERS | 7 +++---- MAINTAINERS | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 14b269fd3c2..6a24811abf3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,14 +2,13 @@ * @GoogleContainerTools/skaffold-team -# Buildpacks owners - -**/**buildpacks** @GoogleContainerTools/skaffold-team - # JIB owners **/**jib** @GoogleContainerTools/skaffold-team @loosebazooka @TadCordle @chanseokoh +# kpt owners +pkg/skaffold/deploy/**kpt** @GoogleContainerTools/skaffold-team @yuwenma + # Debug owners pkg/skaffold/debug/** @GoogleContainerTools/skaffold-team @loosebazooka @quoctruong diff --git a/MAINTAINERS b/MAINTAINERS index 567a6b8c451..2a46b547ebd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1,7 +1,7 @@ -Balint Pato +Brian de Alwis David Gageot +Gaurav Ghosh +Marlon Gamez Nick Kubala Tejal Desai -Brian de Alwis Thomas Strömberg -Gaurav Ghosh From 10ee3025ebbbbd456deba65372ea393de932f97b Mon Sep 17 00:00:00 2001 From: gsquared94 Date: Mon, 17 Aug 2020 12:48:12 +0530 Subject: [PATCH 085/138] refactor: move all update checks to single function; enforce honoring updateCheck flag --- cmd/skaffold/app/cmd/cmd.go | 25 ++------- cmd/skaffold/app/cmd/runner.go | 16 ++++-- pkg/skaffold/update/update.go | 36 ++++++++++++- pkg/skaffold/update/update_test.go | 83 +++++++++++++++++++++++++----- 4 files changed, 118 insertions(+), 42 deletions(-) diff --git a/cmd/skaffold/app/cmd/cmd.go b/cmd/skaffold/app/cmd/cmd.go index 6a95a96f9b1..695b84e0edf 100644 --- a/cmd/skaffold/app/cmd/cmd.go +++ b/cmd/skaffold/app/cmd/cmd.go @@ -23,7 +23,6 @@ import ( "os" "strings" - "github.com/blang/semver" "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -102,8 +101,11 @@ func NewSkaffoldCommand(out, err io.Writer) *cobra.Command { logrus.Debugf("Update check and survey prompt when running `init --analyze`") default: go func() { - if err := updateCheck(updateMsg, opts.GlobalConfig); err != nil { + msg, err := update.CheckVersion(opts.GlobalConfig) + if err != nil { logrus.Infof("update check failed: %s", err) + } else if msg != "" { + updateMsg <- msg } surveyPrompt <- config.ShouldDisplayPrompt(opts.GlobalConfig) }() @@ -195,25 +197,6 @@ func NewCmdOptions() *cobra.Command { return cmd } -func updateCheck(ch chan string, configfile string) error { - if !update.IsUpdateCheckEnabled(configfile) { - logrus.Debugf("Update check not enabled, skipping.") - return nil - } - latest, current, err := update.GetLatestAndCurrentVersion() - if err != nil { - return fmt.Errorf("get latest and current Skaffold version: %w", err) - } - if latest.GT(current) { - ch <- fmt.Sprintf("There is a new version (%s) of Skaffold available. Download it from:\n %s\n", latest, releaseURL(latest)) - } - return nil -} - -func releaseURL(v semver.Version) string { - return fmt.Sprintf("https://github.com/GoogleContainerTools/skaffold/releases/tag/v" + v.String()) -} - // Each flag can also be set with an env variable whose name starts with `SKAFFOLD_`. func setFlagsFromEnvVariables(rootCmd *cobra.Command) { rootCmd.PersistentFlags().VisitAll(func(f *pflag.Flag) { diff --git a/cmd/skaffold/app/cmd/runner.go b/cmd/skaffold/app/cmd/runner.go index 170eb3593ab..e4d8a385c20 100644 --- a/cmd/skaffold/app/cmd/runner.go +++ b/cmd/skaffold/app/cmd/runner.go @@ -76,7 +76,9 @@ func runContext(opts config.SkaffoldOptions) (*runcontext.RunContext, *latest.Sk // If the error is NOT that the file doesn't exist, then we warn the user // that maybe they are using an outdated version of Skaffold that's unable to read // the configuration. - warnIfUpdateIsAvailable() + if err = warnIfUpdateIsAvailable(); err != nil { + return nil, nil, err + } return nil, nil, fmt.Errorf("parsing skaffold config: %w", err) } @@ -104,9 +106,13 @@ func runContext(opts config.SkaffoldOptions) (*runcontext.RunContext, *latest.Sk return runCtx, config, nil } -func warnIfUpdateIsAvailable() { - latest, current, versionErr := update.GetLatestAndCurrentVersion() - if versionErr == nil && latest.GT(current) { - logrus.Warnf("Your Skaffold version might be too old. Download the latest version (%s) from:\n %s\n", latest, releaseURL(latest)) +func warnIfUpdateIsAvailable() error { + warning, err := update.CheckVersionOnError(opts.GlobalConfig) + if err != nil { + return fmt.Errorf("update check failed: %s", err) + } + if warning != "" { + logrus.Warn(warning) } + return nil } diff --git a/pkg/skaffold/update/update.go b/pkg/skaffold/update/update.go index 36b6d53523d..3c09fc6ecc9 100644 --- a/pkg/skaffold/update/update.go +++ b/pkg/skaffold/update/update.go @@ -40,9 +40,37 @@ var ( const LatestVersionURL = "https://storage.googleapis.com/skaffold/releases/latest/VERSION" -// IsUpdateCheckEnabled returns whether or not the update check is enabled +// CheckVersion returns an update message when update check is enabled and skaffold binary in not latest +func CheckVersion(config string) (string, error) { + return checkVersion(config, false) +} + +// CheckVersionOnError returns an error message when update check is enabled and skaffold binary in not latest +func CheckVersionOnError(config string) (string, error) { + return checkVersion(config, true) +} + +func checkVersion(config string, onError bool) (string, error) { + if !isUpdateCheckEnabled(config) { + logrus.Debugf("Update check not enabled, skipping.") + return "", nil + } + latest, current, err := GetLatestAndCurrentVersion() + if err != nil { + return "", fmt.Errorf("get latest and current Skaffold version: %w", err) + } + if !latest.GT(current) { + return "", nil + } + if onError { + return fmt.Sprintf("Your Skaffold version might be too old. Download the latest version (%s) from:\n %s\n", latest, releaseURL(latest)), nil + } + return fmt.Sprintf("There is a new version (%s) of Skaffold available. Download it from:\n %s\n", latest, releaseURL(latest)), nil +} + +// isUpdateCheckEnabled returns whether or not the update check is enabled // It is true by default, but setting it to any other value than true will disable the check -func IsUpdateCheckEnabled(configfile string) bool { +func isUpdateCheckEnabled(configfile string) bool { // Don't perform a version check on dirty trees if version.Get().GitTreeState == "dirty" { return false @@ -86,3 +114,7 @@ func DownloadLatestVersion() (string, error) { } return strings.TrimSuffix(string(versionBytes), "\n"), nil } + +func releaseURL(v semver.Version) string { + return fmt.Sprintf("https://github.com/GoogleContainerTools/skaffold/releases/tag/v" + v.String()) +} diff --git a/pkg/skaffold/update/update_test.go b/pkg/skaffold/update/update_test.go index 7b483614eaa..c1b1e72e787 100644 --- a/pkg/skaffold/update/update_test.go +++ b/pkg/skaffold/update/update_test.go @@ -17,51 +17,106 @@ limitations under the License. package update import ( + "fmt" "testing" + "github.com/blang/semver" + "github.com/GoogleContainerTools/skaffold/testutil" ) -func TestIsUpdateCheckEnabled(t *testing.T) { +func TestCheckVersions(t *testing.T) { tests := []struct { - description string - enabled bool - configCheck bool - expected bool + description string + checkVersionsFunc func() (semver.Version, semver.Version, error) + expected []string + enabled bool + configCheck bool + shouldError bool }{ { description: "globally disabled - disabled in config -> disabled", enabled: false, configCheck: false, - expected: false, + expected: []string{"", ""}, }, { description: "globally enabled - disabled in config -> disabled", enabled: true, configCheck: false, - expected: false, + expected: []string{"", ""}, }, { description: "globally disabled - enabled in config -> disabled", enabled: false, configCheck: true, - expected: false, + expected: []string{"", ""}, }, { - description: "globally enabled - enabled in config -> enabled", - enabled: true, - configCheck: true, - expected: true, + description: "globally enabled - enabled in config - latest version -> disabled", + enabled: true, + configCheck: true, + checkVersionsFunc: currentEqualsLatest, + expected: []string{"", ""}, + }, + { + description: "globally enabled - enabled in config - older version -> enabled", + enabled: true, + configCheck: true, + checkVersionsFunc: latestGreaterThanCurrent, + expected: []string{ + "There is a new version (1.1.0) of Skaffold available. Download it from:\n https://github.com/GoogleContainerTools/skaffold/releases/tag/v1.1.0\n", + "Your Skaffold version might be too old. Download the latest version (1.1.0) from:\n https://github.com/GoogleContainerTools/skaffold/releases/tag/v1.1.0\n", + }, + }, + { + description: "globally enabled - enabled in config - version check failed -> enabled", + enabled: true, + configCheck: true, + checkVersionsFunc: errorGettingVersions, + shouldError: true, + expected: []string{"", ""}, }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { t.Override(&EnableCheck, test.enabled) t.Override(&isConfigUpdateCheckEnabled, func(string) bool { return test.configCheck }) + t.Override(&GetLatestAndCurrentVersion, test.checkVersionsFunc) - isEnabled := IsUpdateCheckEnabled("dummyconfig") + msg, err := CheckVersion("foo") + t.CheckErrorAndDeepEqual(test.shouldError, err, test.expected[0], msg) - t.CheckDeepEqual(test.expected, isEnabled) + msg, err = CheckVersionOnError("foo") + t.CheckErrorAndDeepEqual(test.shouldError, err, test.expected[1], msg) }) } } + +func latestGreaterThanCurrent() (semver.Version, semver.Version, error) { + return semver.Version{ + Major: 1, + Minor: 1, + Patch: 0, + }, semver.Version{ + Major: 1, + Minor: 0, + Patch: 0, + }, nil +} + +func currentEqualsLatest() (semver.Version, semver.Version, error) { + return semver.Version{ + Major: 1, + Minor: 0, + Patch: 0, + }, semver.Version{ + Major: 1, + Minor: 0, + Patch: 0, + }, nil +} + +func errorGettingVersions() (semver.Version, semver.Version, error) { + return semver.Version{}, semver.Version{}, fmt.Errorf("error getting versions") +} From 077c40041b85e0062539e490805a8e5c4a22c749 Mon Sep 17 00:00:00 2001 From: gsquared94 Date: Tue, 18 Aug 2020 00:16:27 +0530 Subject: [PATCH 086/138] fix error reassignment --- cmd/skaffold/app/cmd/runner.go | 10 ++++------ pkg/skaffold/update/update.go | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/cmd/skaffold/app/cmd/runner.go b/cmd/skaffold/app/cmd/runner.go index e4d8a385c20..7bf38490c4b 100644 --- a/cmd/skaffold/app/cmd/runner.go +++ b/cmd/skaffold/app/cmd/runner.go @@ -76,9 +76,7 @@ func runContext(opts config.SkaffoldOptions) (*runcontext.RunContext, *latest.Sk // If the error is NOT that the file doesn't exist, then we warn the user // that maybe they are using an outdated version of Skaffold that's unable to read // the configuration. - if err = warnIfUpdateIsAvailable(); err != nil { - return nil, nil, err - } + warnIfUpdateIsAvailable() return nil, nil, fmt.Errorf("parsing skaffold config: %w", err) } @@ -106,13 +104,13 @@ func runContext(opts config.SkaffoldOptions) (*runcontext.RunContext, *latest.Sk return runCtx, config, nil } -func warnIfUpdateIsAvailable() error { +func warnIfUpdateIsAvailable() { warning, err := update.CheckVersionOnError(opts.GlobalConfig) if err != nil { - return fmt.Errorf("update check failed: %s", err) + logrus.Debugf("update check failed: %w", err) + return } if warning != "" { logrus.Warn(warning) } - return nil } diff --git a/pkg/skaffold/update/update.go b/pkg/skaffold/update/update.go index 3c09fc6ecc9..51a04bbdcdc 100644 --- a/pkg/skaffold/update/update.go +++ b/pkg/skaffold/update/update.go @@ -57,7 +57,7 @@ func checkVersion(config string, onError bool) (string, error) { } latest, current, err := GetLatestAndCurrentVersion() if err != nil { - return "", fmt.Errorf("get latest and current Skaffold version: %w", err) + return "", fmt.Errorf("getting latest and current skaffold versions: %w", err) } if !latest.GT(current) { return "", nil From 07f87fe6a455b859cbd8a568685004769ac928c6 Mon Sep 17 00:00:00 2001 From: gsquared94 Date: Tue, 18 Aug 2020 12:21:35 +0530 Subject: [PATCH 087/138] Fix incorrect verb in string format --- cmd/skaffold/app/cmd/runner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/skaffold/app/cmd/runner.go b/cmd/skaffold/app/cmd/runner.go index 7bf38490c4b..399138404b4 100644 --- a/cmd/skaffold/app/cmd/runner.go +++ b/cmd/skaffold/app/cmd/runner.go @@ -107,7 +107,7 @@ func runContext(opts config.SkaffoldOptions) (*runcontext.RunContext, *latest.Sk func warnIfUpdateIsAvailable() { warning, err := update.CheckVersionOnError(opts.GlobalConfig) if err != nil { - logrus.Debugf("update check failed: %w", err) + logrus.Debugf("update check failed: %v", err) return } if warning != "" { From fb1eed0faa7327495db41ed8f374d3e59ba1cae5 Mon Sep 17 00:00:00 2001 From: gsquared94 Date: Tue, 18 Aug 2020 12:23:21 +0530 Subject: [PATCH 088/138] Fix incorrect verb in string format --- cmd/skaffold/app/cmd/runner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/skaffold/app/cmd/runner.go b/cmd/skaffold/app/cmd/runner.go index 399138404b4..fd2703280b8 100644 --- a/cmd/skaffold/app/cmd/runner.go +++ b/cmd/skaffold/app/cmd/runner.go @@ -107,7 +107,7 @@ func runContext(opts config.SkaffoldOptions) (*runcontext.RunContext, *latest.Sk func warnIfUpdateIsAvailable() { warning, err := update.CheckVersionOnError(opts.GlobalConfig) if err != nil { - logrus.Debugf("update check failed: %v", err) + logrus.Infof("update check failed: %s", err) return } if warning != "" { From df141cd9e24c304e9f2353e4801400d6bb4ea478 Mon Sep 17 00:00:00 2001 From: Nick Kubala Date: Mon, 17 Aug 2020 10:51:36 -0700 Subject: [PATCH 089/138] improved error message when skaffold config not found --- cmd/skaffold/app/cmd/runner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/skaffold/app/cmd/runner.go b/cmd/skaffold/app/cmd/runner.go index fd2703280b8..86ddc9cbac5 100644 --- a/cmd/skaffold/app/cmd/runner.go +++ b/cmd/skaffold/app/cmd/runner.go @@ -70,7 +70,7 @@ func runContext(opts config.SkaffoldOptions) (*runcontext.RunContext, *latest.Sk parsed, err := schema.ParseConfigAndUpgrade(opts.ConfigurationFile, latest.Version) if err != nil { if os.IsNotExist(errors.Unwrap(err)) { - return nil, nil, fmt.Errorf("[%s] not found. You might need to run `skaffold init`", opts.ConfigurationFile) + return nil, nil, fmt.Errorf("skaffold config file %s not found - check your current working directory, or try running `skaffold init`", opts.ConfigurationFile) } // If the error is NOT that the file doesn't exist, then we warn the user From b57d64d3ac80fbb3bd81ece60d556c02da6c09eb Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Tue, 18 Aug 2020 14:30:10 +0000 Subject: [PATCH 090/138] Add kpt deployer config (#4689) --- docs/content/en/schemas/v2beta7.json | 73 +++++++++++++++++++++ pkg/skaffold/runner/new.go | 4 ++ pkg/skaffold/runner/new_test.go | 89 ++++++++++++++++++++++++++ pkg/skaffold/schema/latest/config.go | 31 +++++++++ pkg/skaffold/schema/v2beta6/upgrade.go | 2 + 5 files changed, 199 insertions(+) diff --git a/docs/content/en/schemas/v2beta7.json b/docs/content/en/schemas/v2beta7.json index 32438e34588..4f6da448f73 100755 --- a/docs/content/en/schemas/v2beta7.json +++ b/docs/content/en/schemas/v2beta7.json @@ -828,6 +828,11 @@ "description": "*beta* uses the `helm` CLI to apply the charts to the cluster.", "x-intellij-html-description": "beta uses the helm CLI to apply the charts to the cluster." }, + "kpt": { + "$ref": "#/definitions/KptDeploy", + "description": "*beta* uses the `kpt` CLI to manage and deploy manifests.", + "x-intellij-html-description": "beta uses the kpt CLI to manage and deploy manifests." + }, "kubeContext": { "type": "string", "description": "Kubernetes context that Skaffold should deploy to.", @@ -861,6 +866,7 @@ "helm", "kubectl", "kustomize", + "kpt", "statusCheckDeadlineSeconds", "kubeContext", "logs" @@ -1593,6 +1599,73 @@ "description": "configures Kaniko caching. If a cache is specified, Kaniko will use a remote cache which will speed up builds.", "x-intellij-html-description": "configures Kaniko caching. If a cache is specified, Kaniko will use a remote cache which will speed up builds." }, + "KptDeploy": { + "properties": { + "dir": { + "type": "string", + "description": "path to the directory to run kpt functions against.", + "x-intellij-html-description": "path to the directory to run kpt functions against." + }, + "fn": { + "$ref": "#/definitions/KptFn", + "description": "adds additional configurations for `kpt fn`.", + "x-intellij-html-description": "adds additional configurations for kpt fn." + }, + "live": { + "$ref": "#/definitions/KptLive", + "description": "adds additional configurations for `kpt live`.", + "x-intellij-html-description": "adds additional configurations for kpt live." + } + }, + "preferredOrder": [ + "dir", + "fn", + "live" + ], + "additionalProperties": false, + "description": "*beta* uses the `kpt` CLI to manage and deploy manifests.", + "x-intellij-html-description": "beta uses the kpt CLI to manage and deploy manifests." + }, + "KptFn": { + "properties": { + "fnPath": { + "type": "string", + "description": "path to a kpt function or pipeline.", + "x-intellij-html-description": "path to a kpt function or pipeline." + }, + "image": { + "type": "string", + "description": "specifies an image to be run as a function.", + "x-intellij-html-description": "specifies an image to be run as a function." + } + }, + "preferredOrder": [ + "fnPath", + "image" + ], + "additionalProperties": false, + "description": "adds additional configurations used when calling `kpt fn`.", + "x-intellij-html-description": "adds additional configurations used when calling kpt fn." + }, + "KptLive": { + "properties": { + "apply": { + "items": { + "type": "string" + }, + "type": "array", + "description": "additional flags passed on creations (`kpt live apply`).", + "x-intellij-html-description": "additional flags passed on creations (kpt live apply).", + "default": "[]" + } + }, + "preferredOrder": [ + "apply" + ], + "additionalProperties": false, + "description": "adds additional configurations used when calling `kpt live` on every command (Global), creations (Apply), or deletions (Destroy).", + "x-intellij-html-description": "adds additional configurations used when calling kpt live on every command (Global), creations (Apply), or deletions (Destroy)." + }, "KubectlDeploy": { "properties": { "flags": { diff --git a/pkg/skaffold/runner/new.go b/pkg/skaffold/runner/new.go index a54dd7cad9e..6f9c887996c 100644 --- a/pkg/skaffold/runner/new.go +++ b/pkg/skaffold/runner/new.go @@ -204,6 +204,10 @@ func getDeployer(runCtx *runcontext.RunContext, labels map[string]string) deploy deployers = append(deployers, deploy.NewKustomizeDeployer(runCtx, labels)) } + if d.KptDeploy != nil { + deployers = append(deployers, deploy.NewKptDeployer(runCtx, labels)) + } + // avoid muxing overhead when only a single deployer is configured if len(deployers) == 1 { return deployers[0] diff --git a/pkg/skaffold/runner/new_test.go b/pkg/skaffold/runner/new_test.go index 7c6d07cc232..aaeea6b2efc 100644 --- a/pkg/skaffold/runner/new_test.go +++ b/pkg/skaffold/runner/new_test.go @@ -17,13 +17,102 @@ limitations under the License. package runner import ( + "reflect" "testing" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/tag" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/testutil" ) +func TestGetDeployer(t *testing.T) { + tests := []struct { + description string + cfg latest.DeployType + expected deploy.Deployer + }{ + { + description: "no deployer", + expected: deploy.DeployerMux{}, + }, + { + description: "helm deployer", + cfg: latest.DeployType{HelmDeploy: &latest.HelmDeploy{}}, + expected: deploy.NewHelmDeployer(&runcontext.RunContext{}, nil), + }, + { + description: "kubectl deployer", + cfg: latest.DeployType{KubectlDeploy: &latest.KubectlDeploy{}}, + expected: deploy.NewKubectlDeployer(&runcontext.RunContext{ + Cfg: latest.Pipeline{ + Deploy: latest.DeployConfig{ + DeployType: latest.DeployType{ + KubectlDeploy: &latest.KubectlDeploy{ + Flags: latest.KubectlFlags{}, + }, + }, + }, + }, + }, nil), + }, + { + description: "kustomize deployer", + cfg: latest.DeployType{KustomizeDeploy: &latest.KustomizeDeploy{}}, + expected: deploy.NewKustomizeDeployer(&runcontext.RunContext{ + Cfg: latest.Pipeline{ + Deploy: latest.DeployConfig{ + DeployType: latest.DeployType{ + KustomizeDeploy: &latest.KustomizeDeploy{ + Flags: latest.KubectlFlags{}, + }, + }, + }, + }, + }, nil), + }, + { + description: "kpt deployer", + cfg: latest.DeployType{KptDeploy: &latest.KptDeploy{}}, + expected: deploy.NewKptDeployer(&runcontext.RunContext{}, nil), + }, + { + description: "multiple deployers", + cfg: latest.DeployType{ + HelmDeploy: &latest.HelmDeploy{}, + KptDeploy: &latest.KptDeploy{}, + }, + expected: deploy.DeployerMux{ + deploy.NewHelmDeployer(&runcontext.RunContext{}, nil), + deploy.NewKptDeployer(&runcontext.RunContext{}, nil), + }, + }, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + deployer := getDeployer(&runcontext.RunContext{ + Cfg: latest.Pipeline{ + Deploy: latest.DeployConfig{ + DeployType: test.cfg, + }, + }, + }, nil) + + t.CheckTypeEquality(test.expected, deployer) + + if reflect.TypeOf(test.expected) == reflect.TypeOf(deploy.DeployerMux{}) { + expected := test.expected.(deploy.DeployerMux) + deployers := deployer.(deploy.DeployerMux) + t.CheckDeepEqual(len(expected), len(deployers)) + for i, v := range expected { + t.CheckTypeEquality(v, deployers[i]) + } + } + }) + } +} + func TestCreateComponents(t *testing.T) { gitExample, _ := tag.NewGitCommit("", "") envExample, _ := tag.NewEnvTemplateTagger("test") diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index b78f113550f..2a04cb326e9 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -453,6 +453,9 @@ type DeployType struct { // KustomizeDeploy *beta* uses the `kustomize` CLI to "patch" a deployment for a target environment. KustomizeDeploy *KustomizeDeploy `yaml:"kustomize,omitempty"` + + // KptDeploy *beta* uses the `kpt` CLI to manage and deploy manifests. + KptDeploy *KptDeploy `yaml:"kpt,omitempty"` } // KubectlDeploy *beta* uses a client side `kubectl apply` to deploy manifests. @@ -523,6 +526,34 @@ type KustomizeDeploy struct { BuildArgs []string `yaml:"buildArgs,omitempty"` } +// KptDeploy *beta* uses the `kpt` CLI to manage and deploy manifests. +type KptDeploy struct { + // Dir is the path to the directory to run kpt functions against. + Dir string `yaml:"dir,omitempty"` + + // Fn adds additional configurations for `kpt fn`. + Fn KptFn `yaml:"fn,omitempty"` + + // Live adds additional configurations for `kpt live`. + Live KptLive `yaml:"live,omitempty"` +} + +// KptFn adds additional configurations used when calling `kpt fn`. +type KptFn struct { + // FnPath is the path to a kpt function or pipeline. + FnPath string `yaml:"fnPath,omitempty"` + + // Image specifies an image to be run as a function. + Image string `yaml:"image,omitempty"` +} + +// KptLive adds additional configurations used when calling `kpt live` +// on every command (Global), creations (Apply), or deletions (Destroy). +type KptLive struct { + // Apply are additional flags passed on creations (`kpt live apply`). + Apply []string `yaml:"apply,omitempty"` +} + // HelmRelease describes a helm release to be deployed. type HelmRelease struct { // Name is the name of the Helm release. diff --git a/pkg/skaffold/schema/v2beta6/upgrade.go b/pkg/skaffold/schema/v2beta6/upgrade.go index e69167e5123..1be6696fe29 100755 --- a/pkg/skaffold/schema/v2beta6/upgrade.go +++ b/pkg/skaffold/schema/v2beta6/upgrade.go @@ -24,6 +24,8 @@ import ( // Upgrade upgrades a configuration to the next version. // Config changes from v2beta6 to v2beta7 +// 1. Additions: +// New KptDeploy, KptFn, and KptLive to support Kpt Deployer. func (c *SkaffoldConfig) Upgrade() (util.VersionedConfig, error) { var newConfig next.SkaffoldConfig pkgutil.CloneThroughJSON(c, &newConfig) From 3b0dd749a7682305f217d06a9028ca61d6fd795d Mon Sep 17 00:00:00 2001 From: Gaurav <39389231+gsquared94@users.noreply.github.com> Date: Tue, 18 Aug 2020 20:10:21 +0530 Subject: [PATCH 091/138] Fix bazel integration tests on Github Actions (#4678) * update bazel rules_docker version * update bazel rules_docker version * fix bazel example * increase log tailing timeout to 90s --- .github/workflows/integration-darwin.yml | 8 -------- .github/workflows/integration-linux.yml | 8 -------- integration/examples/bazel/WORKSPACE | 17 ++++++++++++++--- integration/util.go | 2 +- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/.github/workflows/integration-darwin.yml b/.github/workflows/integration-darwin.yml index 5d9dea38c52..32a46f0f214 100644 --- a/.github/workflows/integration-darwin.yml +++ b/.github/workflows/integration-darwin.yml @@ -12,7 +12,6 @@ jobs: strategy: matrix: kustomize_version: [3.5.4] - bazel_version: [3.2.0] ko_version: [0.4.0] kompose_version: [1.21.0] gcloud_sdk_version: [276.0.0] @@ -35,13 +34,6 @@ jobs: run: | echo ::set-env name=SKAFFOLD_VERSION::"$(git log --format="%H" -n 1)" - - name: Install Bazel - run: | - curl -fLO "https://github.com/bazelbuild/bazel/releases/download/${{ matrix.bazel_version }}/bazel-${{ matrix.bazel_version }}-installer-darwin-x86_64.sh" - chmod +x "bazel-${{ matrix.bazel_version }}-installer-darwin-x86_64.sh" - ./bazel-${{ matrix.bazel_version }}-installer-darwin-x86_64.sh --user - echo ::add-path::${HOME}/bin - - name: Install Kustomize run: | wget -O kustomize.tar.gz https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v${{ matrix.kustomize_version }}/kustomize_v${{ matrix.kustomize_version }}_darwin_amd64.tar.gz diff --git a/.github/workflows/integration-linux.yml b/.github/workflows/integration-linux.yml index 03874b3eaa1..7b40cd9f216 100644 --- a/.github/workflows/integration-linux.yml +++ b/.github/workflows/integration-linux.yml @@ -13,7 +13,6 @@ jobs: matrix: kustomize_version: [3.5.4] ko_version: [0.4.0] - bazel_version: [3.2.0] kompose_version: [1.21.0] gcloud_sdk_version: [276.0.0] container_structure_tests_version: [1.8.0] @@ -34,13 +33,6 @@ jobs: run: | echo ::set-env name=SKAFFOLD_VERSION::"$(git log --format="%H" -n 1)" - - name: Install Bazel - run: | - curl -fLO "https://github.com/bazelbuild/bazel/releases/download/${{ matrix.bazel_version }}/bazel-${{ matrix.bazel_version }}-installer-linux-x86_64.sh" - chmod +x bazel-${{ matrix.bazel_version }}-installer-linux-x86_64.sh - ./bazel-${{ matrix.bazel_version }}-installer-linux-x86_64.sh --user - echo ::add-path::${HOME}/bin - - name: Install Kustomize run: | wget -O kustomize.tar.gz https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v${{ matrix.kustomize_version }}/kustomize_v${{ matrix.kustomize_version }}_linux_amd64.tar.gz diff --git a/integration/examples/bazel/WORKSPACE b/integration/examples/bazel/WORKSPACE index 1c1f8a3ea15..5637093ed3e 100644 --- a/integration/examples/bazel/WORKSPACE +++ b/integration/examples/bazel/WORKSPACE @@ -4,9 +4,9 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "io_bazel_rules_docker", - sha256 = "3efbd23e195727a67f87b2a04fb4388cc7a11a0c0c2cf33eec225fb8ffbb27ea", - strip_prefix = "rules_docker-0.14.2", - urls = ["https://github.com/bazelbuild/rules_docker/releases/download/v0.14.2/rules_docker-v0.14.2.tar.gz"], + sha256 = "4521794f0fba2e20f3bf15846ab5e01d5332e587e9ce81629c7f96c793bb7036", + strip_prefix = "rules_docker-0.14.4", + urls = ["https://github.com/bazelbuild/rules_docker/releases/download/v0.14.4/rules_docker-v0.14.4.tar.gz"], ) http_archive( @@ -32,6 +32,17 @@ load( container_repositories() +load( + "@io_bazel_rules_docker//repositories:deps.bzl", + container_deps = "deps", +) + +container_deps() + +load("@io_bazel_rules_docker//repositories:pip_repositories.bzl", "pip_deps") + +pip_deps() + load( "@io_bazel_rules_docker//go:image.bzl", _go_image_repos = "repositories", diff --git a/integration/util.go b/integration/util.go index 0efc029a881..ba010c141e4 100644 --- a/integration/util.go +++ b/integration/util.go @@ -359,7 +359,7 @@ func WaitForLogs(t *testing.T, out io.Reader, firstMessage string, moreMessages current := 0 message := firstMessage - timer := time.NewTimer(30 * time.Second) + timer := time.NewTimer(90 * time.Second) defer timer.Stop() for { select { From 27100add412e8aa0bfb646ffa9f2f960b270665b Mon Sep 17 00:00:00 2001 From: Brian de Alwis Date: Tue, 18 Aug 2020 11:29:02 -0400 Subject: [PATCH 092/138] Simplify devLoopEvent message text (#4684) --- pkg/skaffold/event/event.go | 6 +++--- pkg/skaffold/event/event_test.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/skaffold/event/event.go b/pkg/skaffold/event/event.go index bea5091569b..511c4a400b1 100644 --- a/pkg/skaffold/event/event.go +++ b/pkg/skaffold/event/event.go @@ -628,11 +628,11 @@ func (ev *eventHandler) handleExec(f firedEvent) { de := e.DevLoopEvent switch de.Status { case InProgress: - logEntry.Entry = fmt.Sprintf("DevInit Iteration %d in progress", de.Iteration) + logEntry.Entry = "Update initiated due to file change" case Succeeded: - logEntry.Entry = fmt.Sprintf("DevInit Iteration %d successful", de.Iteration) + logEntry.Entry = "Update successful" case Failed: - logEntry.Entry = fmt.Sprintf("DevInit Iteration %d failed with error code %v", de.Iteration, de.Err.ErrCode) + logEntry.Entry = fmt.Sprintf("Update failed with error code %v", de.Err.ErrCode) } default: return diff --git a/pkg/skaffold/event/event_test.go b/pkg/skaffold/event/event_test.go index bbf65060303..e877d0203b5 100644 --- a/pkg/skaffold/event/event_test.go +++ b/pkg/skaffold/event/event_test.go @@ -475,7 +475,7 @@ func TestDevLoopFailedInPhase(t *testing.T) { handler.logLock.Lock() logEntry := handler.eventLog[len(handler.eventLog)-1] handler.logLock.Unlock() - return logEntry.Entry == fmt.Sprintf("DevInit Iteration 1 failed with error code %v", proto.StatusCode_BUILD_PUSH_ACCESS_DENIED) + return logEntry.Entry == fmt.Sprintf("Update failed with error code %v", proto.StatusCode_BUILD_PUSH_ACCESS_DENIED) }, }, { @@ -489,7 +489,7 @@ func TestDevLoopFailedInPhase(t *testing.T) { handler.logLock.Lock() logEntry := handler.eventLog[len(handler.eventLog)-1] handler.logLock.Unlock() - return logEntry.Entry == fmt.Sprintf("DevInit Iteration 1 failed with error code %v", proto.StatusCode_DEPLOY_UNKNOWN) + return logEntry.Entry == fmt.Sprintf("Update failed with error code %v", proto.StatusCode_DEPLOY_UNKNOWN) }, }, { @@ -504,7 +504,7 @@ func TestDevLoopFailedInPhase(t *testing.T) { handler.logLock.Lock() logEntry := handler.eventLog[len(handler.eventLog)-1] handler.logLock.Unlock() - return logEntry.Entry == fmt.Sprintf("DevInit Iteration 1 failed with error code %v", proto.StatusCode_STATUSCHECK_UNHEALTHY) + return logEntry.Entry == fmt.Sprintf("Update failed with error code %v", proto.StatusCode_STATUSCHECK_UNHEALTHY) }, }, } From 4425d88b7fb5e8f54e6c135d3fb3dc7c0bbf1a35 Mon Sep 17 00:00:00 2001 From: Brian de Alwis Date: Tue, 18 Aug 2020 14:55:14 -0400 Subject: [PATCH 093/138] Move context validation to build phase so as to not interfere with deploy (#4657) * Revert #4492 * Error in build when encountering an invalid artifact context * gofmt * Add tests * Hide util.Copy; fix up stray local testing bogons * revert to origin/master * gofmt --- integration/deploy_test.go | 100 ++++++++++++++++++ pkg/skaffold/runner/build_deploy.go | 22 ++++ pkg/skaffold/runner/build_deploy_test.go | 55 ++++++++++ pkg/skaffold/schema/samples_test.go | 10 +- pkg/skaffold/schema/validation/validation.go | 21 ---- .../schema/validation/validation_test.go | 66 ------------ 6 files changed, 178 insertions(+), 96 deletions(-) diff --git a/integration/deploy_test.go b/integration/deploy_test.go index 07bce976ce4..c0b4dcbee56 100644 --- a/integration/deploy_test.go +++ b/integration/deploy_test.go @@ -17,11 +17,17 @@ limitations under the License. package integration import ( + "errors" + "io" + "os" + "path/filepath" "strings" "testing" "github.com/GoogleContainerTools/skaffold/cmd/skaffold/app/flags" "github.com/GoogleContainerTools/skaffold/integration/skaffold" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/walk" "github.com/GoogleContainerTools/skaffold/testutil" ) @@ -105,3 +111,97 @@ func TestDeployWithInCorrectConfig(t *testing.T) { t.Errorf("failed without saying the reason: %s", output) } } + +// Verify that we can deploy without artifact details (https://github.com/GoogleContainerTools/skaffold/issues/4616) +func TestDeployWithoutWorkspaces(t *testing.T) { + MarkIntegrationTest(t, NeedsGcp) + + ns, _ := SetupNamespace(t) + + outputBytes := skaffold.Build("--quiet").InDir("examples/nodejs").InNs(ns.Name).RunOrFailOutput(t) + // Parse the Build Output + buildArtifacts, err := flags.ParseBuildOutput(outputBytes) + failNowIfError(t, err) + if len(buildArtifacts.Builds) != 1 { + t.Fatalf("expected 1 artifact to be built, but found %d", len(buildArtifacts.Builds)) + } + + tmpDir := testutil.NewTempDir(t) + buildOutputFile := tmpDir.Path("build.out") + tmpDir.Write("build.out", string(outputBytes)) + copyFiles(tmpDir.Root(), "examples/nodejs/skaffold.yaml") + copyFiles(tmpDir.Root(), "examples/nodejs/k8s") + + // Run Deploy using the build output + // See https://github.com/GoogleContainerTools/skaffold/issues/2372 on why status-check=false + skaffold.Deploy("--build-artifacts", buildOutputFile, "--status-check=false").InDir(tmpDir.Root()).InNs(ns.Name).RunOrFail(t) +} + +// Copies a file or directory tree. There are 2x3 cases: +// 1. If _src_ is a file, +// 1. and _dst_ exists and is a file then _src_ is copied into _dst_ +// 2. and _dst_ exists and is a directory, then _src_ is copied as _dst/$(basename src)_ +// 3. and _dst_ does not exist, then _src_ is copied as _dst_. +// 2. If _src_ is a directory, +// 1. and _dst_ exists and is a file, then return an error +// 2. and _dst_ exists and is a directory, then src is copied as _dst/$(basename src)_ +// 3. and _dst_ does not exist, then src is copied as _dst/src[1:]_. +func copyFiles(dst, src string) error { + if util.IsFile(src) { + switch { + case util.IsFile(dst): // copy _src_ to _dst_ + case util.IsDir(dst): // copy _src_ to _dst/src[-1] + dst = filepath.Join(dst, filepath.Base(src)) + default: // copy _src_ to _dst_ + if err := os.MkdirAll(filepath.Dir(dst), os.ModePerm); err != nil { + return err + } + } + in, err := os.Open(src) + if err != nil { + return err + } + out, err := os.Create(dst) + if err != nil { + return err + } + _, err = io.Copy(out, in) + return err + } else if !util.IsDir(src) { + return errors.New("src does not exist") + } + // so src is a directory + if util.IsFile(dst) { + return errors.New("cannot copy directory into file") + } + srcPrefix := src + if util.IsDir(dst) { // src is copied to _dst/$(basename src) + srcPrefix = filepath.Dir(src) + } else if err := os.MkdirAll(filepath.Dir(dst), os.ModePerm); err != nil { + return err + } + return walk.From(src).Unsorted().WhenIsFile().Do(func(path string, _ walk.Dirent) error { + rel, err := filepath.Rel(srcPrefix, path) + if err != nil { + return err + } + in, err := os.Open(path) + if err != nil { + return err + } + defer in.Close() + + destFile := filepath.Join(dst, rel) + if err := os.MkdirAll(filepath.Dir(destFile), os.ModePerm); err != nil { + return err + } + + out, err := os.Create(destFile) + if err != nil { + return err + } + + _, err = io.Copy(out, in) + return err + }) +} diff --git a/pkg/skaffold/runner/build_deploy.go b/pkg/skaffold/runner/build_deploy.go index 03a9d35bea3..cad5dd672c2 100644 --- a/pkg/skaffold/runner/build_deploy.go +++ b/pkg/skaffold/runner/build_deploy.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "io" + "os" "time" "github.com/sirupsen/logrus" @@ -38,6 +39,10 @@ func (r *SkaffoldRunner) BuildAndTest(ctx context.Context, out io.Writer, artifa return []build.Artifact{}, nil } + if err := checkWorkspaces(artifacts); err != nil { + return nil, err + } + tags, err := r.imageTags(ctx, out, artifacts) if err != nil { return nil, err @@ -201,3 +206,20 @@ func (r *SkaffoldRunner) imageTags(ctx context.Context, out io.Writer, artifacts logrus.Infoln("Tags generated in", time.Since(start)) return imageTags, nil } + +func checkWorkspaces(artifacts []*latest.Artifact) error { + for _, a := range artifacts { + if a.Workspace != "" { + if info, err := os.Stat(a.Workspace); err != nil { + // err could be permission-related + if os.IsNotExist(err) { + return fmt.Errorf("image %q context %q does not exist", a.ImageName, a.Workspace) + } + return fmt.Errorf("image %q context %q: %w", a.ImageName, a.Workspace, err) + } else if !info.IsDir() { + return fmt.Errorf("image %q context %q is not a directory", a.ImageName, a.Workspace) + } + } + } + return nil +} diff --git a/pkg/skaffold/runner/build_deploy_test.go b/pkg/skaffold/runner/build_deploy_test.go index 63c0524c7c0..215bbd8ccfc 100644 --- a/pkg/skaffold/runner/build_deploy_test.go +++ b/pkg/skaffold/runner/build_deploy_test.go @@ -128,3 +128,58 @@ func TestBuildAndTestSkipBuild(t *testing.T) { t.CheckDeepEqual([]Actions{{}}, testBench.Actions()) }) } + +func TestCheckWorkspaces(t *testing.T) { + tmpDir := testutil.NewTempDir(t).Touch("file") + tmpFile := tmpDir.Path("file") + + tests := []struct { + description string + artifacts []*latest.Artifact + shouldErr bool + }{ + { + description: "no workspace", + artifacts: []*latest.Artifact{ + { + ImageName: "image", + }, + }, + }, + { + description: "directory that exists", + artifacts: []*latest.Artifact{ + { + ImageName: "image", + Workspace: tmpDir.Root(), + }, + }, + }, + { + description: "error on non-existent location", + artifacts: []*latest.Artifact{ + { + ImageName: "image", + Workspace: "doesnotexist", + }, + }, + shouldErr: true, + }, + { + description: "error on file", + artifacts: []*latest.Artifact{ + { + ImageName: "image", + Workspace: tmpFile, + }, + }, + shouldErr: true, + }, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + err := checkWorkspaces(test.artifacts) + t.CheckError(test.shouldErr, err) + }) + } +} diff --git a/pkg/skaffold/schema/samples_test.go b/pkg/skaffold/schema/samples_test.go index cd2b0ec7314..f050718da5b 100644 --- a/pkg/skaffold/schema/samples_test.go +++ b/pkg/skaffold/schema/samples_test.go @@ -74,15 +74,7 @@ func TestParseSamples(t *testing.T) { } func checkSkaffoldConfig(t *testutil.T, yaml []byte) { - root := t.NewTempDir() - configFile := root.Path("skaffold.yaml") - root.Write("skaffold.yaml", string(yaml)) - // create workspace directories referenced in these samples - for _, d := range []string{"app", "node", "python", "leeroy-web", "leeroy-app", "backend", "base-service", "world-service"} { - root.Mkdir(d) - } - root.Chdir() - + configFile := t.TempFile("skaffold.yaml", yaml) cfg, err := ParseConfigAndUpgrade(configFile, latest.Version) t.CheckNoError(err) diff --git a/pkg/skaffold/schema/validation/validation.go b/pkg/skaffold/schema/validation/validation.go index e0c84479f11..6c3c1a6448c 100644 --- a/pkg/skaffold/schema/validation/validation.go +++ b/pkg/skaffold/schema/validation/validation.go @@ -18,7 +18,6 @@ package validation import ( "fmt" - "os" "reflect" "strings" @@ -37,7 +36,6 @@ var ( // Process checks if the Skaffold pipeline is valid and returns all encountered errors as a concatenated string func Process(config *latest.SkaffoldConfig) error { errs := visitStructs(config, validateYamltags) - errs = append(errs, validateWorkspaces(config.Build.Artifacts)...) errs = append(errs, validateImageNames(config.Build.Artifacts)...) errs = append(errs, validateDockerNetworkMode(config.Build.Artifacts)...) errs = append(errs, validateCustomDependencies(config.Build.Artifacts)...) @@ -58,25 +56,6 @@ func Process(config *latest.SkaffoldConfig) error { return fmt.Errorf(strings.Join(messages, " | ")) } -// validateWorkspaces makes sure the artifact workspaces are valid directories. -func validateWorkspaces(artifacts []*latest.Artifact) (errs []error) { - for _, a := range artifacts { - if a.Workspace != "" { - if info, err := os.Stat(a.Workspace); err != nil { - // err could be permission-related - if os.IsNotExist(err) { - errs = append(errs, fmt.Errorf("image %q context %q does not exist", a.ImageName, a.Workspace)) - } else { - errs = append(errs, fmt.Errorf("image %q context %q: %w", a.ImageName, a.Workspace, err)) - } - } else if !info.IsDir() { - errs = append(errs, fmt.Errorf("image %q context %q is not a directory", a.ImageName, a.Workspace)) - } - } - } - return -} - // validateImageNames makes sure the artifact image names are valid base names, // without tags nor digests. func validateImageNames(artifacts []*latest.Artifact) (errs []error) { diff --git a/pkg/skaffold/schema/validation/validation_test.go b/pkg/skaffold/schema/validation/validation_test.go index e9af5ff37a4..12cb2380f0b 100644 --- a/pkg/skaffold/schema/validation/validation_test.go +++ b/pkg/skaffold/schema/validation/validation_test.go @@ -717,72 +717,6 @@ func TestValidateJibPluginType(t *testing.T) { } } -func TestValidateWorkspaces(t *testing.T) { - tmpDir := testutil.NewTempDir(t).Touch("file") - tmpFile := tmpDir.Path("file") - - tests := []struct { - description string - artifacts []*latest.Artifact - shouldErr bool - }{ - { - description: "no workspace", - artifacts: []*latest.Artifact{ - { - ImageName: "image", - }, - }, - }, - { - description: "directory that exists", - artifacts: []*latest.Artifact{ - { - ImageName: "image", - Workspace: tmpDir.Root(), - }, - }, - }, - { - description: "error on non-existent location", - artifacts: []*latest.Artifact{ - { - ImageName: "image", - Workspace: "doesnotexist", - }, - }, - shouldErr: true, - }, - { - description: "error on file", - artifacts: []*latest.Artifact{ - { - ImageName: "image", - Workspace: tmpFile, - }, - }, - shouldErr: true, - }, - } - for _, test := range tests { - testutil.Run(t, test.description, func(t *testutil.T) { - // disable yamltags validation - t.Override(&validateYamltags, func(interface{}) error { return nil }) - - err := Process( - &latest.SkaffoldConfig{ - Pipeline: latest.Pipeline{ - Build: latest.BuildConfig{ - Artifacts: test.artifacts, - }, - }, - }) - - t.CheckError(test.shouldErr, err) - }) - } -} - func TestValidateLogsConfig(t *testing.T) { tests := []struct { prefix string From d0f665e586421e848b83d3e800a4efa766625dc9 Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Tue, 18 Aug 2020 22:30:43 +0000 Subject: [PATCH 094/138] kpt deployer applyDir implementation and tests (#4691) --- docs/content/en/schemas/v2beta7.json | 6 ++ pkg/skaffold/deploy/kpt.go | 66 ++++++++++++++- pkg/skaffold/deploy/kpt_test.go | 122 +++++++++++++++++++++++++++ pkg/skaffold/schema/latest/config.go | 3 + 4 files changed, 196 insertions(+), 1 deletion(-) diff --git a/docs/content/en/schemas/v2beta7.json b/docs/content/en/schemas/v2beta7.json index 4f6da448f73..34e462b7fa2 100755 --- a/docs/content/en/schemas/v2beta7.json +++ b/docs/content/en/schemas/v2beta7.json @@ -1601,6 +1601,11 @@ }, "KptDeploy": { "properties": { + "applyDir": { + "type": "string", + "description": "path to the directory to deploy to the cluster.", + "x-intellij-html-description": "path to the directory to deploy to the cluster." + }, "dir": { "type": "string", "description": "path to the directory to run kpt functions against.", @@ -1619,6 +1624,7 @@ }, "preferredOrder": [ "dir", + "applyDir", "fn", "live" ], diff --git a/pkg/skaffold/deploy/kpt.go b/pkg/skaffold/deploy/kpt.go index 87c633a589f..3fa3c27f335 100644 --- a/pkg/skaffold/deploy/kpt.go +++ b/pkg/skaffold/deploy/kpt.go @@ -18,18 +18,28 @@ package deploy import ( "context" + "fmt" "io" + "os" + "os/exec" + "path/filepath" + "strings" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" ) // KptDeployer deploys workflows with kpt CLI type KptDeployer struct { + *latest.KptDeploy } func NewKptDeployer(runCtx *runcontext.RunContext, labels map[string]string) *KptDeployer { - return &KptDeployer{} + return &KptDeployer{ + KptDeploy: runCtx.Pipeline().Deploy.KptDeploy, + } } func (k *KptDeployer) Deploy(ctx context.Context, out io.Writer, builds []build.Artifact) ([]string, error) { @@ -47,3 +57,57 @@ func (k *KptDeployer) Cleanup(ctx context.Context, out io.Writer) error { func (k *KptDeployer) Render(ctx context.Context, out io.Writer, builds []build.Artifact, offline bool, filepath string) error { return nil } + +// getApplyDir returns the path to applyDir if specified by the user. Otherwise, getApplyDir +// creates a hidden directory in place of applyDir. +func (k *KptDeployer) getApplyDir(ctx context.Context) (string, error) { + if k.ApplyDir != "" { + if _, err := os.Stat(k.ApplyDir); os.IsNotExist(err) { + return "", err + } + return k.ApplyDir, nil + } + + applyDir := ".kpt-hydrated" + + // 0755 is a permission setting where the owner can read, write, and execute. + // Others can read and execute but not modify the file. + if err := os.MkdirAll(applyDir, 0755); err != nil { + return "", fmt.Errorf("applyDir was unspecified. creating applyDir: %w", err) + } + + if _, err := os.Stat(filepath.Join(applyDir, "inventory-template.yaml")); os.IsNotExist(err) { + cmd := exec.CommandContext(ctx, "kpt", kptCommandArgs(applyDir, []string{"live", "init"}, nil, nil)...) + if err := util.RunCmd(cmd); err != nil { + return "", err + } + } + + return applyDir, nil +} + +// kptCommandArgs returns a list of additional arguments for the kpt command. +func kptCommandArgs(dir string, commands, flags, globalFlags []string) []string { + var args []string + + for _, v := range commands { + parts := strings.Split(v, " ") + args = append(args, parts...) + } + + if len(dir) > 0 { + args = append(args, dir) + } + + for _, v := range flags { + parts := strings.Split(v, " ") + args = append(args, parts...) + } + + for _, v := range globalFlags { + parts := strings.Split(v, " ") + args = append(args, parts...) + } + + return args +} diff --git a/pkg/skaffold/deploy/kpt_test.go b/pkg/skaffold/deploy/kpt_test.go index 2e30177920a..fe278cd6df9 100644 --- a/pkg/skaffold/deploy/kpt_test.go +++ b/pkg/skaffold/deploy/kpt_test.go @@ -18,9 +18,13 @@ package deploy import ( "context" + "os" + "strings" "testing" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/testutil" ) @@ -95,3 +99,121 @@ func TestKpt_Render(t *testing.T) { }) } } + +func TestKpt_GetApplyDir(t *testing.T) { + tests := []struct { + description string + applyDir string + expected string + commands util.Command + shouldErr bool + }{ + { + description: "specified an invalid applyDir", + applyDir: "invalid_path", + shouldErr: true, + }, + { + description: "specified a valid applyDir", + applyDir: "valid_path", + expected: "valid_path", + }, + { + description: "unspecified applyDir", + expected: ".kpt-hydrated", + commands: testutil.CmdRun("kpt live init .kpt-hydrated"), + }, + { + description: "existing template resource in .kpt-hydrated", + expected: ".kpt-hydrated", + }, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + t.Override(&util.DefaultExecCommand, test.commands) + tmpDir := t.NewTempDir().Chdir() + + if test.applyDir == test.expected { + os.Mkdir(test.applyDir, 0755) + } + + if test.description == "existing template resource in .kpt-hydrated" { + tmpDir.Touch(".kpt-hydrated/inventory-template.yaml") + } + + k := NewKptDeployer(&runcontext.RunContext{ + WorkingDir: ".", + Cfg: latest.Pipeline{ + Deploy: latest.DeployConfig{ + DeployType: latest.DeployType{ + KptDeploy: &latest.KptDeploy{ + ApplyDir: test.applyDir, + }, + }, + }, + }, + }, nil) + + applyDir, err := k.getApplyDir(context.Background()) + + t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expected, applyDir) + }) + } +} + +func TestKpt_KptCommandArgs(t *testing.T) { + tests := []struct { + description string + dir string + commands []string + flags []string + globalFlags []string + expected []string + }{ + { + description: "empty", + }, + { + description: "all inputs have len >0", + dir: "test", + commands: []string{"live", "apply"}, + flags: []string{"--fn-path", "kpt-func.yaml"}, + globalFlags: []string{"-h"}, + expected: strings.Split("live apply test --fn-path kpt-func.yaml -h", " "), + }, + { + description: "empty dir", + commands: []string{"live", "apply"}, + flags: []string{"--fn-path", "kpt-func.yaml"}, + globalFlags: []string{"-h"}, + expected: strings.Split("live apply --fn-path kpt-func.yaml -h", " "), + }, + { + description: "empty commands", + dir: "test", + flags: []string{"--fn-path", "kpt-func.yaml"}, + globalFlags: []string{"-h"}, + expected: strings.Split("test --fn-path kpt-func.yaml -h", " "), + }, + { + description: "empty flags", + dir: "test", + commands: []string{"live", "apply"}, + globalFlags: []string{"-h"}, + expected: strings.Split("live apply test -h", " "), + }, + { + description: "empty globalFlags", + dir: "test", + commands: []string{"live", "apply"}, + flags: []string{"--fn-path", "kpt-func.yaml"}, + expected: strings.Split("live apply test --fn-path kpt-func.yaml", " "), + }, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + res := kptCommandArgs(test.dir, test.commands, test.flags, test.globalFlags) + t.CheckDeepEqual(test.expected, res) + }) + } +} diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 2a04cb326e9..3a541eb270f 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -531,6 +531,9 @@ type KptDeploy struct { // Dir is the path to the directory to run kpt functions against. Dir string `yaml:"dir,omitempty"` + // ApplyDir is the path to the directory to deploy to the cluster. + ApplyDir string `yaml:"applyDir,omitempty"` + // Fn adds additional configurations for `kpt fn`. Fn KptFn `yaml:"fn,omitempty"` From 62049df3a6a3521f5982b9a9409dabb62cf0d64f Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Tue, 18 Aug 2020 22:50:57 +0000 Subject: [PATCH 095/138] small changes to kpt deployer docs and ordering (#4694) --- docs/content/en/schemas/v2beta7.json | 10 +++++----- pkg/skaffold/runner/new.go | 8 ++++---- pkg/skaffold/schema/latest/config.go | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/content/en/schemas/v2beta7.json b/docs/content/en/schemas/v2beta7.json index 34e462b7fa2..ef1aabff5da 100755 --- a/docs/content/en/schemas/v2beta7.json +++ b/docs/content/en/schemas/v2beta7.json @@ -864,9 +864,9 @@ }, "preferredOrder": [ "helm", + "kpt", "kubectl", "kustomize", - "kpt", "statusCheckDeadlineSeconds", "kubeContext", "logs" @@ -1636,13 +1636,13 @@ "properties": { "fnPath": { "type": "string", - "description": "path to a kpt function or pipeline.", - "x-intellij-html-description": "path to a kpt function or pipeline." + "description": "a directory to read functions from instead of the configuration directory.", + "x-intellij-html-description": "a directory to read functions from instead of the configuration directory." }, "image": { "type": "string", - "description": "specifies an image to be run as a function.", - "x-intellij-html-description": "specifies an image to be run as a function." + "description": "an image to be run as a function in lieu of running functions from a directory.", + "x-intellij-html-description": "an image to be run as a function in lieu of running functions from a directory." } }, "preferredOrder": [ diff --git a/pkg/skaffold/runner/new.go b/pkg/skaffold/runner/new.go index 6f9c887996c..640b8910d1b 100644 --- a/pkg/skaffold/runner/new.go +++ b/pkg/skaffold/runner/new.go @@ -196,6 +196,10 @@ func getDeployer(runCtx *runcontext.RunContext, labels map[string]string) deploy deployers = append(deployers, deploy.NewHelmDeployer(runCtx, labels)) } + if d.KptDeploy != nil { + deployers = append(deployers, deploy.NewKptDeployer(runCtx, labels)) + } + if d.KubectlDeploy != nil { deployers = append(deployers, deploy.NewKubectlDeployer(runCtx, labels)) } @@ -204,10 +208,6 @@ func getDeployer(runCtx *runcontext.RunContext, labels map[string]string) deploy deployers = append(deployers, deploy.NewKustomizeDeployer(runCtx, labels)) } - if d.KptDeploy != nil { - deployers = append(deployers, deploy.NewKptDeployer(runCtx, labels)) - } - // avoid muxing overhead when only a single deployer is configured if len(deployers) == 1 { return deployers[0] diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 3a541eb270f..20a14fd5723 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -447,15 +447,15 @@ type DeployType struct { // HelmDeploy *beta* uses the `helm` CLI to apply the charts to the cluster. HelmDeploy *HelmDeploy `yaml:"helm,omitempty"` + // KptDeploy *beta* uses the `kpt` CLI to manage and deploy manifests. + KptDeploy *KptDeploy `yaml:"kpt,omitempty"` + // 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"` // KustomizeDeploy *beta* uses the `kustomize` CLI to "patch" a deployment for a target environment. KustomizeDeploy *KustomizeDeploy `yaml:"kustomize,omitempty"` - - // KptDeploy *beta* uses the `kpt` CLI to manage and deploy manifests. - KptDeploy *KptDeploy `yaml:"kpt,omitempty"` } // KubectlDeploy *beta* uses a client side `kubectl apply` to deploy manifests. @@ -543,10 +543,10 @@ type KptDeploy struct { // KptFn adds additional configurations used when calling `kpt fn`. type KptFn struct { - // FnPath is the path to a kpt function or pipeline. + // FnPath is a directory to read functions from instead of the configuration directory. FnPath string `yaml:"fnPath,omitempty"` - // Image specifies an image to be run as a function. + // Image is an image to be run as a function in lieu of running functions from a directory. Image string `yaml:"image,omitempty"` } From 6e98174df7c31d38ad61c5411c0e062ebebd1af1 Mon Sep 17 00:00:00 2001 From: Tyler Schroeder Date: Wed, 19 Aug 2020 07:37:50 -0700 Subject: [PATCH 096/138] fix WSL path check to be case insensitive (#4688) --- pkg/skaffold/docker/client.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/skaffold/docker/client.go b/pkg/skaffold/docker/client.go index b6a8d8f997b..71294c92c6f 100644 --- a/pkg/skaffold/docker/client.go +++ b/pkg/skaffold/docker/client.go @@ -17,7 +17,6 @@ limitations under the License. package docker import ( - "bytes" "context" "errors" "fmt" @@ -171,8 +170,8 @@ func detectWsl() (bool, error) { if err != nil { return false, fmt.Errorf("read /proc/version: %w", err) } - - if bytes.Contains(b, []byte("Microsoft")) { + str := strings.ToLower(string(b)) + if strings.Contains(str, "microsoft") { return true, nil } } From 035c89fa1ea0a6de19da714ed5ea68ec6e32ee5c Mon Sep 17 00:00:00 2001 From: Tejal Desai Date: Wed, 19 Aug 2020 09:40:12 -0700 Subject: [PATCH 097/138] Remove remote rules (#4698) Remove remote rules --- pkg/diag/download/http.go | 36 -------- pkg/diag/download/http_test.go | 29 ------ pkg/diag/recommender/custom.go | 100 --------------------- pkg/diag/recommender/custom_test.go | 134 ---------------------------- pkg/diag/validator/pod.go | 5 +- pkg/skaffold/deploy/status_check.go | 10 +-- 6 files changed, 2 insertions(+), 312 deletions(-) delete mode 100644 pkg/diag/download/http.go delete mode 100644 pkg/diag/download/http_test.go delete mode 100644 pkg/diag/recommender/custom.go delete mode 100644 pkg/diag/recommender/custom_test.go diff --git a/pkg/diag/download/http.go b/pkg/diag/download/http.go deleted file mode 100644 index b2ebbe8c905..00000000000 --- a/pkg/diag/download/http.go +++ /dev/null @@ -1,36 +0,0 @@ -/* -Copyright 2020 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 download - -import ( - "fmt" - "io/ioutil" - "net/http" -) - -// HTTPDownload reads the file at given url and return the read bytes. -func HTTPDownload(url string) ([]byte, error) { - resp, err := http.Get(url) - if err != nil { - return nil, fmt.Errorf("downloading the http url %w", err) - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("http %d, error %q", resp.StatusCode, resp.Status) - } - return ioutil.ReadAll(resp.Body) -} diff --git a/pkg/diag/download/http_test.go b/pkg/diag/download/http_test.go deleted file mode 100644 index a8139730829..00000000000 --- a/pkg/diag/download/http_test.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2020 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 download - -import ( - "testing" - - "github.com/GoogleContainerTools/skaffold/testutil" -) - -func TestHTTPDownload(t *testing.T) { - v, err := HTTPDownload("https://storage.googleapis.com/skaffold/releases/v1.0.0/VERSION") - testutil.CheckError(t, false, err) - testutil.CheckDeepEqual(t, "v1.0.0\n", string(v)) -} diff --git a/pkg/diag/recommender/custom.go b/pkg/diag/recommender/custom.go deleted file mode 100644 index cba4c8181b0..00000000000 --- a/pkg/diag/recommender/custom.go +++ /dev/null @@ -1,100 +0,0 @@ -/* -Copyright 2020 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 recommender - -import ( - "encoding/json" - "strings" - - "github.com/GoogleContainerTools/skaffold/pkg/diag/download" - "github.com/GoogleContainerTools/skaffold/proto" -) - -var ( - // testing - downloadRules = download.HTTPDownload -) - -const ( - DiagDefaultRules = "https://storage.googleapis.com/skaffold/diag/customRules/v1/rules.json" -) - -type Custom struct { - RulesPath string - rules []Rule - deployContext map[string]string -} - -// NewCustom returns a custom recommender from rules file or return error -func NewCustom(path string, deployContext map[string]string) (Custom, error) { - rules, err := loadRules(path) - if err != nil { - return Custom{}, err - } - return Custom{ - RulesPath: path, - rules: rules, - deployContext: deployContext, - }, nil -} - -func (r Custom) Make(errCode proto.StatusCode) proto.Suggestion { - for _, rule := range r.rules { - if rule.Matches(errCode, r.deployContext) { - return proto.Suggestion{ - SuggestionCode: rule.SuggestionCode, - Action: rule.Suggestion, - } - } - } - return NilSuggestion -} - -func loadRules(path string) ([]Rule, error) { - var ruleConfig RulesConfigV1 - bytes, err := downloadRules(path) - if err != nil { - return nil, err - } - err = json.Unmarshal(bytes, &ruleConfig) - return ruleConfig.Rules, err -} - -type RulesConfigV1 struct { - Rules []Rule `json:"rules"` -} - -type Rule struct { - ErrCode proto.StatusCode `json:"errorCode"` - SuggestionCode proto.SuggestionCode `json:"suggestionCode"` - Suggestion string `json:"suggestion"` - ContextPrefixMatches map[string]string `json:"contextPrefixMatches"` -} - -func (r Rule) Matches(errCode proto.StatusCode, deployContext map[string]string) bool { - if r.ErrCode != errCode { - return false - } - for k, prefix := range r.ContextPrefixMatches { - if value, ok := deployContext[k]; ok { - if !strings.HasPrefix(value, prefix) { - return false - } - } - } - return true -} diff --git a/pkg/diag/recommender/custom_test.go b/pkg/diag/recommender/custom_test.go deleted file mode 100644 index a44269a5767..00000000000 --- a/pkg/diag/recommender/custom_test.go +++ /dev/null @@ -1,134 +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 recommender - -import ( - "testing" - - "github.com/GoogleContainerTools/skaffold/proto" - "github.com/GoogleContainerTools/skaffold/testutil" -) - -func TestCustomRecos(t *testing.T) { - tests := []struct { - description string - deployContext map[string]string - rulesJSON []byte - errCode proto.StatusCode - expected proto.Suggestion - }{ - { - description: "test simple rule without any context prefixes", - rulesJSON: []byte(`{ -"rules": [{ - "errorCode": 401, - "suggestionCode": 401, - "suggestion": "Try deleting stale pods", - "contextPrefixMatches": {} -}] -}`), - errCode: proto.StatusCode_STATUSCHECK_NODE_DISK_PRESSURE, - expected: proto.Suggestion{ - SuggestionCode: proto.SuggestionCode_ADDRESS_NODE_DISK_PRESSURE, - Action: "Try deleting stale pods", - }, - }, - { - description: "test simple rule with a context prefixes", - rulesJSON: []byte(`{ - "rules": [{ - "errorCode": 401, - "suggestionCode": 401, - "suggestion": "Try running minikube status", - "contextPrefixMatches": { - "clusterName": "minikube" - } - }, - { - "errorCode": 401, - "suggestionCode": 401, - "suggestion": "Try running gcloud", - "contextPrefixMatches": { - "clusterName": "gke_" - } - } - ] -}`), - errCode: proto.StatusCode_STATUSCHECK_NODE_DISK_PRESSURE, - deployContext: map[string]string{ - "clusterName": "gke_test", - }, - expected: proto.Suggestion{ - SuggestionCode: proto.SuggestionCode_ADDRESS_NODE_DISK_PRESSURE, - Action: "Try running gcloud", - }, - }, - } - - for _, test := range tests { - testutil.Run(t, test.description, func(t *testutil.T) { - mDownload := func(_ string) ([]byte, error) { - return test.rulesJSON, nil - } - t.Override(&downloadRules, mDownload) - - r, err := NewCustom("testHtpp.json", test.deployContext) - t.CheckNoError(err) - t.CheckDeepEqual(test.expected, r.Make(test.errCode)) - }) - } -} - -func TestCustomRecosInt(t *testing.T) { - tests := []struct { - description string - deployContext map[string]string - errCode proto.StatusCode - expected proto.Suggestion - }{ - { - description: "test simple rule without any context prefixes", - errCode: proto.StatusCode_STATUSCHECK_NODE_DISK_PRESSURE, - deployContext: map[string]string{ - "clusterName": "minikube", - }, - expected: proto.Suggestion{ - SuggestionCode: proto.SuggestionCode_ADDRESS_NODE_DISK_PRESSURE, - Action: "Try running minikube status", - }, - }, - { - description: "test simple rule with a context prefixes", - errCode: proto.StatusCode_STATUSCHECK_NODE_DISK_PRESSURE, - deployContext: map[string]string{ - "clusterName": "gke_test", - }, - expected: proto.Suggestion{ - SuggestionCode: proto.SuggestionCode_ADDRESS_NODE_DISK_PRESSURE, - Action: "Try running gcloud", - }, - }, - } - - for _, test := range tests { - testutil.Run(t, test.description, func(t *testutil.T) { - r, err := NewCustom(DiagDefaultRules, test.deployContext) - t.CheckNoError(err) - t.CheckDeepEqual(test.expected, r.Make(test.errCode)) - }) - } -} diff --git a/pkg/diag/validator/pod.go b/pkg/diag/validator/pod.go index 5e49e69452b..ed73b3570ff 100644 --- a/pkg/diag/validator/pod.go +++ b/pkg/diag/validator/pod.go @@ -74,11 +74,8 @@ type PodValidator struct { } // NewPodValidator initializes a PodValidator -func NewPodValidator(k kubernetes.Interface, deployContext map[string]string) *PodValidator { +func NewPodValidator(k kubernetes.Interface) *PodValidator { rs := []Recommender{recommender.ContainerError{}} - if r, err := recommender.NewCustom(recommender.DiagDefaultRules, deployContext); err == nil { - rs = append(rs, r) - } return &PodValidator{k: k, recos: rs} } diff --git a/pkg/skaffold/deploy/status_check.go b/pkg/skaffold/deploy/status_check.go index 166efccd3a0..dd7c95bbf3b 100644 --- a/pkg/skaffold/deploy/status_check.go +++ b/pkg/skaffold/deploy/status_check.go @@ -53,10 +53,6 @@ const ( kubernetesMaxDeadline = 600 ) -var ( - deployContext map[string]string -) - type counter struct { total int pending int32 @@ -76,10 +72,6 @@ func statusCheck(ctx context.Context, defaultLabeller *DefaultLabeller, runCtx * return proto.StatusCode_STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR, fmt.Errorf("getting Kubernetes client: %w", err) } - deployContext = map[string]string{ - "clusterName": runCtx.GetKubeContext(), - } - deployments := make([]*resource.Deployment, 0) for _, n := range runCtx.GetNamespaces() { newDeployments, err := getDeployments(client, n, defaultLabeller, @@ -135,7 +127,7 @@ func getDeployments(client kubernetes.Interface, ns string, l *DefaultLabeller, } pd := diag.New([]string{d.Namespace}). WithLabel(RunIDLabel, l.Labels()[RunIDLabel]). - WithValidators([]validator.Validator{validator.NewPodValidator(client, deployContext)}) + WithValidators([]validator.Validator{validator.NewPodValidator(client)}) for k, v := range d.Spec.Template.Labels { pd = pd.WithLabel(k, v) From 079662de2826b19cb57e223b10e72317f0118b24 Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Wed, 19 Aug 2020 16:43:29 +0000 Subject: [PATCH 098/138] Kpt Deployer Dependencies() implementation and tests (#4696) * dependencies implementation and tests * clarifying comments --- pkg/skaffold/deploy/kpt.go | 50 +++++++++++++- pkg/skaffold/deploy/kpt_test.go | 116 ++++++++++++++++++++++++++++++-- 2 files changed, 160 insertions(+), 6 deletions(-) diff --git a/pkg/skaffold/deploy/kpt.go b/pkg/skaffold/deploy/kpt.go index 3fa3c27f335..e8a8acb44ce 100644 --- a/pkg/skaffold/deploy/kpt.go +++ b/pkg/skaffold/deploy/kpt.go @@ -23,6 +23,7 @@ import ( "os" "os/exec" "path/filepath" + "regexp" "strings" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" @@ -47,7 +48,19 @@ func (k *KptDeployer) Deploy(ctx context.Context, out io.Writer, builds []build. } func (k *KptDeployer) Dependencies() ([]string, error) { - return nil, nil + deps := newStringSet() + if len(k.Fn.FnPath) > 0 { + deps.insert(k.Fn.FnPath) + } + + configDeps, err := getResources(k.Dir) + if err != nil { + return nil, fmt.Errorf("finding dependencies in %s: %w", k.Dir, err) + } + + deps.insert(configDeps...) + + return deps.toList(), nil } func (k *KptDeployer) Cleanup(ctx context.Context, out io.Writer) error { @@ -111,3 +124,38 @@ func kptCommandArgs(dir string, commands, flags, globalFlags []string) []string return args } + +// getResources returns a list of all file names in `root` that end in .yaml or .yml +// and all local kustomization dependencies under root. +func getResources(root string) ([]string, error) { + var files []string + + if _, err := os.Stat(root); os.IsNotExist(err) { + return nil, err + } + + err := filepath.Walk(root, func(path string, info os.FileInfo, _ error) error { + // Using regex match is not entirely accurate in deciding whether something is a resource or not. + // Kpt should provide better functionality for determining whether files are resources. + isResource, err := regexp.MatchString(`\.ya?ml$`, filepath.Base(path)) + if err != nil { + return fmt.Errorf("matching %s with regex: %w", filepath.Base(path), err) + } + + if info.IsDir() { + depsForKustomization, err := dependenciesForKustomization(path) + if err != nil { + return err + } + + files = append(files, depsForKustomization...) + } else if isResource { + // Windows uses `\` instead of `/` for paths. Replacing these will improve consistency for testing. + files = append(files, strings.ReplaceAll(path, "\\", "/")) + } + + return nil + }) + + return files, err +} diff --git a/pkg/skaffold/deploy/kpt_test.go b/pkg/skaffold/deploy/kpt_test.go index fe278cd6df9..2e78c4490b6 100644 --- a/pkg/skaffold/deploy/kpt_test.go +++ b/pkg/skaffold/deploy/kpt_test.go @@ -49,18 +49,124 @@ func TestKpt_Deploy(t *testing.T) { func TestKpt_Dependencies(t *testing.T) { tests := []struct { - description string - expected []string - shouldErr bool + description string + cfg *latest.KptDeploy + createFiles map[string]string + kustomizations map[string]string + expected []string + shouldErr bool }{ { - description: "nil", + description: "bad dir", + cfg: &latest.KptDeploy{ + Dir: "invalid_path", + }, + shouldErr: true, + }, + { + description: "empty dir and unspecified fnPath", + cfg: &latest.KptDeploy{ + Dir: ".", + }, + }, + { + description: "dir", + cfg: &latest.KptDeploy{ + Dir: ".", + }, + createFiles: map[string]string{ + "foo.yaml": "", + "README.md": "", + }, + expected: []string{"foo.yaml"}, + }, + { + description: "dir with subdirs and file path variants", + cfg: &latest.KptDeploy{ + Dir: ".", + }, + createFiles: map[string]string{ + "food.yml": "", + "foo/bar.yaml": "", + "foo/bat//bad.yml": "", + "foo/bat\\README.md": "", + }, + expected: []string{"foo/bar.yaml", "foo/bat/bad.yml", "food.yml"}, + }, + { + description: "fnpath", + cfg: &latest.KptDeploy{ + Dir: ".", + Fn: latest.KptFn{FnPath: "kpt-func.yaml"}, + }, + expected: []string{"kpt-func.yaml"}, + }, + { + description: "fnpath and dir and kustomization", + cfg: &latest.KptDeploy{ + Dir: ".", + Fn: latest.KptFn{FnPath: "kpt-func.yaml"}, + }, + createFiles: map[string]string{"foo.yml": ""}, + kustomizations: map[string]string{"kustomization.yaml": `configMapGenerator: +- files: [app1.properties]`}, + expected: []string{"app1.properties", "foo.yml", "kpt-func.yaml", "kustomization.yaml"}, + }, + { + description: "dependencies that can only be detected as a kustomization", + cfg: &latest.KptDeploy{ + Dir: ".", + }, + kustomizations: map[string]string{"kustomization.yaml": `configMapGenerator: +- files: [app1.properties]`}, + expected: []string{"app1.properties", "kustomization.yaml"}, + }, + { + description: "kustomization.yml variant", + cfg: &latest.KptDeploy{ + Dir: ".", + }, + kustomizations: map[string]string{"kustomization.yml": `configMapGenerator: +- files: [app1.properties]`}, + expected: []string{"app1.properties", "kustomization.yml"}, + }, + { + description: "Kustomization variant", + cfg: &latest.KptDeploy{ + Dir: ".", + }, + kustomizations: map[string]string{"Kustomization": `configMapGenerator: +- files: [app1.properties]`}, + expected: []string{"Kustomization", "app1.properties"}, + }, + { + description: "incorrectly named kustomization", + cfg: &latest.KptDeploy{ + Dir: ".", + }, + kustomizations: map[string]string{"customization": `configMapGenerator: +- files: [app1.properties]`}, }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - k := NewKptDeployer(&runcontext.RunContext{}, nil) + tmpDir := t.NewTempDir().Chdir() + + tmpDir.WriteFiles(test.createFiles) + tmpDir.WriteFiles(test.kustomizations) + + k := NewKptDeployer(&runcontext.RunContext{ + Cfg: latest.Pipeline{ + Deploy: latest.DeployConfig{ + DeployType: latest.DeployType{ + KptDeploy: test.cfg, + }, + }, + }, + }, nil) + res, err := k.Dependencies() + t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expected, res) }) } From 71a0f4e2342c0e76937fd4baf790148dd79aa534 Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Wed, 19 Aug 2020 17:38:56 +0000 Subject: [PATCH 099/138] Kpt Deployer Cleanup() implementation and tests (#4697) * cleanup implementation and tests * docstring and interpret stdout as err for kpt Co-authored-by: Felix --- pkg/skaffold/deploy/kpt.go | 15 ++++++++- pkg/skaffold/deploy/kpt_test.go | 54 +++++++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/pkg/skaffold/deploy/kpt.go b/pkg/skaffold/deploy/kpt.go index e8a8acb44ce..74daa90e11b 100644 --- a/pkg/skaffold/deploy/kpt.go +++ b/pkg/skaffold/deploy/kpt.go @@ -63,7 +63,20 @@ func (k *KptDeployer) Dependencies() ([]string, error) { return deps.toList(), nil } -func (k *KptDeployer) Cleanup(ctx context.Context, out io.Writer) error { +// Cleanup deletes what was deployed by calling `kpt live destroy`. +func (k *KptDeployer) Cleanup(ctx context.Context, _ io.Writer) error { + applyDir, err := k.getApplyDir(ctx) + if err != nil { + return fmt.Errorf("getting applyDir: %w", err) + } + + cmd := exec.CommandContext(ctx, "kpt", kptCommandArgs(applyDir, []string{"live", "destroy"}, nil, nil)...) + out, err := util.RunCmdOut(cmd) + if err != nil { + // Kpt errors are written in STDOUT and surrounded by `\n`. + return fmt.Errorf("kpt live destroy: %s", strings.Trim(string(out), "\n")) + } + return nil } diff --git a/pkg/skaffold/deploy/kpt_test.go b/pkg/skaffold/deploy/kpt_test.go index 2e78c4490b6..7fe55667d52 100644 --- a/pkg/skaffold/deploy/kpt_test.go +++ b/pkg/skaffold/deploy/kpt_test.go @@ -18,10 +18,13 @@ package deploy import ( "context" + "errors" + "io/ioutil" "os" "strings" "testing" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" @@ -175,16 +178,61 @@ func TestKpt_Dependencies(t *testing.T) { func TestKpt_Cleanup(t *testing.T) { tests := []struct { description string + applyDir string + commands util.Command shouldErr bool }{ { - description: "nil", + description: "invalid user specified applyDir", + applyDir: "invalid_path", + shouldErr: true, + }, + { + description: "valid user specified applyDir w/o template resource", + applyDir: "valid_path", + commands: testutil.CmdRunOutErr("kpt live destroy valid_path", "", errors.New("BUG")), + shouldErr: true, + }, + { + description: "valid user specified applyDir w/ template resource (emulated)", + applyDir: "valid_path", + commands: testutil.CmdRunOut("kpt live destroy valid_path", ""), + }, + { + description: "unspecified applyDir", + commands: testutil. + CmdRun("kpt live init .kpt-hydrated"). + AndRunOut("kpt live destroy .kpt-hydrated", ""), }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - k := NewKptDeployer(&runcontext.RunContext{}, nil) - err := k.Cleanup(context.Background(), nil) + t.Override(&util.DefaultExecCommand, test.commands) + t.NewTempDir().Chdir() + + if test.applyDir == "valid_path" { + os.Mkdir(test.applyDir, 0755) + } + + k := NewKptDeployer(&runcontext.RunContext{ + WorkingDir: ".", + Cfg: latest.Pipeline{ + Deploy: latest.DeployConfig{ + DeployType: latest.DeployType{ + KptDeploy: &latest.KptDeploy{ + ApplyDir: test.applyDir, + }, + }, + }, + }, + KubeContext: testKubeContext, + Opts: config.SkaffoldOptions{ + Namespace: testNamespace, + }, + }, nil) + + err := k.Cleanup(context.Background(), ioutil.Discard) + t.CheckError(test.shouldErr, err) }) } From 28d305c44798f177d792c9e959aee292b248a68f Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Wed, 19 Aug 2020 21:53:03 +0000 Subject: [PATCH 100/138] update kpt deployer Dependencies() tests (#4703) --- pkg/skaffold/deploy/kpt.go | 3 +-- pkg/skaffold/deploy/kpt_test.go | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/skaffold/deploy/kpt.go b/pkg/skaffold/deploy/kpt.go index 74daa90e11b..f5e635c931e 100644 --- a/pkg/skaffold/deploy/kpt.go +++ b/pkg/skaffold/deploy/kpt.go @@ -163,8 +163,7 @@ func getResources(root string) ([]string, error) { files = append(files, depsForKustomization...) } else if isResource { - // Windows uses `\` instead of `/` for paths. Replacing these will improve consistency for testing. - files = append(files, strings.ReplaceAll(path, "\\", "/")) + files = append(files, path) } return nil diff --git a/pkg/skaffold/deploy/kpt_test.go b/pkg/skaffold/deploy/kpt_test.go index 7fe55667d52..2d2e7b70a29 100644 --- a/pkg/skaffold/deploy/kpt_test.go +++ b/pkg/skaffold/deploy/kpt_test.go @@ -170,7 +170,7 @@ func TestKpt_Dependencies(t *testing.T) { res, err := k.Dependencies() - t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expected, res) + t.CheckErrorAndDeepEqual(test.shouldErr, err, tmpDir.Paths(test.expected...), tmpDir.Paths(res...)) }) } } From 47ff2607d1877e653e07764efd1ad8c4bce6c735 Mon Sep 17 00:00:00 2001 From: Brian de Alwis Date: Thu, 20 Aug 2020 13:56:41 -0400 Subject: [PATCH 101/138] Suppress clutter from docker-credential-gcloud error messages (#4705) * Suppress clutter from docker-credential-gcloud error messages * Use `critical` --- pkg/skaffold/docker/image.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/pkg/skaffold/docker/image.go b/pkg/skaffold/docker/image.go index 86a6f64e828..c400d37f94b 100644 --- a/pkg/skaffold/docker/image.go +++ b/pkg/skaffold/docker/image.go @@ -21,6 +21,7 @@ import ( "encoding/json" "fmt" "io" + "os" "sort" "strings" "sync" @@ -312,11 +313,25 @@ func (l *localDaemon) isAlreadyPushed(ctx context.Context, ref, registryAuth str // Pull pulls an image reference from a registry. func (l *localDaemon) Pull(ctx context.Context, out io.Writer, ref string) error { + // We first try pulling the image with credentials. If that fails then retry + // without credentials in case the image is public. + + // Set CLOUDSDK_CORE_VERBOSITY to suppress error messages emitted by docker-credential-gcloud + // when the user is not authenticated or lacks credentials to pull the given image. The errors + // are irrelevant when the image is public (e.g., `gcr.io/buildpacks/builder:v1`).` + // If the image is private, the error from GCR directs the user to the GCR authentication + // page which provides steps to rememdy the situation. + if v, found := os.LookupEnv("CLOUDSDK_CORE_VERBOSITY"); found { + defer os.Setenv("CLOUDSDK_CORE_VERBOSITY", v) + } else { + defer os.Unsetenv("CLOUDSDK_CORE_VERBOSITY") + } + os.Setenv("CLOUDSDK_CORE_VERBOSITY", "critical") + // Eargerly create credentials. registryAuth, err := l.encodedRegistryAuth(ctx, DefaultAuthHelper, ref) // Let's ignore the error because maybe the image is public // and can be pulled without credentials. - rc, err := l.apiClient.ImagePull(ctx, ref, types.ImagePullOptions{ RegistryAuth: registryAuth, PrivilegeFunc: func() (string, error) { From 708519b3c5f2e1dce2b106dfea1871211cbc3e84 Mon Sep 17 00:00:00 2001 From: Nick Kubala Date: Thu, 20 Aug 2020 13:37:02 -0700 Subject: [PATCH 102/138] v1.13.2 Release (#4707) --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c7b7972f71..9de5400248d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,31 @@ +# v1.13.2 Release - 08/20/2020 + +**Linux** +`curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v1.13.2/skaffold-linux-amd64 && chmod +x skaffold && sudo mv skaffold /usr/local/bin` + +**macOS** +`curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v1.13.2/skaffold-darwin-amd64 && chmod +x skaffold && sudo mv skaffold /usr/local/bin` + +**Windows** + https://storage.googleapis.com/skaffold/releases/v1.13.2/skaffold-windows-amd64.exe + +**Docker image** +`gcr.io/k8s-skaffold/skaffold:v1.13.2` + +**This point release contains several usability fixes that should improve user experience, especially when running Skaffold through Cloud Code.** + +Highlights: +* Suppress clutter from docker-credential-gcloud error messages [#4705](https://github.com/GoogleContainerTools/skaffold/pull/4705) +* Remove remote rules [#4698](https://github.com/GoogleContainerTools/skaffold/pull/4698) +* Simplify devLoopEvent message text [#4684](https://github.com/GoogleContainerTools/skaffold/pull/4684) +* Return deployment status code when status check can't retrieve pods from cluster [#4683](https://github.com/GoogleContainerTools/skaffold/pull/4683) +* Improved error message when skaffold config not found [#4679](https://github.com/GoogleContainerTools/skaffold/pull/4679) +* Move all update checks to single function; enforce honoring updateCheck flag [#4677](https://github.com/GoogleContainerTools/skaffold/pull/4677) +* Enable watch trigger only when either one of autoBuild, autoSync or autoDeploy is active [#4676](https://github.com/GoogleContainerTools/skaffold/pull/4676) +* Move context validation to build phase so as to not interfere with deploy [#4657](https://github.com/GoogleContainerTools/skaffold/pull/4657) +* Send update-check message to stderr [#4655](https://github.com/GoogleContainerTools/skaffold/pull/4655) +* Make event handling sequential and set the correct timestamp [#4644](https://github.com/GoogleContainerTools/skaffold/pull/4644) + # v1.13.1 Release - 08/04/2020 **Linux** From 43023aa505394cae4718d9c499833fa48f82d066 Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Fri, 21 Aug 2020 16:54:46 +0000 Subject: [PATCH 103/138] Kpt Deployer Render() implementation and tests (#4708) * render implementation and tests w/o kustomization * error handling Co-authored-by: Felix --- pkg/skaffold/deploy/kpt.go | 108 ++++++++++++-- pkg/skaffold/deploy/kpt_test.go | 255 +++++++++++++++++++++++++++++++- 2 files changed, 346 insertions(+), 17 deletions(-) diff --git a/pkg/skaffold/deploy/kpt.go b/pkg/skaffold/deploy/kpt.go index f5e635c931e..af5ad55e0fa 100644 --- a/pkg/skaffold/deploy/kpt.go +++ b/pkg/skaffold/deploy/kpt.go @@ -18,6 +18,7 @@ package deploy import ( "context" + "errors" "fmt" "io" "os" @@ -27,19 +28,33 @@ import ( "strings" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" + deploy "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" ) +var ( + kptHydrated = ".kpt-hydrated" + inventoryTemplate = "inventory-template.yaml" +) + // KptDeployer deploys workflows with kpt CLI type KptDeployer struct { *latest.KptDeploy + + insecureRegistries map[string]bool + labels map[string]string + globalConfig string } func NewKptDeployer(runCtx *runcontext.RunContext, labels map[string]string) *KptDeployer { return &KptDeployer{ - KptDeploy: runCtx.Pipeline().Deploy.KptDeploy, + KptDeploy: runCtx.Pipeline().Deploy.KptDeploy, + insecureRegistries: runCtx.GetInsecureRegistries(), + labels: labels, + globalConfig: runCtx.GlobalConfig(), } } @@ -47,6 +62,8 @@ func (k *KptDeployer) Deploy(ctx context.Context, out io.Writer, builds []build. return nil, nil } +// Dependencies returns a list of files that the deployer depends on. This does NOT include applyDir. +// In dev mode, a redeploy will be triggered if one of these files is updated. func (k *KptDeployer) Dependencies() ([]string, error) { deps := newStringSet() if len(k.Fn.FnPath) > 0 { @@ -80,12 +97,83 @@ func (k *KptDeployer) Cleanup(ctx context.Context, _ io.Writer) error { return nil } -func (k *KptDeployer) Render(ctx context.Context, out io.Writer, builds []build.Artifact, offline bool, filepath string) error { - return nil +func (k *KptDeployer) Render(ctx context.Context, out io.Writer, builds []build.Artifact, _ bool, filepath string) error { + manifests, err := k.renderManifests(ctx, out, builds) + if err != nil { + return err + } + + return outputRenderedManifests(manifests.String(), filepath, out) +} + +func (k *KptDeployer) renderManifests(ctx context.Context, _ io.Writer, builds []build.Artifact) (deploy.ManifestList, error) { + debugHelpersRegistry, err := config.GetDebugHelpersRegistry(k.globalConfig) + if err != nil { + return nil, fmt.Errorf("retrieving debug helpers registry: %w", err) + } + + manifests, err := k.kptFnRun(ctx) + if err != nil { + return nil, fmt.Errorf("running kpt functions: %w", err) + } + + if len(manifests) == 0 { + return nil, nil + } + + manifests, err = manifests.ReplaceImages(builds) + if err != nil { + return nil, fmt.Errorf("replacing images in manifests: %w", err) + } + + for _, transform := range manifestTransforms { + manifests, err = transform(manifests, builds, Registries{k.insecureRegistries, debugHelpersRegistry}) + if err != nil { + return nil, fmt.Errorf("unable to transform manifests: %w", err) + } + } + + return manifests.SetLabels(k.labels) +} + +// kptFnRun does a dry run with the specified kpt functions (fn-path XOR image) against dir. +// If neither fn-path nor image are specified, functions will attempt to be discovered in dir. +// An error is returned when both fn-path and image are specified. +func (k *KptDeployer) kptFnRun(ctx context.Context) (deploy.ManifestList, error) { + var manifests deploy.ManifestList + + // --dry-run sets the pipeline's output to STDOUT, otherwise output is set to sinkDir. + // For now, k.Dir will be treated as sinkDir (and sourceDir). + flags := []string{"--dry-run"} + specifiedFnPath := false + + if len(k.Fn.FnPath) > 0 { + flags = append(flags, "--fn-path", k.Fn.FnPath) + specifiedFnPath = true + } + if len(k.Fn.Image) > 0 { + if specifiedFnPath { + return nil, errors.New("cannot specify both fn-path and image") + } + + flags = append(flags, "--image", k.Fn.Image) + } + + cmd := exec.CommandContext(ctx, "kpt", kptCommandArgs(k.Dir, []string{"fn", "run"}, flags, nil)...) + out, err := util.RunCmdOut(cmd) + if err != nil { + return nil, err + } + + if len(out) > 0 { + manifests.Append(out) + } + + return manifests, nil } // getApplyDir returns the path to applyDir if specified by the user. Otherwise, getApplyDir -// creates a hidden directory in place of applyDir. +// creates a hidden directory named .kpt-hydrated in place of applyDir. func (k *KptDeployer) getApplyDir(ctx context.Context) (string, error) { if k.ApplyDir != "" { if _, err := os.Stat(k.ApplyDir); os.IsNotExist(err) { @@ -94,22 +182,20 @@ func (k *KptDeployer) getApplyDir(ctx context.Context) (string, error) { return k.ApplyDir, nil } - applyDir := ".kpt-hydrated" - // 0755 is a permission setting where the owner can read, write, and execute. // Others can read and execute but not modify the file. - if err := os.MkdirAll(applyDir, 0755); err != nil { + if err := os.MkdirAll(kptHydrated, 0755); err != nil { return "", fmt.Errorf("applyDir was unspecified. creating applyDir: %w", err) } - if _, err := os.Stat(filepath.Join(applyDir, "inventory-template.yaml")); os.IsNotExist(err) { - cmd := exec.CommandContext(ctx, "kpt", kptCommandArgs(applyDir, []string{"live", "init"}, nil, nil)...) - if err := util.RunCmd(cmd); err != nil { + if _, err := os.Stat(filepath.Join(kptHydrated, inventoryTemplate)); os.IsNotExist(err) { + cmd := exec.CommandContext(ctx, "kpt", kptCommandArgs(kptHydrated, []string{"live", "init"}, nil, nil)...) + if _, err := util.RunCmdOut(cmd); err != nil { return "", err } } - return applyDir, nil + return kptHydrated, nil } // kptCommandArgs returns a list of additional arguments for the kpt command. diff --git a/pkg/skaffold/deploy/kpt_test.go b/pkg/skaffold/deploy/kpt_test.go index 2d2e7b70a29..a634a230c34 100644 --- a/pkg/skaffold/deploy/kpt_test.go +++ b/pkg/skaffold/deploy/kpt_test.go @@ -17,6 +17,7 @@ limitations under the License. package deploy import ( + "bytes" "context" "errors" "io/ioutil" @@ -24,6 +25,7 @@ import ( "strings" "testing" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" @@ -201,7 +203,7 @@ func TestKpt_Cleanup(t *testing.T) { { description: "unspecified applyDir", commands: testutil. - CmdRun("kpt live init .kpt-hydrated"). + CmdRunOut("kpt live init .kpt-hydrated", ""). AndRunOut("kpt live destroy .kpt-hydrated", ""), }, } @@ -239,17 +241,258 @@ func TestKpt_Cleanup(t *testing.T) { } func TestKpt_Render(t *testing.T) { + output1 := `apiVersion: v1 +kind: Pod +metadata: + namespace: default +spec: + containers: + - image: gcr.io/project/image1 + name: image1 +` + + output2 := `apiVersion: v1 +kind: Pod +metadata: + namespace: default +spec: + containers: + - image: gcr.io/project/image2 + name: image2 +` + + output3 := `apiVersion: v1 +kind: Pod +metadata: + namespace: default +spec: + containers: + - image: gcr.io/project/image1 + name: image1 + - image: gcr.io/project/image2 + name: image2 +` + + output4 := `apiVersion: v1 +kind: Pod +metadata: + namespace: default +spec: + containers: + - image: gcr.io/project/image1 + name: image1 +--- +apiVersion: v1 +kind: Pod +metadata: + namespace: default +spec: + containers: + - image: gcr.io/project/image2 + name: image2 +` + tests := []struct { description string + builds []build.Artifact + labels map[string]string + cfg *latest.KptDeploy + commands util.Command + expected string shouldErr bool }{ - {}, + { + description: "no fnPath or image specified", + builds: []build.Artifact{ + { + ImageName: "gcr.io/project/image1", + Tag: "gcr.io/project/image1:tag1", + }, + }, + cfg: &latest.KptDeploy{ + Dir: ".", + }, + commands: testutil.CmdRunOut("kpt fn run . --dry-run", output1), + expected: `apiVersion: v1 +kind: Pod +metadata: + namespace: default +spec: + containers: + - image: gcr.io/project/image1:tag1 + name: image1 +`, + }, + { + description: "fnPath specified", + builds: []build.Artifact{ + { + ImageName: "gcr.io/project/image2", + Tag: "gcr.io/project/image2:tag2", + }, + }, + cfg: &latest.KptDeploy{ + Dir: "test", + Fn: latest.KptFn{FnPath: "kpt-func.yaml"}, + }, + commands: testutil.CmdRunOut("kpt fn run test --dry-run --fn-path kpt-func.yaml", output2), + expected: `apiVersion: v1 +kind: Pod +metadata: + namespace: default +spec: + containers: + - image: gcr.io/project/image2:tag2 + name: image2 +`, + }, + { + description: "image specified", + builds: []build.Artifact{ + { + ImageName: "gcr.io/project/image1", + Tag: "gcr.io/project/image1:tag1", + }, + { + ImageName: "gcr.io/project/image2", + Tag: "gcr.io/project/image2:tag2", + }, + }, + cfg: &latest.KptDeploy{ + Dir: "test", + Fn: latest.KptFn{Image: "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar"}, + }, + commands: testutil.CmdRunOut("kpt fn run test --dry-run --image gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", output3), + expected: `apiVersion: v1 +kind: Pod +metadata: + namespace: default +spec: + containers: + - image: gcr.io/project/image1:tag1 + name: image1 + - image: gcr.io/project/image2:tag2 + name: image2 +`, + }, + { + description: "multiple resources outputted", + builds: []build.Artifact{ + { + ImageName: "gcr.io/project/image1", + Tag: "gcr.io/project/image1:tag1", + }, + { + ImageName: "gcr.io/project/image2", + Tag: "gcr.io/project/image2:tag2", + }, + }, + cfg: &latest.KptDeploy{ + Dir: "test", + Fn: latest.KptFn{FnPath: "kpt-func.yaml"}, + }, + commands: testutil.CmdRunOut("kpt fn run test --dry-run --fn-path kpt-func.yaml", output4), + expected: `apiVersion: v1 +kind: Pod +metadata: + namespace: default +spec: + containers: + - image: gcr.io/project/image1:tag1 + name: image1 +--- +apiVersion: v1 +kind: Pod +metadata: + namespace: default +spec: + containers: + - image: gcr.io/project/image2:tag2 + name: image2 +`, + }, + { + description: "user labels", + builds: []build.Artifact{ + { + ImageName: "gcr.io/project/image1", + Tag: "gcr.io/project/image1:tag1", + }, + }, + labels: map[string]string{"user/label": "test"}, + cfg: &latest.KptDeploy{ + Dir: ".", + }, + commands: testutil.CmdRunOut("kpt fn run . --dry-run", output1), + expected: `apiVersion: v1 +kind: Pod +metadata: + labels: + user/label: test + namespace: default +spec: + containers: + - image: gcr.io/project/image1:tag1 + name: image1 +`, + }, + { + description: "empty output from pipeline", + builds: []build.Artifact{ + { + ImageName: "gcr.io/project/image1", + Tag: "gcr.io/project/image1:tag1", + }, + }, + labels: map[string]string{"user/label": "test"}, + cfg: &latest.KptDeploy{ + Dir: ".", + }, + commands: testutil.CmdRunOut("kpt fn run . --dry-run", ``), + expected: "\n", + }, + { + description: "kpt fn run fails", + cfg: &latest.KptDeploy{ + Dir: ".", + }, + commands: testutil.CmdRunOutErr("kpt fn run . --dry-run", "invalid pipeline", errors.New("BUG")), + shouldErr: true, + }, + { + description: "both fnPath and image specified", + cfg: &latest.KptDeploy{ + Dir: "test", + Fn: latest.KptFn{ + FnPath: "kpt-func.yaml", + Image: "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar"}, + }, + shouldErr: true, + }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - k := NewKptDeployer(&runcontext.RunContext{}, nil) - err := k.Render(context.Background(), nil, nil, false, "") - t.CheckError(test.shouldErr, err) + t.Override(&util.DefaultExecCommand, test.commands) + + k := NewKptDeployer(&runcontext.RunContext{ + WorkingDir: ".", + Cfg: latest.Pipeline{ + Deploy: latest.DeployConfig{ + DeployType: latest.DeployType{ + KptDeploy: test.cfg, + }, + }, + }, + KubeContext: testKubeContext, + Opts: config.SkaffoldOptions{ + Namespace: testNamespace, + }, + }, test.labels) + + var b bytes.Buffer + err := k.Render(context.Background(), &b, test.builds, true, "") + + t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expected, b.String()) }) } } @@ -275,7 +518,7 @@ func TestKpt_GetApplyDir(t *testing.T) { { description: "unspecified applyDir", expected: ".kpt-hydrated", - commands: testutil.CmdRun("kpt live init .kpt-hydrated"), + commands: testutil.CmdRunOut("kpt live init .kpt-hydrated", ""), }, { description: "existing template resource in .kpt-hydrated", From 46529503e1e8e754644895ccd06bd6aea8cbc128 Mon Sep 17 00:00:00 2001 From: Gaurav <39389231+gsquared94@users.noreply.github.com> Date: Sun, 23 Aug 2020 18:38:56 +0530 Subject: [PATCH 104/138] Build debuggable containers (#4606) * Add default build arguments for docker and buildpacks builder to optimize images for dev vs debug. * simplify method * Fix cache computation * fix linters * Filter out unused default docker build args. Introduce SKAFFOLD_RUN_MODE arg. * Modify debug test * missing new line * PR feedback * Simplify Dockerfile --- integration/testdata/debug/go/Dockerfile | 5 +- integration/testdata/debug/skaffold.yaml | 5 - pkg/skaffold/build/buildpacks/build_test.go | 69 +++- pkg/skaffold/build/buildpacks/default_args.go | 43 +++ pkg/skaffold/build/buildpacks/env.go | 59 ++++ pkg/skaffold/build/buildpacks/lifecycle.go | 15 +- pkg/skaffold/build/buildpacks/types.go | 11 +- pkg/skaffold/build/cache/cache.go | 2 +- pkg/skaffold/build/cache/hash.go | 81 ++--- pkg/skaffold/build/cache/hash_test.go | 300 ++++++++++-------- pkg/skaffold/build/cache/retrieve_test.go | 11 +- pkg/skaffold/build/cluster/pod.go | 4 +- pkg/skaffold/build/gcb/docker.go | 8 +- pkg/skaffold/build/gcb/kaniko.go | 4 +- pkg/skaffold/build/local/docker.go | 13 +- pkg/skaffold/build/local/docker_test.go | 8 +- pkg/skaffold/build/local/local.go | 4 +- pkg/skaffold/build/local/local_test.go | 4 +- pkg/skaffold/build/local/types.go | 12 +- pkg/skaffold/config/options.go | 26 +- pkg/skaffold/docker/build_args.go | 75 +++++ pkg/skaffold/docker/build_args_test.go | 192 +++++++++++ pkg/skaffold/docker/image.go | 45 +-- pkg/skaffold/docker/image_test.go | 19 +- pkg/skaffold/docker/parse.go | 22 +- pkg/skaffold/docker/parse_test.go | 77 +++++ pkg/skaffold/runner/debugging.go | 3 +- pkg/skaffold/runner/runcontext/context.go | 3 +- pkg/skaffold/util/env_template.go | 24 ++ pkg/skaffold/util/util.go | 25 ++ pkg/skaffold/util/util_test.go | 75 +++++ 31 files changed, 943 insertions(+), 301 deletions(-) create mode 100644 pkg/skaffold/build/buildpacks/default_args.go create mode 100644 pkg/skaffold/build/buildpacks/env.go create mode 100644 pkg/skaffold/docker/build_args.go create mode 100644 pkg/skaffold/docker/build_args_test.go diff --git a/integration/testdata/debug/go/Dockerfile b/integration/testdata/debug/go/Dockerfile index d1a4d643a1a..2f5a2bedfa8 100644 --- a/integration/testdata/debug/go/Dockerfile +++ b/integration/testdata/debug/go/Dockerfile @@ -1,9 +1,8 @@ FROM golang:1.12 as builder COPY app.go . -# Must use eval to handle GOGCFLAGS with spaces like `-gcflags='all=-N -l'` -ARG GOGCFLAGS -RUN eval go build "${GOGCFLAGS}" -o /app . +ARG SKAFFOLD_GO_GCFLAGS +RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app . FROM gcr.io/distroless/base # `skaffold debug` uses GOTRACEBACK as an indicator of the Go runtime diff --git a/integration/testdata/debug/skaffold.yaml b/integration/testdata/debug/skaffold.yaml index f4637d6f9a2..3ad84918c42 100644 --- a/integration/testdata/debug/skaffold.yaml +++ b/integration/testdata/debug/skaffold.yaml @@ -15,9 +15,6 @@ build: context: python3 - image: skaffold-debug-go context: go - docker: - buildArgs: - GOGCFLAGS: "-gcflags='all=-N -l'" deploy: kubectl: @@ -57,5 +54,3 @@ profiles: context: go buildpacks: builder: "gcr.io/buildpacks/builder:v1" - env: - - GOOGLE_GCFLAGS="-gcflags='all=-N -l'" diff --git a/pkg/skaffold/build/buildpacks/build_test.go b/pkg/skaffold/build/buildpacks/build_test.go index 704c50d0282..cab6b4e7f90 100644 --- a/pkg/skaffold/build/buildpacks/build_test.go +++ b/pkg/skaffold/build/buildpacks/build_test.go @@ -24,6 +24,7 @@ import ( "github.com/buildpacks/pack" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/testutil" @@ -46,35 +47,69 @@ func TestBuild(t *testing.T) { api *testutil.FakeAPIClient files map[string]string pushImages bool - devMode bool shouldErr bool + mode config.RunMode expectedOptions *pack.BuildOptions }{ { - description: "success", + description: "success for debug", artifact: buildpacksArtifact("my/builder", "my/run"), tag: "img:tag", + mode: config.RunModes.Debug, api: &testutil.FakeAPIClient{}, expectedOptions: &pack.BuildOptions{ AppPath: ".", Builder: "my/builder", RunImage: "my/run", - Env: map[string]string{}, + Env: debugModeArgs, Image: "img:latest", }, }, { - description: "success with buildpacks", + description: "success for build", + artifact: buildpacksArtifact("my/builder", "my/run"), + tag: "img:tag", + mode: config.RunModes.Build, + api: &testutil.FakeAPIClient{}, + expectedOptions: &pack.BuildOptions{ + AppPath: ".", + Builder: "my/builder", + RunImage: "my/run", + NoPull: true, + Env: nonDebugModeArgs, + Image: "img:latest", + }, + }, + { + description: "success with buildpacks for debug", artifact: withTrustedBuilder(withBuildpacks([]string{"my/buildpack", "my/otherBuildpack"}, buildpacksArtifact("my/otherBuilder", "my/otherRun"))), tag: "img:tag", api: &testutil.FakeAPIClient{}, + mode: config.RunModes.Debug, expectedOptions: &pack.BuildOptions{ AppPath: ".", Builder: "my/otherBuilder", RunImage: "my/otherRun", Buildpacks: []string{"my/buildpack", "my/otherBuildpack"}, TrustBuilder: true, - Env: map[string]string{}, + Env: debugModeArgs, + Image: "img:latest", + }, + }, + { + description: "success with buildpacks for build", + artifact: withTrustedBuilder(withBuildpacks([]string{"my/buildpack", "my/otherBuildpack"}, buildpacksArtifact("my/otherBuilder", "my/otherRun"))), + tag: "img:tag", + api: &testutil.FakeAPIClient{}, + mode: config.RunModes.Build, + expectedOptions: &pack.BuildOptions{ + AppPath: ".", + Builder: "my/otherBuilder", + RunImage: "my/otherRun", + Buildpacks: []string{"my/buildpack", "my/otherBuildpack"}, + TrustBuilder: true, + NoPull: true, + Env: nonDebugModeArgs, Image: "img:latest", }, }, @@ -83,6 +118,7 @@ func TestBuild(t *testing.T) { artifact: buildpacksArtifact("my/builder2", "my/run2"), tag: "img:tag", api: &testutil.FakeAPIClient{}, + mode: config.RunModes.Build, files: map[string]string{ "project.toml": `[[build.env]] name = "GOOGLE_RUNTIME_VERSION" @@ -99,9 +135,9 @@ version = "1.0" Builder: "my/builder2", RunImage: "my/run2", Buildpacks: []string{"my/buildpack", "my/otherBuildpack@1.0"}, - Env: map[string]string{ + Env: addDefaultArgs(config.RunModes.Build, map[string]string{ "GOOGLE_RUNTIME_VERSION": "14.3.0", - }, + }), Image: "img:latest", }, }, @@ -120,7 +156,7 @@ id = "my/ignored" Builder: "my/builder3", RunImage: "my/run3", Buildpacks: []string{"my/buildpack", "my/otherBuildpack"}, - Env: map[string]string{}, + Env: nonDebugModeArgs, Image: "img:latest", }, }, @@ -128,6 +164,7 @@ id = "my/ignored" description: "Combine env from skaffold.yaml and project.toml", artifact: withEnv([]string{"KEY1=VALUE1"}, buildpacksArtifact("my/builder4", "my/run4")), tag: "img:tag", + mode: config.RunModes.Build, api: &testutil.FakeAPIClient{}, files: map[string]string{ "project.toml": `[[build.env]] @@ -139,10 +176,10 @@ value = "VALUE2" AppPath: ".", Builder: "my/builder4", RunImage: "my/run4", - Env: map[string]string{ + Env: addDefaultArgs(config.RunModes.Build, map[string]string{ "KEY1": "VALUE1", "KEY2": "VALUE2", - }, + }), Image: "img:latest", }, }, @@ -151,14 +188,14 @@ value = "VALUE2" artifact: withSync(&latest.Sync{Auto: &latest.Auto{}}, buildpacksArtifact("another/builder", "another/run")), tag: "img:tag", api: &testutil.FakeAPIClient{}, - devMode: true, + mode: config.RunModes.Dev, expectedOptions: &pack.BuildOptions{ AppPath: ".", Builder: "another/builder", RunImage: "another/run", - Env: map[string]string{ + Env: addDefaultArgs(config.RunModes.Build, map[string]string{ "GOOGLE_DEVMODE": "1", - }, + }), Image: "img:latest", }, }, @@ -167,12 +204,12 @@ value = "VALUE2" artifact: buildpacksArtifact("my/other-builder", "my/run"), tag: "img:tag", api: &testutil.FakeAPIClient{}, - devMode: true, + mode: config.RunModes.Dev, expectedOptions: &pack.BuildOptions{ AppPath: ".", Builder: "my/other-builder", RunImage: "my/run", - Env: map[string]string{}, + Env: nonDebugModeArgs, Image: "img:latest", }, }, @@ -223,7 +260,7 @@ value = "VALUE2" Add("img:latest", "builtImageID") localDocker := docker.NewLocalDaemon(test.api, nil, false, nil) - builder := NewArtifactBuilder(localDocker, test.pushImages, test.devMode) + builder := NewArtifactBuilder(localDocker, test.pushImages, test.mode) _, err := builder.Build(context.Background(), ioutil.Discard, test.artifact, test.tag) t.CheckError(test.shouldErr, err) diff --git a/pkg/skaffold/build/buildpacks/default_args.go b/pkg/skaffold/build/buildpacks/default_args.go new file mode 100644 index 00000000000..5d4a2fdcda5 --- /dev/null +++ b/pkg/skaffold/build/buildpacks/default_args.go @@ -0,0 +1,43 @@ +/* +Copyright 2020 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 buildpacks + +import "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" + +var debugModeArgs = map[string]string{ + "GOOGLE_GOGCFLAGS": "'all=-N -l'", // disable build optimization for Golang + // TODO: Add for other languages +} + +var nonDebugModeArgs = map[string]string{} + +func addDefaultArgs(mode config.RunMode, existing map[string]string) map[string]string { + var args map[string]string + switch mode { + case config.RunModes.Debug: + args = debugModeArgs + default: + args = nonDebugModeArgs + } + + for k, v := range args { + if _, found := existing[k]; !found { + existing[k] = v + } + } + return existing +} diff --git a/pkg/skaffold/build/buildpacks/env.go b/pkg/skaffold/build/buildpacks/env.go new file mode 100644 index 00000000000..433b35aab59 --- /dev/null +++ b/pkg/skaffold/build/buildpacks/env.go @@ -0,0 +1,59 @@ +/* +Copyright 2020 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 buildpacks + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/buildpacks/pack/project" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/misc" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" +) + +func GetEnv(a *latest.Artifact, mode config.RunMode) (map[string]string, error) { + artifact := a.BuildpackArtifact + workspace := a.Workspace + + path := filepath.Join(workspace, artifact.ProjectDescriptor) + projectDescriptor, err := project.ReadProjectDescriptor(path) + if err != nil && !os.IsNotExist(err) { + return nil, fmt.Errorf("failed to read project descriptor %q: %w", path, err) + } + return env(a, mode, projectDescriptor) +} + +func env(a *latest.Artifact, mode config.RunMode, projectDescriptor project.Descriptor) (map[string]string, error) { + envVars, err := misc.EvaluateEnv(a.BuildpackArtifact.Env) + if err != nil { + return nil, fmt.Errorf("unable to evaluate env variables: %w", err) + } + + if mode == config.RunModes.Dev && a.Sync != nil && a.Sync.Auto != nil { + envVars = append(envVars, "GOOGLE_DEVMODE=1") + } + + env := envMap(envVars) + for _, kv := range projectDescriptor.Build.Env { + env[kv.Name] = kv.Value + } + env = addDefaultArgs(mode, env) + return env, nil +} diff --git a/pkg/skaffold/build/buildpacks/lifecycle.go b/pkg/skaffold/build/buildpacks/lifecycle.go index d35b2a733d6..874b54a3ff3 100644 --- a/pkg/skaffold/build/buildpacks/lifecycle.go +++ b/pkg/skaffold/build/buildpacks/lifecycle.go @@ -30,7 +30,6 @@ import ( "github.com/buildpacks/pack" "github.com/buildpacks/pack/project" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/misc" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" ) @@ -65,21 +64,11 @@ func (b *Builder) build(ctx context.Context, out io.Writer, a *latest.Artifact, } latest := parsed.BaseName + ":latest" - // Eveluate Env Vars. - envVars, err := misc.EvaluateEnv(artifact.Env) + // Evaluate Env Vars. + env, err := env(a, b.mode, projectDescriptor) if err != nil { return "", fmt.Errorf("unable to evaluate env variables: %w", err) } - - if b.devMode && a.Sync != nil && a.Sync.Auto != nil { - envVars = append(envVars, "GOOGLE_DEVMODE=1") - } - - env := envMap(envVars) - for _, kv := range projectDescriptor.Build.Env { - env[kv.Name] = kv.Value - } - // List buildpacks to be used for the build. // Those specified in the skaffold.yaml replace those in the project.toml. buildpacks := artifact.Buildpacks diff --git a/pkg/skaffold/build/buildpacks/types.go b/pkg/skaffold/build/buildpacks/types.go index a8b9491edb2..40ba76a91ca 100644 --- a/pkg/skaffold/build/buildpacks/types.go +++ b/pkg/skaffold/build/buildpacks/types.go @@ -16,20 +16,23 @@ limitations under the License. package buildpacks -import "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" +import ( + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" +) // Builder is an artifact builder that uses buildpacks type Builder struct { localDocker docker.LocalDaemon pushImages bool - devMode bool + mode config.RunMode } // NewArtifactBuilder returns a new buildpack artifact builder -func NewArtifactBuilder(localDocker docker.LocalDaemon, pushImages, devMode bool) *Builder { +func NewArtifactBuilder(localDocker docker.LocalDaemon, pushImages bool, mode config.RunMode) *Builder { return &Builder{ localDocker: localDocker, pushImages: pushImages, - devMode: devMode, + mode: mode, } } diff --git a/pkg/skaffold/build/cache/cache.go b/pkg/skaffold/build/cache/cache.go index 4e67085c1ae..a3f938dd9d7 100644 --- a/pkg/skaffold/build/cache/cache.go +++ b/pkg/skaffold/build/cache/cache.go @@ -85,7 +85,7 @@ func NewCache(runCtx *runcontext.RunContext, imagesAreLocal bool, dependencies D cacheFile: cacheFile, imagesAreLocal: imagesAreLocal, hashForArtifact: func(ctx context.Context, a *latest.Artifact) (string, error) { - return getHashForArtifact(ctx, dependencies, a, runCtx.DevMode()) + return getHashForArtifact(ctx, dependencies, a, runCtx.Mode()) }, }, nil } diff --git a/pkg/skaffold/build/cache/hash.go b/pkg/skaffold/build/cache/hash.go index 3fc0661852e..fc331dd672e 100644 --- a/pkg/skaffold/build/cache/hash.go +++ b/pkg/skaffold/build/cache/hash.go @@ -29,9 +29,11 @@ import ( "github.com/sirupsen/logrus" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/misc" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/buildpacks" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" ) // For testing @@ -40,11 +42,11 @@ var ( artifactConfigFunction = artifactConfig ) -func getHashForArtifact(ctx context.Context, depLister DependencyLister, a *latest.Artifact, devMode bool) (string, error) { +func getHashForArtifact(ctx context.Context, depLister DependencyLister, a *latest.Artifact, mode config.RunMode) (string, error) { var inputs []string // Append the artifact's configuration - config, err := artifactConfigFunction(a, devMode) + config, err := artifactConfigFunction(a) if err != nil { return "", fmt.Errorf("getting artifact's configuration for %q: %w", a.ImageName, err) } @@ -71,22 +73,12 @@ func getHashForArtifact(ctx context.Context, depLister DependencyLister, a *late } // add build args for the artifact if specified - if buildArgs := retrieveBuildArgs(a); buildArgs != nil { - buildArgs, err := docker.EvaluateBuildArgs(buildArgs) - if err != nil { - return "", fmt.Errorf("evaluating build args: %w", err) - } - args := convertBuildArgsToStringArray(buildArgs) - inputs = append(inputs, args...) + args, err := hashBuildArgs(a, mode) + if err != nil { + return "", fmt.Errorf("hashing build args: %w", err) } - - // add env variables for the artifact if specified - if env := retrieveEnv(a); len(env) > 0 { - evaluatedEnv, err := misc.EvaluateEnv(env) - if err != nil { - return "", fmt.Errorf("evaluating build args: %w", err) - } - inputs = append(inputs, evaluatedEnv...) + if args != nil { + inputs = append(inputs, args...) } // get a key for the hashes @@ -100,53 +92,42 @@ func getHashForArtifact(ctx context.Context, depLister DependencyLister, a *late } // TODO(dgageot): when the buildpacks builder image digest changes, we need to change the hash -func artifactConfig(a *latest.Artifact, devMode bool) (string, error) { +func artifactConfig(a *latest.Artifact) (string, error) { buf, err := json.Marshal(a.ArtifactType) if err != nil { return "", fmt.Errorf("marshalling the artifact's configuration for %q: %w", a.ImageName, err) } - - if devMode && a.BuildpackArtifact != nil && a.Sync != nil && a.Sync.Auto != nil { - return string(buf) + ".DEV", nil - } - return string(buf), nil } -func retrieveBuildArgs(artifact *latest.Artifact) map[string]*string { +func hashBuildArgs(artifact *latest.Artifact, mode config.RunMode) ([]string, error) { + // only one of args or env is ever populated + var args map[string]*string + var env map[string]string + var err error switch { case artifact.DockerArtifact != nil: - return artifact.DockerArtifact.BuildArgs - + args, err = docker.EvalBuildArgs(mode, artifact.Workspace, artifact.DockerArtifact) case artifact.KanikoArtifact != nil: - return artifact.KanikoArtifact.BuildArgs - + args, err = util.EvaluateEnvTemplateMap(artifact.KanikoArtifact.BuildArgs) + case artifact.BuildpackArtifact != nil: + env, err = buildpacks.GetEnv(artifact, mode) case artifact.CustomArtifact != nil && artifact.CustomArtifact.Dependencies.Dockerfile != nil: - return artifact.CustomArtifact.Dependencies.Dockerfile.BuildArgs - + args, err = util.EvaluateEnvTemplateMap(artifact.CustomArtifact.Dependencies.Dockerfile.BuildArgs) default: - return nil + return nil, nil } -} - -func retrieveEnv(artifact *latest.Artifact) []string { - if artifact.BuildpackArtifact != nil { - return artifact.BuildpackArtifact.Env + if err != nil { + return nil, err } - return nil -} - -func convertBuildArgsToStringArray(buildArgs map[string]*string) []string { - var args []string - for k, v := range buildArgs { - if v == nil { - args = append(args, k) - continue - } - args = append(args, fmt.Sprintf("%s=%s", k, *v)) + var sl []string + if args != nil { + sl = util.EnvPtrMapToSlice(args, "=") + } + if env != nil { + sl = util.EnvMapToSlice(env, "=") } - sort.Strings(args) - return args + return sl, nil } // cacheHasher takes hashes the contents and name of a file diff --git a/pkg/skaffold/build/cache/hash_test.go b/pkg/skaffold/build/cache/hash_test.go index e7ea4264e58..33e2dd9604b 100644 --- a/pkg/skaffold/build/cache/hash_test.go +++ b/pkg/skaffold/build/cache/hash_test.go @@ -21,6 +21,7 @@ import ( "os" "testing" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/testutil" @@ -39,14 +40,11 @@ var mockCacheHasher = func(s string) (string, error) { return s, nil } -var fakeArtifactConfig = func(a *latest.Artifact, devMode bool) (string, error) { +var fakeArtifactConfig = func(a *latest.Artifact) (string, error) { if a.ArtifactType.DockerArtifact != nil { return "docker/target=" + a.ArtifactType.DockerArtifact.Target, nil } - if devMode { - return "devmode", nil - } - return "other", nil + return "", nil } func TestGetHashForArtifact(t *testing.T) { @@ -54,31 +52,35 @@ func TestGetHashForArtifact(t *testing.T) { description string dependencies []string artifact *latest.Artifact - devMode bool + mode config.RunMode expected string }{ { description: "hash for artifact", dependencies: []string{"a", "b"}, artifact: &latest.Artifact{}, - expected: "1caa15f7ce87536bddbac30a39768e8e3b212bf591f9b64926fa50c40b614c66", + mode: config.RunModes.Dev, + expected: "d99ab295a682897269b4db0fe7c136ea1ecd542150fa224ee03155b4e3e995d9", }, { description: "ignore file not found", dependencies: []string{"a", "b", "not-found"}, artifact: &latest.Artifact{}, - expected: "1caa15f7ce87536bddbac30a39768e8e3b212bf591f9b64926fa50c40b614c66", + mode: config.RunModes.Dev, + expected: "d99ab295a682897269b4db0fe7c136ea1ecd542150fa224ee03155b4e3e995d9", }, { description: "dependencies in different orders", dependencies: []string{"b", "a"}, artifact: &latest.Artifact{}, - expected: "1caa15f7ce87536bddbac30a39768e8e3b212bf591f9b64926fa50c40b614c66", + mode: config.RunModes.Dev, + expected: "d99ab295a682897269b4db0fe7c136ea1ecd542150fa224ee03155b4e3e995d9", }, { description: "no dependencies", artifact: &latest.Artifact{}, - expected: "53ebd85adc9b03923a7dacfe6002879af526ef6067d441419d6e62fb9bf608ab", + mode: config.RunModes.Dev, + expected: "7c077ca2308714493d07163e1033c4282bd869ff6d477b3e77408587f95e2930", }, { description: "docker target", @@ -89,6 +91,7 @@ func TestGetHashForArtifact(t *testing.T) { }, }, }, + mode: config.RunModes.Dev, expected: "f947b5aad32734914aa2dea0ec95bceff257037e6c2a529007183c3f21547eae", }, { @@ -100,6 +103,7 @@ func TestGetHashForArtifact(t *testing.T) { }, }, }, + mode: config.RunModes.Dev, expected: "09b366c764d0e39f942283cc081d5522b9dde52e725376661808054e3ed0177f", }, { @@ -114,35 +118,45 @@ func TestGetHashForArtifact(t *testing.T) { }, }, }, + mode: config.RunModes.Dev, expected: "f3f710a4ec1d1bfb2a9b8ef2b4b7cc5f254102d17095a71872821b396953a4ce", }, { - description: "env variables", + description: "buildpack in dev mode", dependencies: []string{"a", "b"}, artifact: &latest.Artifact{ ArtifactType: latest.ArtifactType{ - BuildpackArtifact: &latest.BuildpackArtifact{ - Env: []string{"key=value"}, - }, + BuildpackArtifact: &latest.BuildpackArtifact{}, }, }, - expected: "a2e225e66c5932e41b0026164bf204533d59974b42fbb645da2855dc9d432cb9", + mode: config.RunModes.Dev, + expected: "d99ab295a682897269b4db0fe7c136ea1ecd542150fa224ee03155b4e3e995d9", }, { - description: "devmode", + description: "buildpack in debug mode", dependencies: []string{"a", "b"}, - artifact: &latest.Artifact{}, - devMode: true, - expected: "f019dda9d0c38fea4aab1685c7da54f7009aba1cb47e3cb4c6c1ce5b10fa5c32", + artifact: &latest.Artifact{ + ArtifactType: latest.ArtifactType{ + BuildpackArtifact: &latest.BuildpackArtifact{}, + }, + }, + mode: config.RunModes.Debug, + expected: "a15f9e22a5c5a244c47a5205d577fdbf80e886a4b36915050113b082850a9c5c", }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { t.Override(&hashFunction, mockCacheHasher) t.Override(&artifactConfigFunction, fakeArtifactConfig) + if test.artifact.DockerArtifact != nil { + tmpDir := t.NewTempDir() + tmpDir.Write("./Dockerfile", "ARG SKAFFOLD_GO_GCFLAGS\nFROM foo") + test.artifact.Workspace = tmpDir.Path(".") + test.artifact.DockerArtifact.DockerfilePath = "Dockerfile" + } depLister := stubDependencyLister(test.dependencies) - actual, err := getHashForArtifact(context.Background(), depLister, test.artifact, test.devMode) + actual, err := getHashForArtifact(context.Background(), depLister, test.artifact, test.mode) t.CheckNoError(err) t.CheckDeepEqual(test.expected, actual) @@ -158,7 +172,7 @@ func TestArtifactConfig(t *testing.T) { Target: "target", }, }, - }, false) + }) t.CheckNoError(err) config2, err := artifactConfig(&latest.Artifact{ @@ -167,7 +181,7 @@ func TestArtifactConfig(t *testing.T) { Target: "other", }, }, - }, false) + }) t.CheckNoError(err) if config1 == config2 { @@ -176,71 +190,58 @@ func TestArtifactConfig(t *testing.T) { }) } -func TestArtifactConfigDevMode(t *testing.T) { - testutil.Run(t, "", func(t *testutil.T) { - artifact := latest.ArtifactType{ - BuildpackArtifact: &latest.BuildpackArtifact{ - Builder: "any/builder", - }, - } - sync := &latest.Sync{ - Auto: &latest.Auto{}, - } - - config, err := artifactConfig(&latest.Artifact{ - ArtifactType: artifact, - Sync: sync, - }, false) - t.CheckNoError(err) - - configDevMode, err := artifactConfig(&latest.Artifact{ - ArtifactType: artifact, - Sync: sync, - }, true) - t.CheckNoError(err) - - if config == configDevMode { - t.Errorf("configs should be different: [%s] [%s]", config, configDevMode) - } - }) -} - func TestBuildArgs(t *testing.T) { - testutil.Run(t, "", func(t *testutil.T) { - expected := "f5b610f4fea07461411b2ea0e2cddfd2ffc28d1baed49180f5d3acee5a18f9e7" - - artifact := &latest.Artifact{ - ArtifactType: latest.ArtifactType{ - DockerArtifact: &latest.DockerArtifact{ - BuildArgs: map[string]*string{"one": stringPointer("1"), "two": stringPointer("2")}, + tests := []struct { + mode config.RunMode + expected string + }{ + { + mode: config.RunModes.Debug, + expected: "771e726436816ce229a2838b38aee8c85c7dda4411e7ba68cfd898473ae12ada", + }, + { + mode: config.RunModes.Dev, + expected: "f5b610f4fea07461411b2ea0e2cddfd2ffc28d1baed49180f5d3acee5a18f9e7", + }, + } + for _, test := range tests { + testutil.Run(t, "", func(t *testutil.T) { + tmpDir := t.NewTempDir() + tmpDir.Write("./Dockerfile", "ARG SKAFFOLD_GO_GCFLAGS\nFROM foo") + artifact := &latest.Artifact{ + Workspace: tmpDir.Path("."), + ArtifactType: latest.ArtifactType{ + DockerArtifact: &latest.DockerArtifact{ + DockerfilePath: "Dockerfile", + BuildArgs: map[string]*string{"one": util.StringPtr("1"), "two": util.StringPtr("2")}, + }, }, - }, - } - - t.Override(&hashFunction, mockCacheHasher) - t.Override(&artifactConfigFunction, fakeArtifactConfig) + } - actual, err := getHashForArtifact(context.Background(), stubDependencyLister(nil), artifact, false) + t.Override(&hashFunction, mockCacheHasher) + t.Override(&artifactConfigFunction, fakeArtifactConfig) + actual, err := getHashForArtifact(context.Background(), stubDependencyLister(nil), artifact, test.mode) - t.CheckNoError(err) - t.CheckDeepEqual(expected, actual) + t.CheckNoError(err) + t.CheckDeepEqual(test.expected, actual) - // Change order of buildargs - artifact.ArtifactType.DockerArtifact.BuildArgs = map[string]*string{"two": stringPointer("2"), "one": stringPointer("1")} - actual, err = getHashForArtifact(context.Background(), stubDependencyLister(nil), artifact, false) + // Change order of buildargs + artifact.ArtifactType.DockerArtifact.BuildArgs = map[string]*string{"two": util.StringPtr("2"), "one": util.StringPtr("1")} + actual, err = getHashForArtifact(context.Background(), stubDependencyLister(nil), artifact, test.mode) - t.CheckNoError(err) - t.CheckDeepEqual(expected, actual) + t.CheckNoError(err) + t.CheckDeepEqual(test.expected, actual) - // Change build args, get different hash - artifact.ArtifactType.DockerArtifact.BuildArgs = map[string]*string{"one": stringPointer("1")} - actual, err = getHashForArtifact(context.Background(), stubDependencyLister(nil), artifact, false) + // Change build args, get different hash + artifact.ArtifactType.DockerArtifact.BuildArgs = map[string]*string{"one": util.StringPtr("1")} + actual, err = getHashForArtifact(context.Background(), stubDependencyLister(nil), artifact, test.mode) - t.CheckNoError(err) - if actual == expected { - t.Fatal("got same hash as different artifact; expected different hashes.") - } - }) + t.CheckNoError(err) + if actual == test.expected { + t.Fatal("got same hash as different artifact; expected different hashes.") + } + }) + } } func TestBuildArgsEnvSubstitution(t *testing.T) { @@ -250,11 +251,14 @@ func TestBuildArgsEnvSubstitution(t *testing.T) { util.OSEnviron = func() []string { return []string{"FOO=bar"} } - + tmpDir := t.NewTempDir() + tmpDir.Write("./Dockerfile", "ARG SKAFFOLD_GO_GCFLAGS\nFROM foo") artifact := &latest.Artifact{ + Workspace: tmpDir.Path("."), ArtifactType: latest.ArtifactType{ DockerArtifact: &latest.DockerArtifact{ - BuildArgs: map[string]*string{"env": stringPointer("${{.FOO}}")}, + BuildArgs: map[string]*string{"env": util.StringPtr("${{.FOO}}")}, + DockerfilePath: "Dockerfile", }, }, } @@ -263,7 +267,7 @@ func TestBuildArgsEnvSubstitution(t *testing.T) { t.Override(&artifactConfigFunction, fakeArtifactConfig) depLister := stubDependencyLister([]string{"dep"}) - hash1, err := getHashForArtifact(context.Background(), depLister, artifact, false) + hash1, err := getHashForArtifact(context.Background(), depLister, artifact, config.RunModes.Build) t.CheckNoError(err) @@ -273,7 +277,7 @@ func TestBuildArgsEnvSubstitution(t *testing.T) { return []string{"FOO=baz"} } - hash2, err := getHashForArtifact(context.Background(), depLister, artifact, false) + hash2, err := getHashForArtifact(context.Background(), depLister, artifact, config.RunModes.Build) t.CheckNoError(err) if hash1 == hash2 { @@ -330,7 +334,7 @@ func TestCacheHasher(t *testing.T) { path := originalFile depLister := stubDependencyLister([]string{tmpDir.Path(originalFile)}) - oldHash, err := getHashForArtifact(context.Background(), depLister, &latest.Artifact{}, false) + oldHash, err := getHashForArtifact(context.Background(), depLister, &latest.Artifact{}, config.RunModes.Build) t.CheckNoError(err) test.update(originalFile, tmpDir) @@ -339,7 +343,7 @@ func TestCacheHasher(t *testing.T) { } depLister = stubDependencyLister([]string{tmpDir.Path(path)}) - newHash, err := getHashForArtifact(context.Background(), depLister, &latest.Artifact{}, false) + newHash, err := getHashForArtifact(context.Background(), depLister, &latest.Artifact{}, config.RunModes.Build) t.CheckNoError(err) t.CheckFalse(test.differentHash && oldHash == newHash) @@ -348,25 +352,48 @@ func TestCacheHasher(t *testing.T) { } } -func TestRetrieveBuildArgs(t *testing.T) { +func TestHashBuildArgs(t *testing.T) { tests := []struct { description string artifactType latest.ArtifactType - expected map[string]*string + expected []string + mode config.RunMode }{ { - description: "docker artifact with build args", + description: "docker artifact with build args for dev", artifactType: latest.ArtifactType{ DockerArtifact: &latest.DockerArtifact{ - BuildArgs: map[string]*string{}, + BuildArgs: map[string]*string{ + "foo": util.StringPtr("bar"), + }, }, }, - expected: map[string]*string{}, + mode: config.RunModes.Dev, + expected: []string{"foo=bar"}, + }, { + description: "docker artifact with build args for debug", + artifactType: latest.ArtifactType{ + DockerArtifact: &latest.DockerArtifact{ + BuildArgs: map[string]*string{ + "foo": util.StringPtr("bar"), + }, + }, + }, + mode: config.RunModes.Debug, + expected: []string{"SKAFFOLD_GO_GCFLAGS='all=-N -l'", "foo=bar"}, + }, { + description: "docker artifact without build args for debug", + artifactType: latest.ArtifactType{ + DockerArtifact: &latest.DockerArtifact{}, + }, + mode: config.RunModes.Debug, + expected: []string{"SKAFFOLD_GO_GCFLAGS='all=-N -l'"}, }, { - description: "docker artifact without build args", + description: "docker artifact without build args for dev", artifactType: latest.ArtifactType{ DockerArtifact: &latest.DockerArtifact{}, }, + mode: config.RunModes.Dev, }, { description: "kaniko artifact with build args", artifactType: latest.ArtifactType{ @@ -374,12 +401,43 @@ func TestRetrieveBuildArgs(t *testing.T) { BuildArgs: map[string]*string{}, }, }, - expected: map[string]*string{}, + expected: nil, }, { description: "kaniko artifact without build args", artifactType: latest.ArtifactType{ KanikoArtifact: &latest.KanikoArtifact{}, }, + }, { + description: "buildpacks artifact with env for dev", + artifactType: latest.ArtifactType{ + BuildpackArtifact: &latest.BuildpackArtifact{ + Env: []string{"foo=bar"}, + }, + }, + mode: config.RunModes.Dev, + expected: []string{"foo=bar"}, + }, { + description: "buildpacks artifact without env for dev", + artifactType: latest.ArtifactType{ + BuildpackArtifact: &latest.BuildpackArtifact{}, + }, + mode: config.RunModes.Dev, + }, { + description: "buildpacks artifact with env for debug", + artifactType: latest.ArtifactType{ + BuildpackArtifact: &latest.BuildpackArtifact{ + Env: []string{"foo=bar"}, + }, + }, + mode: config.RunModes.Debug, + expected: []string{"GOOGLE_GOGCFLAGS='all=-N -l'", "foo=bar"}, + }, { + description: "buildpacks artifact without env for debug", + artifactType: latest.ArtifactType{ + BuildpackArtifact: &latest.BuildpackArtifact{}, + }, + mode: config.RunModes.Debug, + expected: []string{"GOOGLE_GOGCFLAGS='all=-N -l'"}, }, { description: "custom artifact, dockerfile dependency, with build args", artifactType: latest.ArtifactType{ @@ -391,7 +449,7 @@ func TestRetrieveBuildArgs(t *testing.T) { }, }, }, - expected: map[string]*string{}, + expected: nil, }, { description: "custom artifact, no dockerfile dependency", artifactType: latest.ArtifactType{ @@ -404,54 +462,18 @@ func TestRetrieveBuildArgs(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - actual := retrieveBuildArgs(&latest.Artifact{ + a := &latest.Artifact{ ArtifactType: test.artifactType, - }) - - t.CheckDeepEqual(test.expected, actual) - }) - } -} - -func TestConvertBuildArgsToStringArray(t *testing.T) { - tests := []struct { - description string - buildArgs map[string]*string - expected []string - }{ - { - description: "regular key:value build args", - buildArgs: map[string]*string{ - "one": stringPointer("1"), - "two": stringPointer("2"), - }, - expected: []string{"one=1", "two=2"}, - }, { - description: "empty key:value build args", - buildArgs: map[string]*string{ - "one": stringPointer(""), - "two": stringPointer(""), - }, - expected: []string{"one=", "two="}, - }, { - description: "build args with nil value", - buildArgs: map[string]*string{ - "one": nil, - "two": nil, - }, - expected: []string{"one", "two"}, - }, - } - - for _, test := range tests { - testutil.Run(t, test.description, func(t *testutil.T) { - actual := convertBuildArgsToStringArray(test.buildArgs) - + } + if test.artifactType.DockerArtifact != nil { + tmpDir := t.NewTempDir() + tmpDir.Write("./Dockerfile", "ARG SKAFFOLD_GO_GCFLAGS\nFROM foo") + a.Workspace = tmpDir.Path(".") + a.ArtifactType.DockerArtifact.DockerfilePath = "Dockerfile" + } + actual, err := hashBuildArgs(a, test.mode) + t.CheckNoError(err) t.CheckDeepEqual(test.expected, actual) }) } } - -func stringPointer(s string) *string { - return &s -} diff --git a/pkg/skaffold/build/cache/retrieve_test.go b/pkg/skaffold/build/cache/retrieve_test.go index b40cd2a7baa..0d7f451fc53 100644 --- a/pkg/skaffold/build/cache/retrieve_test.go +++ b/pkg/skaffold/build/cache/retrieve_test.go @@ -57,7 +57,7 @@ func (b *mockBuilder) BuildAndTest(ctx context.Context, out io.Writer, tags tag. b.built = append(b.built, artifact) tag := tags[artifact.ImageName] - _, err := b.dockerDaemon.Build(ctx, out, artifact.Workspace, artifact.DockerArtifact, tag) + _, err := b.dockerDaemon.Build(ctx, out, artifact.Workspace, artifact.DockerArtifact, tag, config.RunModes.Dev) if err != nil { return nil, err } @@ -122,6 +122,11 @@ func TestCacheBuildLocal(t *testing.T) { return dockerDaemon, nil }) + // Mock args builder + t.Override(&docker.EvalBuildArgs, func(mode config.RunMode, workspace string, a *latest.DockerArtifact) (map[string]*string, error) { + return a.BuildArgs, nil + }) + // Create cache artifactCache, err := NewCache(runCtx, true, deps) t.CheckNoError(err) @@ -215,6 +220,10 @@ func TestCacheBuildRemote(t *testing.T) { } }) + // Mock args builder + t.Override(&docker.EvalBuildArgs, func(mode config.RunMode, workspace string, a *latest.DockerArtifact) (map[string]*string, error) { + return a.BuildArgs, nil + }) // Create cache artifactCache, err := NewCache(runCtx, false, deps) t.CheckNoError(err) diff --git a/pkg/skaffold/build/cluster/pod.go b/pkg/skaffold/build/cluster/pod.go index 42822fb5f49..4cf5cde0426 100644 --- a/pkg/skaffold/build/cluster/pod.go +++ b/pkg/skaffold/build/cluster/pod.go @@ -28,8 +28,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/version" ) @@ -250,7 +250,7 @@ func kanikoArgs(artifact *latest.KanikoArtifact, tag string, insecureRegistries args = append(args, artifact.AdditionalFlags...) } - buildArgs, err := docker.EvaluateBuildArgs(artifact.BuildArgs) + buildArgs, err := util.EvaluateEnvTemplateMap(artifact.BuildArgs) if err != nil { return nil, fmt.Errorf("unable to evaluate build args: %w", err) } diff --git a/pkg/skaffold/build/gcb/docker.go b/pkg/skaffold/build/gcb/docker.go index d41685f019f..2a45c419730 100644 --- a/pkg/skaffold/build/gcb/docker.go +++ b/pkg/skaffold/build/gcb/docker.go @@ -23,6 +23,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" ) // dockerBuildSpec lists the build steps required to build a docker image. @@ -61,7 +62,12 @@ func (b *Builder) cacheFromSteps(artifact *latest.DockerArtifact) []*cloudbuild. // dockerBuildArgs lists the arguments passed to `docker` to build a given image. func (b *Builder) dockerBuildArgs(artifact *latest.DockerArtifact, tag string) ([]string, error) { - ba, err := docker.GetBuildArgs(artifact) + buildArgs, err := util.EvaluateEnvTemplateMap(artifact.BuildArgs) + if err != nil { + return nil, fmt.Errorf("unable to evaluate build args: %w", err) + } + + ba, err := docker.ToCLIBuildArgs(artifact, buildArgs) if err != nil { return nil, fmt.Errorf("getting docker build args: %w", err) } diff --git a/pkg/skaffold/build/gcb/kaniko.go b/pkg/skaffold/build/gcb/kaniko.go index 49a0d5f9796..158dc17408f 100644 --- a/pkg/skaffold/build/gcb/kaniko.go +++ b/pkg/skaffold/build/gcb/kaniko.go @@ -22,8 +22,8 @@ import ( "google.golang.org/api/cloudbuild/v1" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" ) func (b *Builder) kanikoBuildSpec(artifact *latest.KanikoArtifact, tag string) (cloudbuild.Build, error) { @@ -63,7 +63,7 @@ func (b *Builder) kanikoBuildSpec(artifact *latest.KanikoArtifact, tag string) ( } func (b *Builder) kanikoBuildArgs(artifact *latest.KanikoArtifact) ([]string, error) { - buildArgs, err := docker.EvaluateBuildArgs(artifact.BuildArgs) + buildArgs, err := util.EvaluateEnvTemplateMap(artifact.BuildArgs) if err != nil { return nil, fmt.Errorf("unable to evaluate build args: %w", err) } diff --git a/pkg/skaffold/build/local/docker.go b/pkg/skaffold/build/local/docker.go index 94aaad4c4d5..d0cba916dfe 100644 --- a/pkg/skaffold/build/local/docker.go +++ b/pkg/skaffold/build/local/docker.go @@ -23,13 +23,14 @@ import ( "os" "os/exec" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/warnings" ) -func (b *Builder) buildDocker(ctx context.Context, out io.Writer, a *latest.Artifact, tag string) (string, error) { +func (b *Builder) buildDocker(ctx context.Context, out io.Writer, a *latest.Artifact, tag string, mode config.RunMode) (string, error) { // Fail fast if the Dockerfile can't be found. dockerfile, err := docker.NormalizeDockerfilePath(a.Workspace, a.DockerArtifact.DockerfilePath) if err != nil { @@ -48,7 +49,7 @@ func (b *Builder) buildDocker(ctx context.Context, out io.Writer, a *latest.Arti if b.cfg.UseDockerCLI || b.cfg.UseBuildkit { imageID, err = b.dockerCLIBuild(ctx, out, a.Workspace, a.ArtifactType.DockerArtifact, tag) } else { - imageID, err = b.localDocker.Build(ctx, out, a.Workspace, a.ArtifactType.DockerArtifact, tag) + imageID, err = b.localDocker.Build(ctx, out, a.Workspace, a.ArtifactType.DockerArtifact, tag, mode) } if err != nil { @@ -73,11 +74,15 @@ func (b *Builder) dockerCLIBuild(ctx context.Context, out io.Writer, workspace s } args := []string{"build", workspace, "--file", dockerfilePath, "-t", tag} - ba, err := docker.GetBuildArgs(a) + ba, err := docker.EvalBuildArgs(b.mode, workspace, a) + if err != nil { + return "", fmt.Errorf("unable to evaluate build args: %w", err) + } + cliArgs, err := docker.ToCLIBuildArgs(a, ba) if err != nil { return "", fmt.Errorf("getting docker build args: %w", err) } - args = append(args, ba...) + args = append(args, cliArgs...) if b.prune { args = append(args, "--force-rm") diff --git a/pkg/skaffold/build/local/docker_test.go b/pkg/skaffold/build/local/docker_test.go index 8e0f4d1fc36..a7d1d505381 100644 --- a/pkg/skaffold/build/local/docker_test.go +++ b/pkg/skaffold/build/local/docker_test.go @@ -22,6 +22,7 @@ import ( "path/filepath" "testing" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" @@ -33,12 +34,14 @@ func TestDockerCLIBuild(t *testing.T) { tests := []struct { description string localBuild latest.LocalBuild + mode config.RunMode extraEnv []string expectedEnv []string }{ { description: "docker build", localBuild: latest.LocalBuild{}, + mode: config.RunModes.Dev, expectedEnv: []string{"KEY=VALUE"}, }, { @@ -72,6 +75,9 @@ func TestDockerCLIBuild(t *testing.T) { t.NewTempDir().Touch("Dockerfile").Chdir() dockerfilePath, _ := filepath.Abs("Dockerfile") t.Override(&docker.DefaultAuthHelper, testAuthHelper{}) + t.Override(&docker.EvalBuildArgs, func(mode config.RunMode, workspace string, a *latest.DockerArtifact) (map[string]*string, error) { + return a.BuildArgs, nil + }) t.Override(&util.DefaultExecCommand, testutil.CmdRunEnv( "docker build . --file "+dockerfilePath+" -t tag --force-rm", test.expectedEnv, @@ -93,7 +99,7 @@ func TestDockerCLIBuild(t *testing.T) { }, } - _, err = builder.buildDocker(context.Background(), ioutil.Discard, artifact, "tag") + _, err = builder.buildDocker(context.Background(), ioutil.Discard, artifact, "tag", test.mode) t.CheckNoError(err) }) } diff --git a/pkg/skaffold/build/local/local.go b/pkg/skaffold/build/local/local.go index 700cf66c9ab..0327a2cae15 100644 --- a/pkg/skaffold/build/local/local.go +++ b/pkg/skaffold/build/local/local.go @@ -87,7 +87,7 @@ func (b *Builder) runBuildForArtifact(ctx context.Context, out io.Writer, a *lat switch { case a.DockerArtifact != nil: - return b.buildDocker(ctx, out, a, tag) + return b.buildDocker(ctx, out, a, tag, b.mode) case a.BazelArtifact != nil: return bazel.NewArtifactBuilder(b.localDocker, b.insecureRegistries, b.pushImages).Build(ctx, out, a, tag) @@ -99,7 +99,7 @@ func (b *Builder) runBuildForArtifact(ctx context.Context, out io.Writer, a *lat return custom.NewArtifactBuilder(b.localDocker, b.insecureRegistries, b.pushImages, b.retrieveExtraEnv()).Build(ctx, out, a, tag) case a.BuildpackArtifact != nil: - return buildpacks.NewArtifactBuilder(b.localDocker, b.pushImages, b.devMode).Build(ctx, out, a, tag) + return buildpacks.NewArtifactBuilder(b.localDocker, b.pushImages, b.mode).Build(ctx, out, a, tag) default: return "", fmt.Errorf("unexpected type %q for local artifact:\n%s", misc.ArtifactType(a), misc.FormatArtifact(a)) diff --git a/pkg/skaffold/build/local/local_test.go b/pkg/skaffold/build/local/local_test.go index eb2aa159c5a..9a737776a0e 100644 --- a/pkg/skaffold/build/local/local_test.go +++ b/pkg/skaffold/build/local/local_test.go @@ -235,7 +235,9 @@ func TestLocalRun(t *testing.T) { t.Override(&docker.NewAPIClient, func(*runcontext.RunContext) (docker.LocalDaemon, error) { return docker.NewLocalDaemon(test.api, nil, false, nil), nil }) - + t.Override(&docker.EvalBuildArgs, func(mode config.RunMode, workspace string, a *latest.DockerArtifact) (map[string]*string, error) { + return a.BuildArgs, nil + }) event.InitializeState(latest.Pipeline{ Deploy: latest.DeployConfig{}, Build: latest.BuildConfig{ diff --git a/pkg/skaffold/build/local/types.go b/pkg/skaffold/build/local/types.go index d5d72a114e2..6dfc5b6bef7 100644 --- a/pkg/skaffold/build/local/types.go +++ b/pkg/skaffold/build/local/types.go @@ -40,7 +40,7 @@ type Builder struct { prune bool pruneChildren bool skipTests bool - devMode bool + mode config.RunMode kubeContext string builtImages []string insecureRegistries map[string]bool @@ -82,11 +82,11 @@ func NewBuilder(runCtx *runcontext.RunContext) (*Builder, error) { localDocker: localDocker, localCluster: localCluster, pushImages: pushImages, - skipTests: runCtx.SkipTests(), - devMode: runCtx.DevMode(), - prune: runCtx.Prune(), - pruneChildren: !runCtx.NoPruneChildren(), - insecureRegistries: runCtx.GetInsecureRegistries(), + skipTests: runCtx.Opts.SkipTests, + mode: runCtx.Mode(), + prune: runCtx.Opts.Prune(), + pruneChildren: !runCtx.Opts.NoPruneChildren, + insecureRegistries: runCtx.InsecureRegistries, muted: runCtx.Muted(), }, nil } diff --git a/pkg/skaffold/config/options.go b/pkg/skaffold/config/options.go index 4e05b41b24f..8dca2e97d40 100644 --- a/pkg/skaffold/config/options.go +++ b/pkg/skaffold/config/options.go @@ -94,18 +94,32 @@ type SkaffoldOptions struct { WaitForDeletions WaitForDeletions } +type RunMode string + +var RunModes = struct { + Build RunMode + Dev RunMode + Debug RunMode + Run RunMode + Deploy RunMode + Render RunMode +}{ + Build: "build", + Dev: "dev", + Debug: "debug", + Run: "run", + Deploy: "deploy", + Render: "render", +} + // Prune returns true iff the user did NOT specify the --no-prune flag, // and the user did NOT specify the --cache-artifacts flag. func (opts *SkaffoldOptions) Prune() bool { return !opts.NoPrune && !opts.CacheArtifacts } -func (opts *SkaffoldOptions) IsDevMode() bool { - return opts.Command == "dev" -} - -func (opts *SkaffoldOptions) IsDebugMode() bool { - return opts.Command == "debug" +func (opts *SkaffoldOptions) Mode() RunMode { + return RunMode(opts.Command) } func (opts *SkaffoldOptions) IsTargetImage(artifact *latest.Artifact) bool { diff --git a/pkg/skaffold/docker/build_args.go b/pkg/skaffold/docker/build_args.go new file mode 100644 index 00000000000..a15edf7f028 --- /dev/null +++ b/pkg/skaffold/docker/build_args.go @@ -0,0 +1,75 @@ +/* +Copyright 2020 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 docker + +import ( + "fmt" + "os" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" +) + +var ( + EvalBuildArgs = evalBuildArgs // To override during testing + + // default build args for skaffold non-debug mode + nonDebugModeArgs = map[string]string{} + // default build args for skaffold debug mode + debugModeArgs = map[string]string{ + "SKAFFOLD_GO_GCFLAGS": "'all=-N -l'", // disable build optimization for Golang + // TODO: Add for other languages + } +) + +func evalBuildArgs(mode config.RunMode, workspace string, a *latest.DockerArtifact) (map[string]*string, error) { + var defaults map[string]string + switch mode { + case config.RunModes.Debug: + defaults = debugModeArgs + default: + defaults = nonDebugModeArgs + } + result := map[string]*string{ + "SKAFFOLD_RUN_MODE": util.StringPtr(string(mode)), + } + for k, v := range defaults { + result[k] = &v + } + absDockerfilePath, err := NormalizeDockerfilePath(workspace, a.DockerfilePath) + if err != nil { + return nil, fmt.Errorf("normalizing dockerfile path: %w", err) + } + f, err := os.Open(absDockerfilePath) + if err != nil { + return nil, fmt.Errorf("reading dockerfile: %w", err) + } + defer f.Close() + result, err = filterUnusedBuildArgs(f, result) + if err != nil { + return nil, fmt.Errorf("removing unused default args: %w", err) + } + for k, v := range a.BuildArgs { + result[k] = v + } + result, err = util.EvaluateEnvTemplateMap(result) + if err != nil { + return nil, fmt.Errorf("unable to expand build args: %w", err) + } + return result, nil +} diff --git a/pkg/skaffold/docker/build_args_test.go b/pkg/skaffold/docker/build_args_test.go new file mode 100644 index 00000000000..15c2330f63d --- /dev/null +++ b/pkg/skaffold/docker/build_args_test.go @@ -0,0 +1,192 @@ +/* +Copyright 2020 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 docker + +import ( + "testing" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" + "github.com/GoogleContainerTools/skaffold/testutil" +) + +func TestEvalBuildArgs(t *testing.T) { + tests := []struct { + description string + dockerfile string + mode config.RunMode + buildArgs map[string]*string + expected map[string]*string + }{ + { + description: "debug with exact build args", + dockerfile: `ARG foo1 +ARG foo2 +ARG foo3 +ARG SKAFFOLD_GO_GCFLAGS +FROM bar1`, + buildArgs: map[string]*string{ + "foo1": util.StringPtr("one"), + "foo2": util.StringPtr("two"), + "foo3": util.StringPtr("three"), + }, + mode: config.RunModes.Debug, + expected: map[string]*string{ + "SKAFFOLD_GO_GCFLAGS": util.StringPtr("'all=-N -l'"), + "foo1": util.StringPtr("one"), + "foo2": util.StringPtr("two"), + "foo3": util.StringPtr("three"), + }, + }, + { + description: "debug with extra build args", + dockerfile: `ARG foo1 +ARG foo3 +ARG SKAFFOLD_GO_GCFLAGS +FROM bar1`, + mode: config.RunModes.Debug, + buildArgs: map[string]*string{ + "foo1": util.StringPtr("one"), + "foo2": util.StringPtr("two"), + "foo3": util.StringPtr("three"), + }, + expected: map[string]*string{ + "SKAFFOLD_GO_GCFLAGS": util.StringPtr("'all=-N -l'"), + "foo1": util.StringPtr("one"), + "foo2": util.StringPtr("two"), + "foo3": util.StringPtr("three"), + }, + }, + { + description: "debug with extra default args", + dockerfile: `ARG foo1 +ARG foo3 +FROM bar1`, + buildArgs: map[string]*string{ + "foo1": util.StringPtr("one"), + "foo2": util.StringPtr("two"), + "foo3": util.StringPtr("three"), + }, + mode: config.RunModes.Debug, + expected: map[string]*string{ + "foo1": util.StringPtr("one"), + "foo2": util.StringPtr("two"), + "foo3": util.StringPtr("three"), + }, + }, + { + description: "debug with exact default args for multistage", + dockerfile: `ARG SKAFFOLD_GO_GCFLAGS +ARG foo1 +FROM bar1 +ARG SKAFFOLD_GO_GCFLAGS +ARG foo2 +FROM bar2 +ARG foo3`, + buildArgs: map[string]*string{ + "foo1": util.StringPtr("one"), + "foo2": util.StringPtr("two"), + "foo3": util.StringPtr("three"), + }, + mode: config.RunModes.Debug, + expected: map[string]*string{ + "SKAFFOLD_GO_GCFLAGS": util.StringPtr("'all=-N -l'"), + "foo1": util.StringPtr("one"), + "foo2": util.StringPtr("two"), + "foo3": util.StringPtr("three"), + }, + }, + { + description: "debug with extra default args for multistage", + dockerfile: `ARG foo1 +ARG SKAFFOLD_RUN_MODE +FROM bar1 +ARG SKAFFOLD_GO_GCFLAGS +ARG foo2 +FROM bar2 +ARG foo3`, + buildArgs: map[string]*string{ + "foo1": util.StringPtr("one"), + "foo2": util.StringPtr("two"), + "foo3": util.StringPtr("three"), + }, + mode: config.RunModes.Debug, + expected: map[string]*string{ + "SKAFFOLD_RUN_MODE": util.StringPtr("debug"), + "SKAFFOLD_GO_GCFLAGS": util.StringPtr("'all=-N -l'"), + "foo1": util.StringPtr("one"), + "foo2": util.StringPtr("two"), + "foo3": util.StringPtr("three"), + }, + }, + { + description: "dev with exact build args", + dockerfile: `ARG foo1 +ARG foo2 +ARG foo3 +ARG SKAFFOLD_RUN_MODE +FROM bar1`, + buildArgs: map[string]*string{ + "foo1": util.StringPtr("one"), + "foo2": util.StringPtr("two"), + "foo3": util.StringPtr("three"), + }, + mode: config.RunModes.Dev, + expected: map[string]*string{ + "SKAFFOLD_RUN_MODE": util.StringPtr("dev"), + "foo1": util.StringPtr("one"), + "foo2": util.StringPtr("two"), + "foo3": util.StringPtr("three"), + }, + }, + { + description: "dev with extra build args", + dockerfile: `ARG foo1 +ARG foo3 +FROM bar1`, + buildArgs: map[string]*string{ + "foo1": util.StringPtr("one"), + "foo2": util.StringPtr("two"), + "foo3": util.StringPtr("three"), + }, + mode: config.RunModes.Dev, + expected: map[string]*string{ + "foo1": util.StringPtr("one"), + "foo2": util.StringPtr("two"), + "foo3": util.StringPtr("three"), + }, + }, + } + + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + artifact := &latest.DockerArtifact{ + DockerfilePath: "Dockerfile", + BuildArgs: test.buildArgs, + } + + tmpDir := t.NewTempDir() + tmpDir.Write("./Dockerfile", test.dockerfile) + workspace := tmpDir.Path(".") + + actual, err := EvalBuildArgs(test.mode, workspace, artifact) + t.CheckNoError(err) + t.CheckDeepEqual(test.expected, actual) + }) + } +} diff --git a/pkg/skaffold/docker/image.go b/pkg/skaffold/docker/image.go index c400d37f94b..90bb5b9e714 100644 --- a/pkg/skaffold/docker/image.go +++ b/pkg/skaffold/docker/image.go @@ -37,6 +37,7 @@ import ( v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/sirupsen/logrus" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" @@ -62,7 +63,7 @@ type LocalDaemon interface { ExtraEnv() []string ServerVersion(ctx context.Context) (types.Version, error) ConfigFile(ctx context.Context, image string) (*v1.ConfigFile, error) - Build(ctx context.Context, out io.Writer, workspace string, a *latest.DockerArtifact, ref string) (string, error) + Build(ctx context.Context, out io.Writer, workspace string, a *latest.DockerArtifact, ref string, mode config.RunMode) (string, error) Push(ctx context.Context, out io.Writer, ref string) (string, error) Pull(ctx context.Context, out io.Writer, ref string) error Load(ctx context.Context, out io.Writer, input io.Reader, ref string) (string, error) @@ -156,14 +157,13 @@ func (l *localDaemon) ConfigFile(ctx context.Context, image string) (*v1.ConfigF } // Build performs a docker build and returns the imageID. -func (l *localDaemon) Build(ctx context.Context, out io.Writer, workspace string, a *latest.DockerArtifact, ref string) (string, error) { +func (l *localDaemon) Build(ctx context.Context, out io.Writer, workspace string, a *latest.DockerArtifact, ref string, mode config.RunMode) (string, error) { logrus.Debugf("Running docker build: context: %s, dockerfile: %s", workspace, a.DockerfilePath) // Like `docker build`, we ignore the errors // See https://github.com/docker/cli/blob/75c1bb1f33d7cedbaf48404597d5bf9818199480/cli/command/image/build.go#L364 authConfigs, _ := DefaultAuthHelper.GetAllAuthConfigs() - - buildArgs, err := EvaluateBuildArgs(a.BuildArgs) + buildArgs, err := EvalBuildArgs(mode, workspace, a) if err != nil { return "", fmt.Errorf("unable to evaluate build args: %w", err) } @@ -427,17 +427,10 @@ func (l *localDaemon) ImageRemove(ctx context.Context, image string, opts types. return nil, fmt.Errorf("could not remove image after %d retries", retries) } -// GetBuildArgs gives the build args flags for docker build. -func GetBuildArgs(a *latest.DockerArtifact) ([]string, error) { +func ToCLIBuildArgs(a *latest.DockerArtifact, evaluatedArgs map[string]*string) ([]string, error) { var args []string - - buildArgs, err := EvaluateBuildArgs(a.BuildArgs) - if err != nil { - return nil, fmt.Errorf("unable to evaluate build args: %w", err) - } - var keys []string - for k := range buildArgs { + for k := range evaluatedArgs { keys = append(keys, k) } sort.Strings(keys) @@ -445,7 +438,7 @@ func GetBuildArgs(a *latest.DockerArtifact) ([]string, error) { for _, k := range keys { args = append(args, "--build-arg") - v := buildArgs[k] + v := evaluatedArgs[k] if v == nil { args = append(args, k) } else { @@ -472,30 +465,6 @@ func GetBuildArgs(a *latest.DockerArtifact) ([]string, error) { return args, nil } -// EvaluateBuildArgs evaluates templated build args. -func EvaluateBuildArgs(args map[string]*string) (map[string]*string, error) { - if args == nil { - return nil, nil - } - - evaluated := map[string]*string{} - for k, v := range args { - if v == nil { - evaluated[k] = nil - continue - } - - value, err := util.ExpandEnvTemplate(*v, nil) - if err != nil { - return nil, fmt.Errorf("unable to get value for build arg %q: %w", k, err) - } - - evaluated[k] = &value - } - - return evaluated, nil -} - func (l *localDaemon) Prune(ctx context.Context, out io.Writer, images []string, pruneChildren bool) error { for _, id := range images { resp, err := l.ImageRemove(ctx, id, types.ImageRemoveOptions{ diff --git a/pkg/skaffold/docker/image_test.go b/pkg/skaffold/docker/image_test.go index 1e130ef975e..9e362f79058 100644 --- a/pkg/skaffold/docker/image_test.go +++ b/pkg/skaffold/docker/image_test.go @@ -26,6 +26,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/client" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/testutil" @@ -103,6 +104,7 @@ func TestBuild(t *testing.T) { workspace string artifact *latest.DockerArtifact expected types.ImageBuildOptions + mode config.RunMode shouldErr bool expectedError string }{ @@ -115,6 +117,7 @@ func TestBuild(t *testing.T) { Tags: []string{"finalimage"}, AuthConfigs: allAuthConfig, }, + mode: config.RunModes.Dev, }, { description: "build with options", @@ -135,6 +138,7 @@ func TestBuild(t *testing.T) { NetworkMode: "None", NoCache: true, }, + mode: config.RunModes.Dev, expected: types.ImageBuildOptions{ Tags: []string{"finalimage"}, Dockerfile: "Dockerfile", @@ -155,6 +159,7 @@ func TestBuild(t *testing.T) { api: &testutil.FakeAPIClient{ ErrImageBuild: true, }, + mode: config.RunModes.Dev, workspace: ".", artifact: &latest.DockerArtifact{}, shouldErr: true, @@ -166,6 +171,7 @@ func TestBuild(t *testing.T) { ErrStream: true, }, workspace: ".", + mode: config.RunModes.Dev, artifact: &latest.DockerArtifact{}, shouldErr: true, expectedError: "unable to stream build output", @@ -177,6 +183,7 @@ func TestBuild(t *testing.T) { "key": util.StringPtr("{{INVALID"), }, }, + mode: config.RunModes.Dev, shouldErr: true, expectedError: `function "INVALID" not defined`, }, @@ -184,10 +191,13 @@ func TestBuild(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { t.Override(&DefaultAuthHelper, testAuthHelper{}) + t.Override(&EvalBuildArgs, func(mode config.RunMode, workspace string, a *latest.DockerArtifact) (map[string]*string, error) { + return util.EvaluateEnvTemplateMap(a.BuildArgs) + }) t.SetEnvs(test.env) localDocker := NewLocalDaemon(test.api, nil, false, nil) - _, err := localDocker.Build(context.Background(), ioutil.Discard, test.workspace, test.artifact, "finalimage") + _, err := localDocker.Build(context.Background(), ioutil.Discard, test.workspace, test.artifact, "finalimage", test.mode) if test.shouldErr { t.CheckErrorContains(test.expectedError, err) @@ -318,8 +328,13 @@ func TestGetBuildArgs(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { t.Override(&util.OSEnviron, func() []string { return test.env }) + args, err := util.EvaluateEnvTemplateMap(test.artifact.BuildArgs) + t.CheckError(test.shouldErr, err) + if test.shouldErr { + return + } - result, err := GetBuildArgs(test.artifact) + result, err := ToCLIBuildArgs(test.artifact, args) t.CheckError(test.shouldErr, err) if !test.shouldErr { diff --git a/pkg/skaffold/docker/parse.go b/pkg/skaffold/docker/parse.go index e75c3b684a2..2bb2cd71648 100644 --- a/pkg/skaffold/docker/parse.go +++ b/pkg/skaffold/docker/parse.go @@ -19,6 +19,7 @@ package docker import ( "context" "fmt" + "io" "os" "path" "path/filepath" @@ -94,8 +95,27 @@ func readCopyCmdsFromDockerfile(onlyLastImage bool, absDockerfilePath, workspace return expandSrcGlobPatterns(workspace, cpCmds) } +// filterUnusedBuildArgs removes entries from the build arguments map that are not found in the dockerfile +func filterUnusedBuildArgs(dockerFile io.Reader, buildArgs map[string]*string) (map[string]*string, error) { + res, err := parser.Parse(dockerFile) + if err != nil { + return nil, fmt.Errorf("parsing dockerfile: %w", err) + } + m := make(map[string]*string) + for _, n := range res.AST.Children { + if n.Value != command.Arg { + continue + } + k := strings.SplitN(n.Next.Value, "=", 2)[0] + if v, ok := buildArgs[k]; ok { + m[k] = v + } + } + return m, nil +} + func expandBuildArgs(nodes []*parser.Node, buildArgs map[string]*string) error { - args, err := EvaluateBuildArgs(buildArgs) + args, err := util.EvaluateEnvTemplateMap(buildArgs) if err != nil { return fmt.Errorf("unable to evaluate build args: %w", err) } diff --git a/pkg/skaffold/docker/parse_test.go b/pkg/skaffold/docker/parse_test.go index dd03e080f56..bbf58be7d09 100644 --- a/pkg/skaffold/docker/parse_test.go +++ b/pkg/skaffold/docker/parse_test.go @@ -17,8 +17,10 @@ limitations under the License. package docker import ( + "strings" "testing" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/testutil" ) @@ -30,3 +32,78 @@ func TestUnquote(t *testing.T) { testutil.CheckDeepEqual(t, `scratch`, unquote(`''scratch''`)) testutil.CheckDeepEqual(t, `'scratch'`, unquote(`"'scratch'"`)) } + +func TestRemoveExtraBuildArgs(t *testing.T) { + tests := []struct { + description string + dockerfile string + buildArgs map[string]*string + expected map[string]*string + }{ + { + description: "no args in dockerfile", + dockerfile: `FROM nginx:stable`, + buildArgs: map[string]*string{ + "foo": util.StringPtr("FOO"), + "bar": util.StringPtr("BAR"), + }, + expected: map[string]*string{}, + }, + { + description: "exact args in dockerfile", + dockerfile: `ARG foo +ARG bar +FROM nginx:stable`, + buildArgs: map[string]*string{ + "foo": util.StringPtr("FOO"), + "bar": util.StringPtr("BAR"), + }, + expected: map[string]*string{ + "foo": util.StringPtr("FOO"), + "bar": util.StringPtr("BAR"), + }, + }, + { + description: "extra build args", + dockerfile: `ARG foo +ARG bar +FROM nginx:stable`, + buildArgs: map[string]*string{ + "foo": util.StringPtr("FOO"), + "bar": util.StringPtr("BAR"), + "foobar": util.StringPtr("FOOBAR"), + "gopher": util.StringPtr("GOPHER"), + }, + expected: map[string]*string{ + "foo": util.StringPtr("FOO"), + "bar": util.StringPtr("BAR"), + }, + }, + { + description: "extra build args for multistage", + dockerfile: `ARG foo +FROM nginx:stable +ARG bar1 +FROM golang:stable +ARG bar2`, + buildArgs: map[string]*string{ + "foo": util.StringPtr("FOO"), + "bar1": util.StringPtr("BAR"), + "bar2": util.StringPtr("BAR2"), + }, + expected: map[string]*string{ + "foo": util.StringPtr("FOO"), + "bar1": util.StringPtr("BAR"), + "bar2": util.StringPtr("BAR2"), + }, + }, + } + + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + r := strings.NewReader(test.dockerfile) + got, _ := filterUnusedBuildArgs(r, test.buildArgs) + t.CheckDeepEqual(test.expected, got) + }) + } +} diff --git a/pkg/skaffold/runner/debugging.go b/pkg/skaffold/runner/debugging.go index f8d309faf36..1e8f8735e4a 100644 --- a/pkg/skaffold/runner/debugging.go +++ b/pkg/skaffold/runner/debugging.go @@ -17,11 +17,12 @@ limitations under the License. package runner import ( + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/debugging" ) func (r *SkaffoldRunner) createContainerManager() *debugging.ContainerManager { - if !r.runCtx.DebugMode() { + if r.runCtx.Mode() != config.RunModes.Debug { return nil } diff --git a/pkg/skaffold/runner/runcontext/context.go b/pkg/skaffold/runner/runcontext/context.go index 6a281d5c61e..dc2243ef0b6 100644 --- a/pkg/skaffold/runner/runcontext/context.go +++ b/pkg/skaffold/runner/runcontext/context.go @@ -58,9 +58,8 @@ func (rc *RunContext) CacheFile() string { return rc.Opt func (rc *RunContext) ConfigurationFile() string { return rc.Opts.ConfigurationFile } func (rc *RunContext) CustomLabels() []string { return rc.Opts.CustomLabels } func (rc *RunContext) CustomTag() string { return rc.Opts.CustomTag } -func (rc *RunContext) DebugMode() bool { return rc.Opts.IsDebugMode() } func (rc *RunContext) DefaultRepo() *string { return rc.Opts.DefaultRepo.Value() } -func (rc *RunContext) DevMode() bool { return rc.Opts.IsDevMode() } +func (rc *RunContext) Mode() config.RunMode { return rc.Opts.Mode() } func (rc *RunContext) DigestSource() string { return rc.Opts.DigestSource } func (rc *RunContext) DryRun() bool { return rc.Opts.DryRun } func (rc *RunContext) ForceDeploy() bool { return rc.Opts.Force } diff --git a/pkg/skaffold/util/env_template.go b/pkg/skaffold/util/env_template.go index 5fea042f685..7573cde21c5 100644 --- a/pkg/skaffold/util/env_template.go +++ b/pkg/skaffold/util/env_template.go @@ -65,3 +65,27 @@ func ExecuteEnvTemplate(envTemplate *template.Template, customMap map[string]str } return buf.String(), nil } + +// EvaluateEnvTemplateMap parses and executes all map values as templates based on OS environment variables +func EvaluateEnvTemplateMap(args map[string]*string) (map[string]*string, error) { + if args == nil { + return nil, nil + } + + evaluated := map[string]*string{} + for k, v := range args { + if v == nil { + evaluated[k] = nil + continue + } + + value, err := ExpandEnvTemplate(*v, nil) + if err != nil { + return nil, fmt.Errorf("unable to get value for key %q: %w", k, err) + } + + evaluated[k] = &value + } + + return evaluated, nil +} diff --git a/pkg/skaffold/util/util.go b/pkg/skaffold/util/util.go index 0f581fa15a2..d1554865b43 100644 --- a/pkg/skaffold/util/util.go +++ b/pkg/skaffold/util/util.go @@ -27,6 +27,7 @@ import ( "os" "path/filepath" "regexp" + "sort" "strings" "github.com/sirupsen/logrus" @@ -204,6 +205,30 @@ func Expand(text, key, value string) string { return text } +// EnvMapToSlice converts map of (string,string) to string slice +func EnvMapToSlice(m map[string]string, separator string) []string { + var sl []string + for k, v := range m { + sl = append(sl, fmt.Sprintf("%s%s%s", k, separator, v)) + } + sort.Strings(sl) + return sl +} + +// EnvPtrMapToSlice converts map of (string,*string) to string slice +func EnvPtrMapToSlice(m map[string]*string, separator string) []string { + var sl []string + for k, v := range m { + if v == nil { + sl = append(sl, k) + continue + } + sl = append(sl, fmt.Sprintf("%s%s%s", k, separator, *v)) + } + sort.Strings(sl) + return sl +} + func isAlphaNum(c uint8) bool { return c == '_' || '0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' } diff --git a/pkg/skaffold/util/util_test.go b/pkg/skaffold/util/util_test.go index c78b766806b..74cb471d862 100644 --- a/pkg/skaffold/util/util_test.go +++ b/pkg/skaffold/util/util_test.go @@ -298,6 +298,77 @@ func TestStrSliceInsert(t *testing.T) { testutil.CheckDeepEqual(t, []string{"a", "b", "c"}, StrSliceInsert([]string{"a", "b", "c"}, 1, nil)) } +func TestFormatMapToStringSlice1(t *testing.T) { + tests := []struct { + description string + args map[string]string + expected []string + }{ + { + description: "regular key:value", + args: map[string]string{ + "one": "1", + "two": "2", + }, + expected: []string{"one=1", "two=2"}, + }, { + description: "empty key:value", + args: map[string]string{ + "one": "", + "two": "", + }, + expected: []string{"one=", "two="}, + }, + } + + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + actual := EnvMapToSlice(test.args, "=") + + t.CheckDeepEqual(test.expected, actual) + }) + } +} + +func TestFormatMapToStringSlice2(t *testing.T) { + tests := []struct { + description string + args map[string]*string + expected []string + }{ + { + description: "regular key:value", + args: map[string]*string{ + "one": stringPointer("1"), + "two": stringPointer("2"), + }, + expected: []string{"one=1", "two=2"}, + }, { + description: "empty key:value", + args: map[string]*string{ + "one": stringPointer(""), + "two": stringPointer(""), + }, + expected: []string{"one=", "two="}, + }, { + description: "nil value", + args: map[string]*string{ + "one": nil, + "two": nil, + }, + expected: []string{"one", "two"}, + }, + } + + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + actual := EnvPtrMapToSlice(test.args, "=") + + t.CheckDeepEqual(test.expected, actual) + }) + } +} + func TestIsFileIsDir(t *testing.T) { tmpDir := testutil.NewTempDir(t).Touch("file") @@ -310,3 +381,7 @@ func TestIsFileIsDir(t *testing.T) { testutil.CheckDeepEqual(t, false, IsFile(filepath.Join(tmpDir.Root(), "nonexistent"))) testutil.CheckDeepEqual(t, false, IsDir(filepath.Join(tmpDir.Root(), "nonexistent"))) } + +func stringPointer(s string) *string { + return &s +} From f1086ccfababecad63b083e0fec5cb4f2a6b2887 Mon Sep 17 00:00:00 2001 From: Julien Ammous Date: Mon, 24 Aug 2020 16:40:42 +0200 Subject: [PATCH 105/138] Update log-tailing.md (#4636) Co-authored-by: Brian de Alwis --- docs/content/en/docs/pipeline-stages/log-tailing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/en/docs/pipeline-stages/log-tailing.md b/docs/content/en/docs/pipeline-stages/log-tailing.md index 89536fe9e1a..41caa7cab10 100644 --- a/docs/content/en/docs/pipeline-stages/log-tailing.md +++ b/docs/content/en/docs/pipeline-stages/log-tailing.md @@ -15,7 +15,7 @@ Log Tailing is **disabled by default** for `run` mode; it can be enabled with th ## Log Structure -To view log structure, run `skaffold run --tail` on [examples microserices](https://github.com/GoogleContainerTools/skaffold/tree/master/examples/microservices) +To view log structure, run `skaffold run --tail` in [`examples/microservices`](https://github.com/GoogleContainerTools/skaffold/tree/master/examples/microservices) ```bash skaffold run --tail From bad4b31a15fe1f694e62ea92a88865837e5069b5 Mon Sep 17 00:00:00 2001 From: Boris Dudelsack Date: Mon, 24 Aug 2020 17:47:39 +0200 Subject: [PATCH 106/138] Helm version should use semver.ParseTolerant to handle missing patch version (#4712) Signed-off-by: Boris Dudelsack --- pkg/skaffold/deploy/helm.go | 2 +- pkg/skaffold/deploy/helm_test.go | 34 +++++++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/pkg/skaffold/deploy/helm.go b/pkg/skaffold/deploy/helm.go index 927efc30f14..2bef7f09961 100644 --- a/pkg/skaffold/deploy/helm.go +++ b/pkg/skaffold/deploy/helm.go @@ -447,7 +447,7 @@ func (h *HelmDeployer) binVer(ctx context.Context) (semver.Version, error) { return semver.Version{}, fmt.Errorf("unable to parse output: %q", raw) } - v, err := semver.Make(matches[1]) + v, err := semver.ParseTolerant(matches[1]) if err != nil { return semver.Version{}, fmt.Errorf("semver make %q: %w", matches[1], err) } diff --git a/pkg/skaffold/deploy/helm_test.go b/pkg/skaffold/deploy/helm_test.go index 162ede2ce9f..0642fceea19 100644 --- a/pkg/skaffold/deploy/helm_test.go +++ b/pkg/skaffold/deploy/helm_test.go @@ -345,11 +345,13 @@ MANIFEST: var ( // Output strings to emulate different versions of Helm - version20rc = `Client: &version.Version{SemVer:"v2.0.0-rc.1", GitCommit:"92be174acf51e60a33287fb7011f4571eaa5cb98", GitTreeState:"clean"}\nError: cannot connect to Tiller\n` - version21 = `Client: &version.Version{SemVer:"v2.15.1", GitCommit:"cf1de4f8ba70eded310918a8af3a96bfe8e7683b", GitTreeState:"clean"}\nServer: &version.Version{SemVer:"v2.16.1", GitCommit:"bbdfe5e7803a12bbdf97e94cd847859890cf4050", GitTreeState:"clean"}\n` - version30b = `version.BuildInfo{Version:"v3.0.0-beta.3", GitCommit:"5cb923eecbe80d1ad76399aee234717c11931d9a", GitTreeState:"clean", GoVersion:"go1.12.9"}` - version30 = `version.BuildInfo{Version:"v3.0.0", GitCommit:"e29ce2a54e96cd02ccfce88bee4f58bb6e2a28b6", GitTreeState:"clean", GoVersion:"go1.13.4"}` - version31 = `version.BuildInfo{Version:"v3.1.1", GitCommit:"afe70585407b420d0097d07b21c47dc511525ac8", GitTreeState:"clean", GoVersion:"go1.13.8"}` + version20rc = `Client: &version.Version{SemVer:"v2.0.0-rc.1", GitCommit:"92be174acf51e60a33287fb7011f4571eaa5cb98", GitTreeState:"clean"}\nError: cannot connect to Tiller\n` + version21 = `Client: &version.Version{SemVer:"v2.15.1", GitCommit:"cf1de4f8ba70eded310918a8af3a96bfe8e7683b", GitTreeState:"clean"}\nServer: &version.Version{SemVer:"v2.16.1", GitCommit:"bbdfe5e7803a12bbdf97e94cd847859890cf4050", GitTreeState:"clean"}\n` + version30b = `version.BuildInfo{Version:"v3.0.0-beta.3", GitCommit:"5cb923eecbe80d1ad76399aee234717c11931d9a", GitTreeState:"clean", GoVersion:"go1.12.9"}` + version30 = `version.BuildInfo{Version:"v3.0.0", GitCommit:"e29ce2a54e96cd02ccfce88bee4f58bb6e2a28b6", GitTreeState:"clean", GoVersion:"go1.13.4"}` + version31 = `version.BuildInfo{Version:"v3.1.1", GitCommit:"afe70585407b420d0097d07b21c47dc511525ac8", GitTreeState:"clean", GoVersion:"go1.13.8"}` + version33 = `version.BuildInfo{Version:"v3.3.0", GitCommit:"8a4aeec08d67a7b84472007529e8097ec3742105", GitTreeState:"dirty", GoVersion:"go1.14.7"}` + version33NonSemver = `version.BuildInfo{Version:"v3.3", GitCommit:"", GitTreeState:"", GoVersion:"go1.15"}` ) func TestHelmDeploy(t *testing.T) { @@ -494,6 +496,28 @@ func TestHelmDeploy(t *testing.T) { builds: testBuilds, shouldErr: true, }, + { + description: "helm3.3 version deploy success", + commands: testutil. + CmdRunWithOutput("helm version --client", version33). + AndRun("helm --kube-context kubecontext get all skaffold-helm --kubeconfig kubeconfig"). + AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). + AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). + AndRun("helm --kube-context kubecontext get all skaffold-helm --kubeconfig kubeconfig"), + runContext: makeRunContext(testDeployConfig, false), + builds: testBuilds, + }, + { + description: "helm3.3 non semver compliant version deploy success", + commands: testutil. + CmdRunWithOutput("helm version --client", version33NonSemver). + AndRun("helm --kube-context kubecontext get all skaffold-helm --kubeconfig kubeconfig"). + AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). + AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). + AndRun("helm --kube-context kubecontext get all skaffold-helm --kubeconfig kubeconfig"), + runContext: makeRunContext(testDeployConfig, false), + builds: testBuilds, + }, { description: "deploy success with recreatePods", commands: testutil. From 6d9e671a26471fb81f1eaacb4688ebb1c53adc6d Mon Sep 17 00:00:00 2001 From: Brian de Alwis Date: Mon, 24 Aug 2020 21:52:40 -0400 Subject: [PATCH 107/138] Add explicit tests for Helm version parsing (#4715) --- pkg/skaffold/deploy/helm_test.go | 62 +++++++++++++++++--------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/pkg/skaffold/deploy/helm_test.go b/pkg/skaffold/deploy/helm_test.go index 0642fceea19..cc68c01158f 100644 --- a/pkg/skaffold/deploy/helm_test.go +++ b/pkg/skaffold/deploy/helm_test.go @@ -345,15 +345,41 @@ MANIFEST: var ( // Output strings to emulate different versions of Helm - version20rc = `Client: &version.Version{SemVer:"v2.0.0-rc.1", GitCommit:"92be174acf51e60a33287fb7011f4571eaa5cb98", GitTreeState:"clean"}\nError: cannot connect to Tiller\n` - version21 = `Client: &version.Version{SemVer:"v2.15.1", GitCommit:"cf1de4f8ba70eded310918a8af3a96bfe8e7683b", GitTreeState:"clean"}\nServer: &version.Version{SemVer:"v2.16.1", GitCommit:"bbdfe5e7803a12bbdf97e94cd847859890cf4050", GitTreeState:"clean"}\n` - version30b = `version.BuildInfo{Version:"v3.0.0-beta.3", GitCommit:"5cb923eecbe80d1ad76399aee234717c11931d9a", GitTreeState:"clean", GoVersion:"go1.12.9"}` - version30 = `version.BuildInfo{Version:"v3.0.0", GitCommit:"e29ce2a54e96cd02ccfce88bee4f58bb6e2a28b6", GitTreeState:"clean", GoVersion:"go1.13.4"}` - version31 = `version.BuildInfo{Version:"v3.1.1", GitCommit:"afe70585407b420d0097d07b21c47dc511525ac8", GitTreeState:"clean", GoVersion:"go1.13.8"}` - version33 = `version.BuildInfo{Version:"v3.3.0", GitCommit:"8a4aeec08d67a7b84472007529e8097ec3742105", GitTreeState:"dirty", GoVersion:"go1.14.7"}` - version33NonSemver = `version.BuildInfo{Version:"v3.3", GitCommit:"", GitTreeState:"", GoVersion:"go1.15"}` + version20rc = `Client: &version.Version{SemVer:"v2.0.0-rc.1", GitCommit:"92be174acf51e60a33287fb7011f4571eaa5cb98", GitTreeState:"clean"}\nError: cannot connect to Tiller\n` + version21 = `Client: &version.Version{SemVer:"v2.15.1", GitCommit:"cf1de4f8ba70eded310918a8af3a96bfe8e7683b", GitTreeState:"clean"}\nServer: &version.Version{SemVer:"v2.16.1", GitCommit:"bbdfe5e7803a12bbdf97e94cd847859890cf4050", GitTreeState:"clean"}\n` + version30b = `version.BuildInfo{Version:"v3.0.0-beta.3", GitCommit:"5cb923eecbe80d1ad76399aee234717c11931d9a", GitTreeState:"clean", GoVersion:"go1.12.9"}` + version30 = `version.BuildInfo{Version:"v3.0.0", GitCommit:"e29ce2a54e96cd02ccfce88bee4f58bb6e2a28b6", GitTreeState:"clean", GoVersion:"go1.13.4"}` + version31 = `version.BuildInfo{Version:"v3.1.1", GitCommit:"afe70585407b420d0097d07b21c47dc511525ac8", GitTreeState:"clean", GoVersion:"go1.13.8"}` ) +func TestBinVer(t *testing.T) { + tests := []struct { + description string + helmVersion string + expected string + shouldErr bool + }{ + {"Helm 2.0RC1", version20rc, "2.0.0-rc.1", false}, + {"Helm 2.15.1", version21, "2.15.1", false}, + {"Helm 3.0b3", version30b, "3.0.0-beta.3", false}, + {"Helm 3.0", version30, "3.0.0", false}, + {"Helm 3.1.1", version31, "3.1.1", false}, + {"Custom Helm 3.3 build from Manjaro", "v3.3", "3.3.0", false}, // not semver compliant + {"Invalid", "3.1.0", "0.0.0", true}, + } + + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + t.Override(&util.DefaultExecCommand, testutil.CmdRunWithOutput("helm version --client", test.helmVersion)) + + deployer := NewHelmDeployer(makeRunContext(testDeployConfig, false), nil) + ver, err := deployer.binVer(context.TODO()) + + t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expected, ver.String()) + }) + } +} + func TestHelmDeploy(t *testing.T) { tmpDir, err := ioutil.TempDir("", "TestHelmDeploy") if err != nil { @@ -496,28 +522,6 @@ func TestHelmDeploy(t *testing.T) { builds: testBuilds, shouldErr: true, }, - { - description: "helm3.3 version deploy success", - commands: testutil. - CmdRunWithOutput("helm version --client", version33). - AndRun("helm --kube-context kubecontext get all skaffold-helm --kubeconfig kubeconfig"). - AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). - AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). - AndRun("helm --kube-context kubecontext get all skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployConfig, false), - builds: testBuilds, - }, - { - description: "helm3.3 non semver compliant version deploy success", - commands: testutil. - CmdRunWithOutput("helm version --client", version33NonSemver). - AndRun("helm --kube-context kubecontext get all skaffold-helm --kubeconfig kubeconfig"). - AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). - AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). - AndRun("helm --kube-context kubecontext get all skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployConfig, false), - builds: testBuilds, - }, { description: "deploy success with recreatePods", commands: testutil. From fe0bb376b11c6858bbadfb7fe6f3cec9d38b9c67 Mon Sep 17 00:00:00 2001 From: Nick Kubala Date: Mon, 24 Aug 2020 20:06:55 -0700 Subject: [PATCH 108/138] drop codecov patch threshold to 40% (#4716) --- codecov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codecov.yml b/codecov.yml index f30de016fa5..e5455ff9168 100644 --- a/codecov.yml +++ b/codecov.yml @@ -5,4 +5,4 @@ coverage: threshold: 10% patch: default: - target: 50% + target: 40% From 241e202e49126484d6502bf3924cbdfc34bb0b9a Mon Sep 17 00:00:00 2001 From: MrLuje Date: Tue, 25 Aug 2020 23:07:37 +0200 Subject: [PATCH 109/138] Add .NET Core container debugging support (#4699) --- docs/content/en/docs/workflows/debug.md | 48 +++++++ integration/debug_test.go | 6 +- integration/testdata/debug/.gitignore | 2 + integration/testdata/debug/kustomization.yaml | 1 + integration/testdata/debug/netcore/Dockerfile | 23 +++ .../testdata/debug/netcore/k8s/pod.yaml | 26 ++++ .../HelloWorld/Controllers/HomeController.cs | 15 ++ .../netcore/src/HelloWorld/HelloWorld.csproj | 7 + .../debug/netcore/src/HelloWorld/Program.cs | 26 ++++ .../debug/netcore/src/HelloWorld/Startup.cs | 39 ++++++ .../netcore/src/HelloWorld/appsettings.json | 10 ++ integration/testdata/debug/skaffold.yaml | 7 + pkg/skaffold/debug/transform.go | 2 +- pkg/skaffold/debug/transform_netcore.go | 76 ++++++++++ pkg/skaffold/debug/transform_netcore_test.go | 131 ++++++++++++++++++ 15 files changed, 415 insertions(+), 4 deletions(-) create mode 100644 integration/testdata/debug/netcore/Dockerfile create mode 100644 integration/testdata/debug/netcore/k8s/pod.yaml create mode 100644 integration/testdata/debug/netcore/src/HelloWorld/Controllers/HomeController.cs create mode 100644 integration/testdata/debug/netcore/src/HelloWorld/HelloWorld.csproj create mode 100644 integration/testdata/debug/netcore/src/HelloWorld/Program.cs create mode 100644 integration/testdata/debug/netcore/src/HelloWorld/Startup.cs create mode 100644 integration/testdata/debug/netcore/src/HelloWorld/appsettings.json create mode 100644 pkg/skaffold/debug/transform_netcore.go create mode 100644 pkg/skaffold/debug/transform_netcore_test.go diff --git a/docs/content/en/docs/workflows/debug.md b/docs/content/en/docs/workflows/debug.md index 1495a0aacfb..497be5e7b53 100644 --- a/docs/content/en/docs/workflows/debug.md +++ b/docs/content/en/docs/workflows/debug.md @@ -30,6 +30,7 @@ Debugging is currently supported for: - NodeJS (runtime ID: `nodejs`) - Java and JVM languages (runtime ID: `jvm`) - Python (runtime ID: `python`) + - .NET Core (runtime ID: `netcore`) Note that many debuggers may require additional information for the location of source files. We are looking for ways to identify this information and to pass it back if found. @@ -101,6 +102,53 @@ The DAP is supported by Visual Studio Code, [Eclipse LSP4e](https://projects.ecl [and other editors and IDEs](https://microsoft.github.io/debug-adapter-protocol/implementors/tools/). DAP is not yet supported by JetBrains IDEs like PyCharm. +#### .NET Core + +.NET Core applications are configured to be deployed along with `vsdbg`. + +In order to configure your application for debugging, your app must be: + +- Identified as being dotnet-based by having an entrypoint using [dotnet](https://github.com/dotnet/sdk) cli + or one of the following environment variables `ASPNETCORE_URLS`, `DOTNET_RUNNING_IN_CONTAINER`, + `DOTNET_SYSTEM_GLOBALIZATION_INVARIANT`. +- Built with the `--configuration Debug` options to disable optimizations. + +**Note for users of [VS Code's debug adapter for C#](https://github.com/OmniSharp/omnisharp-vscode):** +the following configuration can be used to debug a container. It assumes that your code is deployed +in `/app` or `/src` folder in the container. If that is not the case, the `sourceFileMap` property +should be changed to match the correct folder. `processId` is usually 1 but might be different if you +have an unusual entrypoint. You can also use `"${command:pickRemoteProcess}"` instead if supported by +your base image. (`//` comments must be stripped.) +```json +{ + "name": "Skaffold Debug", + "type": "coreclr", + "request": "attach", + "processId" : 1, + "justMyCode": true, // set to `true` in debug configuration and `false` in release configuration + "pipeTransport": { + "pipeProgram": "kubectl", + "pipeArgs": [ + "exec", + "-i", + "", // name of the pod you debug. + "--" + ], + "pipeCwd": "${workspaceFolder}", + "debuggerPath": "/dbg/netcore/vsdbg", // location where vsdbg binary installed. + "quoteArgs": false + }, + "sourceFileMap": { + // Change this mapping if your app in not deployed in /src or /app in your docker image + "/src": "${workspaceFolder}", + "/app": "${workspaceFolder}" + // May also be like this, depending of your repository layout + // "/src": "${workspaceFolder}/src", + // "/app": "${workspaceFolder}/src/" + } +} +``` + ## IDE Support via Events and Metadata `debug` provides additional support for IDEs to detect the debuggable containers and to determine diff --git a/integration/debug_test.go b/integration/debug_test.go index c38923a9a7d..66b4d55e615 100644 --- a/integration/debug_test.go +++ b/integration/debug_test.go @@ -39,19 +39,19 @@ func TestDebug(t *testing.T) { { description: "kubectl", deployments: []string{"java"}, - pods: []string{"nodejs", "npm", "python3", "go"}, + pods: []string{"nodejs", "npm", "python3", "go", "netcore"}, }, { description: "kustomize", args: []string{"--profile", "kustomize"}, deployments: []string{"java"}, - pods: []string{"nodejs", "npm", "python3", "go"}, + pods: []string{"nodejs", "npm", "python3", "go", "netcore"}, }, { description: "buildpacks", args: []string{"--profile", "buildpacks"}, deployments: []string{"java"}, - pods: []string{"nodejs", "npm", "python3", "go"}, + pods: []string{"nodejs", "npm", "python3", "go", "netcore"}, }, } for _, test := range tests { diff --git a/integration/testdata/debug/.gitignore b/integration/testdata/debug/.gitignore index 1bd722694bd..fa1b78aea1e 100644 --- a/integration/testdata/debug/.gitignore +++ b/integration/testdata/debug/.gitignore @@ -1,2 +1,4 @@ node_modules *.swp +netcore/**/obj +netcore/**/bin \ No newline at end of file diff --git a/integration/testdata/debug/kustomization.yaml b/integration/testdata/debug/kustomization.yaml index 25c3745769a..120281340b4 100644 --- a/integration/testdata/debug/kustomization.yaml +++ b/integration/testdata/debug/kustomization.yaml @@ -4,3 +4,4 @@ resources: - npm/k8s/pod.yaml - python3/k8s/pod.yaml - go/k8s/pod.yaml + - netcore/k8s/pod.yaml diff --git a/integration/testdata/debug/netcore/Dockerfile b/integration/testdata/debug/netcore/Dockerfile new file mode 100644 index 00000000000..2d523b84c65 --- /dev/null +++ b/integration/testdata/debug/netcore/Dockerfile @@ -0,0 +1,23 @@ +# See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile +# to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base +WORKDIR /app +EXPOSE 80 +EXPOSE 443 + +FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build +COPY ["src/HelloWorld/HelloWorld.csproj", "src/HelloWorld/"] +RUN dotnet restore "src/HelloWorld/HelloWorld.csproj" +COPY . . +WORKDIR "/src/HelloWorld" +RUN ls -al +RUN dotnet build "HelloWorld.csproj" --configuration Debug -o /app/build + +FROM build AS publish +RUN dotnet publish "HelloWorld.csproj" --configuration Debug -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "HelloWorld.dll"] \ No newline at end of file diff --git a/integration/testdata/debug/netcore/k8s/pod.yaml b/integration/testdata/debug/netcore/k8s/pod.yaml new file mode 100644 index 00000000000..d5e06c872f5 --- /dev/null +++ b/integration/testdata/debug/netcore/k8s/pod.yaml @@ -0,0 +1,26 @@ +apiVersion: v1 +kind: Pod +metadata: + name: netcore +spec: + containers: + - name: dotnet-web + image: skaffold-debug-netcore + ports: + - name: http + containerPort: 80 + protocol: TCP + livenessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 5 + failureThreshold: 4 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 5 + failureThreshold: 30 + timeoutSeconds: 5 \ No newline at end of file diff --git a/integration/testdata/debug/netcore/src/HelloWorld/Controllers/HomeController.cs b/integration/testdata/debug/netcore/src/HelloWorld/Controllers/HomeController.cs new file mode 100644 index 00000000000..9dca6c561f7 --- /dev/null +++ b/integration/testdata/debug/netcore/src/HelloWorld/Controllers/HomeController.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.Mvc; + +namespace HelloWorld.Controllers +{ + [ApiController] + [Route("/")] + public class HomeController : ControllerBase + { + [HttpGet] + public string Get() + { + return "Ok"; + } + } +} diff --git a/integration/testdata/debug/netcore/src/HelloWorld/HelloWorld.csproj b/integration/testdata/debug/netcore/src/HelloWorld/HelloWorld.csproj new file mode 100644 index 00000000000..40e2b902fae --- /dev/null +++ b/integration/testdata/debug/netcore/src/HelloWorld/HelloWorld.csproj @@ -0,0 +1,7 @@ + + + + netcoreapp3.1 + + + diff --git a/integration/testdata/debug/netcore/src/HelloWorld/Program.cs b/integration/testdata/debug/netcore/src/HelloWorld/Program.cs new file mode 100644 index 00000000000..7a863520d22 --- /dev/null +++ b/integration/testdata/debug/netcore/src/HelloWorld/Program.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace HelloWorld +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/integration/testdata/debug/netcore/src/HelloWorld/Startup.cs b/integration/testdata/debug/netcore/src/HelloWorld/Startup.cs new file mode 100644 index 00000000000..d9b2e14709d --- /dev/null +++ b/integration/testdata/debug/netcore/src/HelloWorld/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace HelloWorld +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/integration/testdata/debug/netcore/src/HelloWorld/appsettings.json b/integration/testdata/debug/netcore/src/HelloWorld/appsettings.json new file mode 100644 index 00000000000..81ff877711d --- /dev/null +++ b/integration/testdata/debug/netcore/src/HelloWorld/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/integration/testdata/debug/skaffold.yaml b/integration/testdata/debug/skaffold.yaml index 3ad84918c42..e92943bd234 100644 --- a/integration/testdata/debug/skaffold.yaml +++ b/integration/testdata/debug/skaffold.yaml @@ -15,6 +15,8 @@ build: context: python3 - image: skaffold-debug-go context: go + - image: skaffold-debug-netcore + context: netcore deploy: kubectl: @@ -24,6 +26,7 @@ deploy: - npm/k8s/pod.yaml - python3/k8s/pod.yaml - go/k8s/pod.yaml + - netcore/k8s/pod.yaml profiles: - name: kustomize @@ -54,3 +57,7 @@ profiles: context: go buildpacks: builder: "gcr.io/buildpacks/builder:v1" + - image: skaffold-debug-netcore + context: netcore + buildpacks: + builder: "gcr.io/buildpacks/builder:v1" diff --git a/pkg/skaffold/debug/transform.go b/pkg/skaffold/debug/transform.go index a61ad7b8128..52e60776811 100644 --- a/pkg/skaffold/debug/transform.go +++ b/pkg/skaffold/debug/transform.go @@ -71,7 +71,7 @@ import ( type ContainerDebugConfiguration struct { // Artifact is the corresponding artifact's image name used in the skaffold.yaml Artifact string `json:"artifact,omitempty"` - // Runtime represents the underlying language runtime (`go`, `jvm`, `nodejs`, `python`) + // Runtime represents the underlying language runtime (`go`, `jvm`, `nodejs`, `python`, `netcore`) Runtime string `json:"runtime,omitempty"` // WorkingDir is the working directory in the image configuration; may be empty WorkingDir string `json:"workingDir,omitempty"` diff --git a/pkg/skaffold/debug/transform_netcore.go b/pkg/skaffold/debug/transform_netcore.go new file mode 100644 index 00000000000..871affec5d8 --- /dev/null +++ b/pkg/skaffold/debug/transform_netcore.go @@ -0,0 +1,76 @@ +/* +Copyright 2020 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 debug + +import ( + "strings" + + "github.com/sirupsen/logrus" + v1 "k8s.io/api/core/v1" +) + +type netcoreTransformer struct{} + +func init() { + containerTransforms = append(containerTransforms, netcoreTransformer{}) +} + +// isLaunchingNetcore determines if the arguments seems to be invoking dotnet +func isLaunchingNetcore(args []string) bool { + if len(args) < 2 { + return false + } + + if args[0] == "dotnet" || strings.HasSuffix(args[0], "/dotnet") { + return true + } + + if args[0] == "exec" && (args[1] == "dotnet" || strings.HasSuffix(args[1], "/dotnet")) { + return true + } + + return false +} + +func (t netcoreTransformer) IsApplicable(config imageConfiguration) bool { + // Some official base images (eg: dotnet/core/runtime-deps) contain the following env vars + for _, v := range []string{"ASPNETCORE_URLS", "DOTNET_RUNNING_IN_CONTAINER", "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT"} { + if _, found := config.env[v]; found { + return true + } + } + + if len(config.entrypoint) > 0 && !isEntrypointLauncher(config.entrypoint) { + return isLaunchingNetcore(config.entrypoint) + } + + if len(config.arguments) > 0 { + return isLaunchingNetcore(config.arguments) + } + + return false +} + +// Apply configures a container definition for vsdbg. +// Returns a simple map describing the debug configuration details. +func (t netcoreTransformer) Apply(container *v1.Container, config imageConfiguration, portAlloc portAllocator) (ContainerDebugConfiguration, string, error) { + logrus.Infof("Configuring %q for netcore debugging", container.Name) + + return ContainerDebugConfiguration{ + Runtime: "netcore", + }, "netcore", nil +} diff --git a/pkg/skaffold/debug/transform_netcore_test.go b/pkg/skaffold/debug/transform_netcore_test.go new file mode 100644 index 00000000000..fb95bee766f --- /dev/null +++ b/pkg/skaffold/debug/transform_netcore_test.go @@ -0,0 +1,131 @@ +/* +Copyright 2020 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 debug + +import ( + "testing" + + v1 "k8s.io/api/core/v1" + + "github.com/GoogleContainerTools/skaffold/testutil" +) + +func TestNetcoreTransformer_IsApplicable(t *testing.T) { + tests := []struct { + description string + source imageConfiguration + launcher string + result bool + }{ + { + description: "ASPNETCORE_URLS", + source: imageConfiguration{env: map[string]string{"ASPNETCORE_URLS": "http://+:80"}}, + result: true, + }, + { + description: "DOTNET_RUNNING_IN_CONTAINER", + source: imageConfiguration{env: map[string]string{"DOTNET_RUNNING_IN_CONTAINER": "true"}}, + result: true, + }, + { + description: "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", + source: imageConfiguration{env: map[string]string{"DOTNET_SYSTEM_GLOBALIZATION_INVARIANT": "true"}}, + result: true, + }, + { + description: "entrypoint with dotnet", + source: imageConfiguration{entrypoint: []string{"dotnet", "myapp.dll"}}, + result: true, + }, + { + description: "entrypoint /bin/sh", + source: imageConfiguration{entrypoint: []string{"/bin/sh"}}, + result: false, + }, + { + description: "launcher entrypoint exec", + source: imageConfiguration{entrypoint: []string{"launcher"}, arguments: []string{"exec", "dotnet", "myapp.dll"}}, + launcher: "launcher", + result: true, + }, + { + description: "launcher entrypoint and random dotnet string", + source: imageConfiguration{entrypoint: []string{"launcher"}, arguments: []string{"echo", "dotnet"}}, + launcher: "launcher", + result: false, + }, + { + description: "nothing", + source: imageConfiguration{}, + result: false, + }, + } + + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + t.Override(&entrypointLaunchers, []string{test.launcher}) + result := netcoreTransformer{}.IsApplicable(test.source) + + t.CheckDeepEqual(test.result, result) + }) + } +} + +func TestNetcoreTransformerApply(t *testing.T) { + tests := []struct { + description string + containerSpec v1.Container + configuration imageConfiguration + shouldErr bool + result v1.Container + debugConfig ContainerDebugConfiguration + image string + }{ + { + description: "empty", + containerSpec: v1.Container{}, + configuration: imageConfiguration{}, + + debugConfig: ContainerDebugConfiguration{Runtime: "netcore"}, + image: "netcore", + shouldErr: false, + }, + { + description: "basic", + containerSpec: v1.Container{}, + configuration: imageConfiguration{entrypoint: []string{"dotnet", "myapp.dll"}}, + + result: v1.Container{}, + debugConfig: ContainerDebugConfiguration{Runtime: "netcore"}, + image: "netcore", + shouldErr: false, + }, + } + var identity portAllocator = func(port int32) int32 { + return port + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + config, image, err := netcoreTransformer{}.Apply(&test.containerSpec, test.configuration, identity) + + t.CheckError(test.shouldErr, err) + t.CheckDeepEqual(test.result, test.containerSpec) + t.CheckDeepEqual(test.debugConfig, config) + t.CheckDeepEqual(test.image, image) + }) + } +} From 27a2d2518afd9a231f2e483e9d20f7f4f2d8ab20 Mon Sep 17 00:00:00 2001 From: Gaurav <39389231+gsquared94@users.noreply.github.com> Date: Thu, 27 Aug 2020 00:46:37 +0530 Subject: [PATCH 110/138] Move kubernetes client into it's own package (#4720) --- integration/util.go | 4 ++-- pkg/skaffold/build/cluster/kaniko.go | 3 ++- pkg/skaffold/build/cluster/secret.go | 6 +++--- pkg/skaffold/build/cluster/secret_test.go | 10 +++++----- pkg/skaffold/deploy/labels.go | 6 +++--- pkg/skaffold/deploy/labels_test.go | 6 +++--- pkg/skaffold/deploy/status_check.go | 4 ++-- pkg/skaffold/kubernetes/{ => client}/client.go | 2 +- pkg/skaffold/kubernetes/owner.go | 4 +++- pkg/skaffold/kubernetes/owner_test.go | 5 +++-- .../kubernetes/portforward/kubectl_forwarder.go | 4 ++-- .../kubernetes/portforward/kubectl_forwarder_test.go | 4 ++-- .../kubernetes/portforward/resource_forwarder.go | 4 ++-- .../kubernetes/portforward/resource_forwarder_test.go | 4 ++-- pkg/skaffold/kubernetes/watcher.go | 4 +++- pkg/skaffold/kubernetes/watcher_test.go | 7 ++++--- pkg/skaffold/runner/build_deploy_test.go | 4 ++-- pkg/skaffold/runner/deploy.go | 4 ++-- pkg/skaffold/runner/deploy_test.go | 6 +++--- pkg/skaffold/runner/dev_test.go | 8 ++++---- pkg/skaffold/sync/sync.go | 4 ++-- pkg/skaffold/sync/sync_test.go | 4 ++-- pkg/webhook/kubernetes/deployment.go | 5 +++-- pkg/webhook/kubernetes/service.go | 6 +++--- 24 files changed, 63 insertions(+), 55 deletions(-) rename pkg/skaffold/kubernetes/{ => client}/client.go (98%) diff --git a/integration/util.go b/integration/util.go index ba010c141e4..4e97bd0dafa 100644 --- a/integration/util.go +++ b/integration/util.go @@ -37,7 +37,7 @@ import ( corev1 "k8s.io/client-go/kubernetes/typed/core/v1" "github.com/GoogleContainerTools/skaffold/integration/binpack" - pkgkubernetes "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" k8s "github.com/GoogleContainerTools/skaffold/pkg/webhook/kubernetes" ) @@ -100,7 +100,7 @@ func Run(t *testing.T, dir, command string, args ...string) { // SetupNamespace creates a Kubernetes namespace to run a test. func SetupNamespace(t *testing.T) (*v1.Namespace, *NSKubernetesClient) { - client, err := pkgkubernetes.Client() + client, err := kubernetesclient.Client() if err != nil { t.Fatalf("Test setup error: getting Kubernetes client: %s", err) } diff --git a/pkg/skaffold/build/cluster/kaniko.go b/pkg/skaffold/build/cluster/kaniko.go index 82e51869150..ec1c63869ae 100644 --- a/pkg/skaffold/build/cluster/kaniko.go +++ b/pkg/skaffold/build/cluster/kaniko.go @@ -29,13 +29,14 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" ) const initContainer = "kaniko-init-container" func (b *Builder) buildWithKaniko(ctx context.Context, out io.Writer, workspace string, artifact *latest.KanikoArtifact, tag string) (string, error) { - client, err := kubernetes.Client() + client, err := kubernetesclient.Client() if err != nil { return "", fmt.Errorf("getting Kubernetes client: %w", err) } diff --git a/pkg/skaffold/build/cluster/secret.go b/pkg/skaffold/build/cluster/secret.go index 906bfb40ee0..098d990c147 100644 --- a/pkg/skaffold/build/cluster/secret.go +++ b/pkg/skaffold/build/cluster/secret.go @@ -28,7 +28,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/color" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" ) const ( @@ -41,7 +41,7 @@ func (b *Builder) setupPullSecret(out io.Writer) (func(), error) { } color.Default.Fprintf(out, "Checking for kaniko secret [%s/%s]...\n", b.Namespace, b.PullSecretName) - client, err := kubernetes.Client() + client, err := kubernetesclient.Client() if err != nil { return nil, fmt.Errorf("getting Kubernetes client: %w", err) } @@ -96,7 +96,7 @@ func (b *Builder) setupDockerConfigSecret(out io.Writer) (func(), error) { color.Default.Fprintf(out, "Creating docker config secret [%s]...\n", b.DockerConfig.SecretName) - client, err := kubernetes.Client() + client, err := kubernetesclient.Client() if err != nil { return nil, fmt.Errorf("getting Kubernetes client: %w", err) } diff --git a/pkg/skaffold/build/cluster/secret_test.go b/pkg/skaffold/build/cluster/secret_test.go index 2573e06caba..8e5baac13a0 100644 --- a/pkg/skaffold/build/cluster/secret_test.go +++ b/pkg/skaffold/build/cluster/secret_test.go @@ -25,7 +25,7 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" - pkgkubernetes "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/testutil" @@ -35,7 +35,7 @@ func TestCreateSecret(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { tmpDir := t.NewTempDir().Touch("secret.json") fakeKubernetesclient := fake.NewSimpleClientset() - t.Override(&pkgkubernetes.Client, func() (kubernetes.Interface, error) { + t.Override(&client.Client, func() (kubernetes.Interface, error) { return fakeKubernetesclient, nil }) @@ -74,7 +74,7 @@ func TestCreateSecret(t *testing.T) { func TestExistingSecretNotFound(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { - t.Override(&pkgkubernetes.Client, func() (kubernetes.Interface, error) { + t.Override(&client.Client, func() (kubernetes.Interface, error) { return fake.NewSimpleClientset(), nil }) @@ -101,7 +101,7 @@ func TestExistingSecretNotFound(t *testing.T) { func TestExistingSecret(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { - t.Override(&pkgkubernetes.Client, func() (kubernetes.Interface, error) { + t.Override(&client.Client, func() (kubernetes.Interface, error) { return fake.NewSimpleClientset(&v1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "kaniko-secret", @@ -133,7 +133,7 @@ func TestExistingSecret(t *testing.T) { func TestSkipSecretCreation(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { - t.Override(&pkgkubernetes.Client, func() (kubernetes.Interface, error) { + t.Override(&client.Client, func() (kubernetes.Interface, error) { return nil, nil }) diff --git a/pkg/skaffold/deploy/labels.go b/pkg/skaffold/deploy/labels.go index 43a531947e0..d29f73d6db7 100644 --- a/pkg/skaffold/deploy/labels.go +++ b/pkg/skaffold/deploy/labels.go @@ -31,7 +31,7 @@ import ( "k8s.io/client-go/discovery" "k8s.io/client-go/dynamic" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" kubectx "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/context" ) @@ -53,12 +53,12 @@ func labelDeployResults(labels map[string]string, results []Artifact) error { } // use the kubectl client to update all k8s objects with a skaffold watermark - dynClient, err := kubernetes.DynamicClient() + dynClient, err := kubernetesclient.DynamicClient() if err != nil { return fmt.Errorf("error getting Kubernetes dynamic client: %w", err) } - client, err := kubernetes.Client() + client, err := kubernetesclient.Client() if err != nil { return fmt.Errorf("error getting Kubernetes client: %w", err) } diff --git a/pkg/skaffold/deploy/labels_test.go b/pkg/skaffold/deploy/labels_test.go index 756d6c1c0a4..efe8927efd8 100644 --- a/pkg/skaffold/deploy/labels_test.go +++ b/pkg/skaffold/deploy/labels_test.go @@ -28,7 +28,7 @@ import ( fakeclient "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/scheme" - k8s "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/testutil" ) @@ -101,9 +101,9 @@ func TestLabelDeployResults(t *testing.T) { Name: "deployments", }}, }) - t.Override(&k8s.Client, mockClient(client)) + t.Override(&kubernetesclient.Client, mockClient(client)) dynClient := fakedynclient.NewSimpleDynamicClient(scheme.Scheme, dep) - t.Override(&k8s.DynamicClient, mockDynamicClient(dynClient)) + t.Override(&kubernetesclient.DynamicClient, mockDynamicClient(dynClient)) // Patch labels labelDeployResults(test.appliedLabels, []Artifact{{Obj: dep}}) diff --git a/pkg/skaffold/deploy/status_check.go b/pkg/skaffold/deploy/status_check.go index dd7c95bbf3b..6e600b04453 100644 --- a/pkg/skaffold/deploy/status_check.go +++ b/pkg/skaffold/deploy/status_check.go @@ -33,7 +33,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/diag/validator" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/resource" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" - pkgkubernetes "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/proto" ) @@ -67,7 +67,7 @@ func StatusCheck(ctx context.Context, defaultLabeller *DefaultLabeller, runCtx * } func statusCheck(ctx context.Context, defaultLabeller *DefaultLabeller, runCtx *runcontext.RunContext, out io.Writer) (proto.StatusCode, error) { - client, err := pkgkubernetes.Client() + client, err := kubernetesclient.Client() if err != nil { return proto.StatusCode_STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR, fmt.Errorf("getting Kubernetes client: %w", err) } diff --git a/pkg/skaffold/kubernetes/client.go b/pkg/skaffold/kubernetes/client/client.go similarity index 98% rename from pkg/skaffold/kubernetes/client.go rename to pkg/skaffold/kubernetes/client/client.go index 7ecb103ceee..5d0c4a17471 100644 --- a/pkg/skaffold/kubernetes/client.go +++ b/pkg/skaffold/kubernetes/client/client.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubernetes +package client import ( "fmt" diff --git a/pkg/skaffold/kubernetes/owner.go b/pkg/skaffold/kubernetes/owner.go index 60fc7866e3f..a33a993621c 100644 --- a/pkg/skaffold/kubernetes/owner.go +++ b/pkg/skaffold/kubernetes/owner.go @@ -21,6 +21,8 @@ import ( "github.com/sirupsen/logrus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" ) // TopLevelOwnerKey returns a key associated with the top level @@ -42,7 +44,7 @@ func TopLevelOwnerKey(obj metav1.Object, kind string) string { } func ownerMetaObject(ns string, owner metav1.OwnerReference) (metav1.Object, error) { - client, err := Client() + client, err := kubernetesclient.Client() if err != nil { return nil, err } diff --git a/pkg/skaffold/kubernetes/owner_test.go b/pkg/skaffold/kubernetes/owner_test.go index e234b7868bc..3d20eafd682 100644 --- a/pkg/skaffold/kubernetes/owner_test.go +++ b/pkg/skaffold/kubernetes/owner_test.go @@ -28,6 +28,7 @@ import ( "k8s.io/client-go/kubernetes" fakekubeclientset "k8s.io/client-go/kubernetes/fake" + kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/testutil" ) @@ -101,7 +102,7 @@ func TestTopLevelOwnerKey(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { client := fakekubeclientset.NewSimpleClientset(test.objects...) - t.Override(&Client, mockClient(client)) + t.Override(&kubernetesclient.Client, mockClient(client)) actual := TopLevelOwnerKey(test.initialObject, test.kind) @@ -277,7 +278,7 @@ func TestOwnerMetaObject(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { client := fakekubeclientset.NewSimpleClientset(test.objects...) - t.Override(&Client, mockClient(client)) + t.Override(&kubernetesclient.Client, mockClient(client)) actual, err := ownerMetaObject("ns", test.or) diff --git a/pkg/skaffold/kubernetes/portforward/kubectl_forwarder.go b/pkg/skaffold/kubernetes/portforward/kubectl_forwarder.go index 94d112ab557..08c1a57b12c 100644 --- a/pkg/skaffold/kubernetes/portforward/kubectl_forwarder.go +++ b/pkg/skaffold/kubernetes/portforward/kubectl_forwarder.go @@ -35,7 +35,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/color" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" ) @@ -213,7 +213,7 @@ func (*KubectlForwarder) monitorErrorLogs(ctx context.Context, logs io.Reader, c // preference to pods that were most recently created. This is in contrast to the selection algorithm // used by kubectl (see https://github.com/GoogleContainerTools/skaffold/issues/4522 for details). func findNewestPodForService(ctx context.Context, ns, serviceName string, servicePort int) (string, int, error) { - client, err := kubernetes.Client() + client, err := kubernetesclient.Client() if err != nil { return "", -1, fmt.Errorf("getting Kubernetes client: %w", err) } diff --git a/pkg/skaffold/kubernetes/portforward/kubectl_forwarder_test.go b/pkg/skaffold/kubernetes/portforward/kubectl_forwarder_test.go index 132a261e1ee..d9048ba15d0 100644 --- a/pkg/skaffold/kubernetes/portforward/kubectl_forwarder_test.go +++ b/pkg/skaffold/kubernetes/portforward/kubectl_forwarder_test.go @@ -35,7 +35,7 @@ import ( "k8s.io/client-go/kubernetes/fake" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl" - pkgkubernetes "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/testutil" ) @@ -399,7 +399,7 @@ func TestFindNewestPodForService(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() - t.Override(&pkgkubernetes.Client, func() (kubernetes.Interface, error) { + t.Override(&client.Client, func() (kubernetes.Interface, error) { return fake.NewSimpleClientset(test.clientResources...), test.clientErr }) diff --git a/pkg/skaffold/kubernetes/portforward/resource_forwarder.go b/pkg/skaffold/kubernetes/portforward/resource_forwarder.go index d3f4413cb66..5de85acec84 100644 --- a/pkg/skaffold/kubernetes/portforward/resource_forwarder.go +++ b/pkg/skaffold/kubernetes/portforward/resource_forwarder.go @@ -23,7 +23,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" ) @@ -103,7 +103,7 @@ func (p *ResourceForwarder) getCurrentEntry(resource latest.PortForwardResource) // retrieveServiceResources retrieves all services in the cluster matching the given label // as a list of PortForwardResources func retrieveServiceResources(label string, namespaces []string) ([]*latest.PortForwardResource, error) { - client, err := kubernetes.Client() + client, err := kubernetesclient.Client() if err != nil { return nil, fmt.Errorf("getting Kubernetes client: %w", err) } diff --git a/pkg/skaffold/kubernetes/portforward/resource_forwarder_test.go b/pkg/skaffold/kubernetes/portforward/resource_forwarder_test.go index 8e5ed5ab69a..feb38b3829a 100644 --- a/pkg/skaffold/kubernetes/portforward/resource_forwarder_test.go +++ b/pkg/skaffold/kubernetes/portforward/resource_forwarder_test.go @@ -35,7 +35,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" - kubernetesutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/testutil" @@ -344,7 +344,7 @@ func TestRetrieveServices(t *testing.T) { objs[i] = s } client := fakekubeclientset.NewSimpleClientset(objs...) - t.Override(&kubernetesutil.Client, mockClient(client)) + t.Override(&kubernetesclient.Client, mockClient(client)) actual, err := retrieveServiceResources(fmt.Sprintf("%s=9876-6789", deploy.RunIDLabel), test.namespaces) diff --git a/pkg/skaffold/kubernetes/watcher.go b/pkg/skaffold/kubernetes/watcher.go index 439d886abdb..76404f9f124 100644 --- a/pkg/skaffold/kubernetes/watcher.go +++ b/pkg/skaffold/kubernetes/watcher.go @@ -24,6 +24,8 @@ import ( v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/watch" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" ) type PodWatcher interface { @@ -66,7 +68,7 @@ func (w *podWatcher) Start() (func(), error) { } } - kubeclient, err := Client() + kubeclient, err := client.Client() if err != nil { return func() {}, fmt.Errorf("getting k8s client: %w", err) } diff --git a/pkg/skaffold/kubernetes/watcher_test.go b/pkg/skaffold/kubernetes/watcher_test.go index 3b8ec0db103..d86a58a75be 100644 --- a/pkg/skaffold/kubernetes/watcher_test.go +++ b/pkg/skaffold/kubernetes/watcher_test.go @@ -28,6 +28,7 @@ import ( "k8s.io/client-go/kubernetes/fake" k8stesting "k8s.io/client-go/testing" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/testutil" ) @@ -66,7 +67,7 @@ func TestPodWatcher(t *testing.T) { }) testutil.Run(t, "fail to get client", func(t *testutil.T) { - t.Override(&Client, func() (kubernetes.Interface, error) { return nil, errors.New("unable to get client") }) + t.Override(&client.Client, func() (kubernetes.Interface, error) { return nil, errors.New("unable to get client") }) watcher := NewPodWatcher(&anyPod{}, []string{"ns"}) watcher.Register(make(chan PodEvent)) @@ -78,7 +79,7 @@ func TestPodWatcher(t *testing.T) { testutil.Run(t, "fail to watch pods", func(t *testutil.T) { clientset := fake.NewSimpleClientset() - t.Override(&Client, func() (kubernetes.Interface, error) { return clientset, nil }) + t.Override(&client.Client, func() (kubernetes.Interface, error) { return clientset, nil }) clientset.Fake.PrependWatchReactor("pods", func(action k8stesting.Action) (handled bool, ret watch.Interface, err error) { return true, nil, errors.New("unable to watch") @@ -94,7 +95,7 @@ func TestPodWatcher(t *testing.T) { testutil.Run(t, "filter 3 events", func(t *testutil.T) { clientset := fake.NewSimpleClientset() - t.Override(&Client, func() (kubernetes.Interface, error) { return clientset, nil }) + t.Override(&client.Client, func() (kubernetes.Interface, error) { return clientset, nil }) podSelector := &hasName{ validNames: []string{"pod1", "pod2", "pod3"}, diff --git a/pkg/skaffold/runner/build_deploy_test.go b/pkg/skaffold/runner/build_deploy_test.go index 215bbd8ccfc..81e50714f22 100644 --- a/pkg/skaffold/runner/build_deploy_test.go +++ b/pkg/skaffold/runner/build_deploy_test.go @@ -25,7 +25,7 @@ import ( "k8s.io/client-go/tools/clientcmd/api" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/testutil" ) @@ -73,7 +73,7 @@ func TestBuildTestDeploy(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { t.SetupFakeKubernetesContext(api.Config{CurrentContext: "cluster1"}) - t.Override(&kubernetes.Client, mockK8sClient) + t.Override(&client.Client, mockK8sClient) ctx := context.Background() artifacts := []*latest.Artifact{{ diff --git a/pkg/skaffold/runner/deploy.go b/pkg/skaffold/runner/deploy.go index fb087b1102d..569d89675dc 100644 --- a/pkg/skaffold/runner/deploy.go +++ b/pkg/skaffold/runner/deploy.go @@ -30,7 +30,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" kubectx "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/context" ) @@ -128,7 +128,7 @@ func (r *SkaffoldRunner) getCurrentContext() (*api.Context, error) { // failIfClusterIsNotReachable checks that Kubernetes is reachable. // This gives a clear early error when the cluster can't be reached. func failIfClusterIsNotReachable() error { - client, err := kubernetes.Client() + client, err := kubernetesclient.Client() if err != nil { return err } diff --git a/pkg/skaffold/runner/deploy_test.go b/pkg/skaffold/runner/deploy_test.go index 673d620ec77..e7acd2e8403 100644 --- a/pkg/skaffold/runner/deploy_test.go +++ b/pkg/skaffold/runner/deploy_test.go @@ -31,7 +31,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/testutil" ) @@ -69,7 +69,7 @@ func TestDeploy(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { t.SetupFakeKubernetesContext(api.Config{CurrentContext: "cluster1"}) - t.Override(&kubernetes.Client, mockK8sClient) + t.Override(&client.Client, mockK8sClient) t.Override(&statusCheck, dummyStatusCheck) runner := createRunner(t, test.testBench, nil) @@ -120,7 +120,7 @@ func TestDeployNamespace(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { t.SetupFakeKubernetesContext(api.Config{CurrentContext: "cluster1"}) - t.Override(&kubernetes.Client, mockK8sClient) + t.Override(&client.Client, mockK8sClient) t.Override(&statusCheck, dummyStatusCheck) runner := createRunner(t, test.testBench, nil) diff --git a/pkg/skaffold/runner/dev_test.go b/pkg/skaffold/runner/dev_test.go index a949b8284d9..0cc3031ea44 100644 --- a/pkg/skaffold/runner/dev_test.go +++ b/pkg/skaffold/runner/dev_test.go @@ -27,7 +27,7 @@ import ( "k8s.io/client-go/tools/clientcmd/api" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/filemon" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/sync" "github.com/GoogleContainerTools/skaffold/testutil" @@ -135,7 +135,7 @@ func TestDevFailFirstCycle(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { t.SetupFakeKubernetesContext(api.Config{CurrentContext: "cluster1"}) - t.Override(&kubernetes.Client, mockK8sClient) + t.Override(&client.Client, mockK8sClient) // runner := createRunner(t, test.testBench).WithMonitor(test.monitor) runner := createRunner(t, test.testBench, test.monitor) @@ -266,7 +266,7 @@ func TestDev(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { t.SetupFakeKubernetesContext(api.Config{CurrentContext: "cluster1"}) - t.Override(&kubernetes.Client, mockK8sClient) + t.Override(&client.Client, mockK8sClient) test.testBench.cycles = len(test.watchEvents) runner := createRunner(t, test.testBench, &TestMonitor{ @@ -352,7 +352,7 @@ func TestDevSync(t *testing.T) { testutil.Run(t, test.description, func(t *testutil.T) { var actualFileSyncEventCalls fileSyncEventCalls t.SetupFakeKubernetesContext(api.Config{CurrentContext: "cluster1"}) - t.Override(&kubernetes.Client, mockK8sClient) + t.Override(&client.Client, mockK8sClient) t.Override(&fileSyncInProgress, func(int, string) { actualFileSyncEventCalls.InProgress++ }) t.Override(&fileSyncFailed, func(int, string, error) { actualFileSyncEventCalls.Failed++ }) t.Override(&fileSyncSucceeded, func(int, string) { actualFileSyncEventCalls.Succeeded++ }) diff --git a/pkg/skaffold/sync/sync.go b/pkg/skaffold/sync/sync.go index 4e6be12a9d0..b66ae4b5a15 100644 --- a/pkg/skaffold/sync/sync.go +++ b/pkg/skaffold/sync/sync.go @@ -36,7 +36,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/jib" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/filemon" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" ) @@ -273,7 +273,7 @@ func Perform(ctx context.Context, image string, files syncMap, cmdFn func(contex errs, ctx := errgroup.WithContext(ctx) - client, err := kubernetes.Client() + client, err := kubernetesclient.Client() if err != nil { return fmt.Errorf("getting Kubernetes client: %w", err) } diff --git a/pkg/skaffold/sync/sync_test.go b/pkg/skaffold/sync/sync_test.go index 2e41b5a650f..19bca0ccbc8 100644 --- a/pkg/skaffold/sync/sync_test.go +++ b/pkg/skaffold/sync/sync_test.go @@ -34,7 +34,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/jib" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/filemon" - pkgkubernetes "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/testutil" @@ -958,7 +958,7 @@ func TestPerform(t *testing.T) { cmdRecord := &TestCmdRecorder{err: test.cmdErr} t.Override(&util.DefaultExecCommand, cmdRecord) - t.Override(&pkgkubernetes.Client, func() (kubernetes.Interface, error) { + t.Override(&client.Client, func() (kubernetes.Interface, error) { return fake.NewSimpleClientset(test.pod), test.clientErr }) diff --git a/pkg/webhook/kubernetes/deployment.go b/pkg/webhook/kubernetes/deployment.go index 27c05575a60..9098f8fac0f 100644 --- a/pkg/webhook/kubernetes/deployment.go +++ b/pkg/webhook/kubernetes/deployment.go @@ -32,6 +32,7 @@ import ( "k8s.io/apimachinery/pkg/util/wait" pkgkubernetes "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/pkg/webhook/constants" "github.com/GoogleContainerTools/skaffold/pkg/webhook/labels" ) @@ -48,7 +49,7 @@ const ( // 2. A container to run hugo server // and one emptyDir volume to hold the git repository func CreateDeployment(pr *github.PullRequestEvent, svc *v1.Service, externalIP string) (*appsv1.Deployment, error) { - client, err := pkgkubernetes.Client() + client, err := kubernetesclient.Client() if err != nil { return nil, fmt.Errorf("getting Kubernetes client: %w", err) } @@ -124,7 +125,7 @@ func CreateDeployment(pr *github.PullRequestEvent, svc *v1.Service, externalIP s // WaitForDeploymentToStabilize waits till the Deployment has stabilized func WaitForDeploymentToStabilize(d *appsv1.Deployment, ip string) error { - client, err := pkgkubernetes.Client() + client, err := kubernetesclient.Client() if err != nil { return fmt.Errorf("getting Kubernetes client: %w", err) } diff --git a/pkg/webhook/kubernetes/service.go b/pkg/webhook/kubernetes/service.go index d6fa1d6aac5..ae734b0710b 100644 --- a/pkg/webhook/kubernetes/service.go +++ b/pkg/webhook/kubernetes/service.go @@ -25,7 +25,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/pkg/webhook/constants" "github.com/GoogleContainerTools/skaffold/pkg/webhook/labels" ) @@ -33,7 +33,7 @@ import ( // CreateService creates a service for the deployment to bind to // and returns the external IP of the service func CreateService(pr *github.PullRequestEvent) (*v1.Service, error) { - client, err := kubernetes.Client() + client, err := kubernetesclient.Client() if err != nil { return nil, fmt.Errorf("getting Kubernetes client: %w", err) } @@ -82,7 +82,7 @@ func serviceName(prNumber int) string { } func getService(svc *v1.Service) (*v1.Service, error) { - client, err := kubernetes.Client() + client, err := kubernetesclient.Client() if err != nil { return nil, fmt.Errorf("getting Kubernetes client: %w", err) } From 9cd8bdd8e50434370b68fa4927efbb27d3646cb9 Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Wed, 26 Aug 2020 20:30:48 +0000 Subject: [PATCH 111/138] Add Kustomize Hydration to Kpt Deployer's Render method (#4719) * adding kustomize hydration into kpt's render * fix comments * improve documentation --- pkg/skaffold/deploy/kpt.go | 114 ++++++++++++++++---- pkg/skaffold/deploy/kpt_test.go | 186 ++++++++++++++++++++------------ 2 files changed, 208 insertions(+), 92 deletions(-) diff --git a/pkg/skaffold/deploy/kpt.go b/pkg/skaffold/deploy/kpt.go index af5ad55e0fa..b54a8ba399e 100644 --- a/pkg/skaffold/deploy/kpt.go +++ b/pkg/skaffold/deploy/kpt.go @@ -17,6 +17,7 @@ limitations under the License. package deploy import ( + "bytes" "context" "errors" "fmt" @@ -36,8 +37,9 @@ import ( ) var ( - kptHydrated = ".kpt-hydrated" inventoryTemplate = "inventory-template.yaml" + kptHydrated = ".kpt-hydrated" + pipeline = ".pipeline" ) // KptDeployer deploys workflows with kpt CLI @@ -77,6 +79,14 @@ func (k *KptDeployer) Dependencies() ([]string, error) { deps.insert(configDeps...) + // Kpt deployer assumes that the kustomization configuration to build lives directly under k.Dir. + kustomizeDeps, err := dependenciesForKustomization(k.Dir) + if err != nil { + return nil, fmt.Errorf("finding kustomization directly under %s: %w", k.Dir, err) + } + + deps.insert(kustomizeDeps...) + return deps.toList(), nil } @@ -97,6 +107,7 @@ func (k *KptDeployer) Cleanup(ctx context.Context, _ io.Writer) error { return nil } +// Render hydrates manifests using both kustomization and kpt functions. func (k *KptDeployer) Render(ctx context.Context, out io.Writer, builds []build.Artifact, _ bool, filepath string) error { manifests, err := k.renderManifests(ctx, out, builds) if err != nil { @@ -106,12 +117,34 @@ func (k *KptDeployer) Render(ctx context.Context, out io.Writer, builds []build. return outputRenderedManifests(manifests.String(), filepath, out) } +// renderManifests handles a majority of the hydration process for manifests. +// This involves reading configs from a source directory, running kustomize build, running kpt pipelines, +// adding image digests, and adding run-id labels. func (k *KptDeployer) renderManifests(ctx context.Context, _ io.Writer, builds []build.Artifact) (deploy.ManifestList, error) { debugHelpersRegistry, err := config.GetDebugHelpersRegistry(k.globalConfig) if err != nil { return nil, fmt.Errorf("retrieving debug helpers registry: %w", err) } + // .pipeline is a temp dir used to store output between steps of the desired workflow + // This can be removed once kpt can fully support the desired workflow independently. + if err := os.RemoveAll(filepath.Join(pipeline, k.Dir)); err != nil { + return nil, fmt.Errorf("deleting temporary directory %s: %w", filepath.Join(pipeline, k.Dir), err) + } + // 0755 is a permission setting where the owner can read, write, and execute. + // Others can read and execute but not modify the directory. + if err := os.MkdirAll(filepath.Join(pipeline, k.Dir), 0755); err != nil { + return nil, fmt.Errorf("creating temporary directory %s: %w", filepath.Join(pipeline, k.Dir), err) + } + + if err := k.readConfigs(ctx); err != nil { + return nil, fmt.Errorf("reading config manifests: %w", err) + } + + if err := k.kustomizeBuild(ctx); err != nil { + return nil, fmt.Errorf("kustomize build: %w", err) + } + manifests, err := k.kptFnRun(ctx) if err != nil { return nil, fmt.Errorf("running kpt functions: %w", err) @@ -136,30 +169,75 @@ func (k *KptDeployer) renderManifests(ctx context.Context, _ io.Writer, builds [ return manifests.SetLabels(k.labels) } -// kptFnRun does a dry run with the specified kpt functions (fn-path XOR image) against dir. -// If neither fn-path nor image are specified, functions will attempt to be discovered in dir. -// An error is returned when both fn-path and image are specified. +// readConfigs uses `kpt fn source` to read config manifests from k.Dir +// and uses `kpt fn sink` to output those manifests to .pipeline. +func (k *KptDeployer) readConfigs(ctx context.Context) error { + cmd := exec.CommandContext(ctx, "kpt", kptCommandArgs(k.Dir, []string{"fn", "source"}, nil, nil)...) + b, err := util.RunCmdOut(cmd) + if err != nil { + return err + } + + cmd = exec.CommandContext(ctx, "kpt", kptCommandArgs(filepath.Join(pipeline, k.Dir), []string{"fn", "sink"}, nil, nil)...) + cmd.Stdin = bytes.NewBuffer(b) + if _, err := util.RunCmdOut(cmd); err != nil { + return err + } + + return nil +} + +// kustomizeBuild runs `kustomize build` if a kustomization config exists and outputs to .pipeline. +func (k *KptDeployer) kustomizeBuild(ctx context.Context) error { + if _, err := findKustomizationConfig(k.Dir); err != nil { + // No kustomization config was found directly under k.Dir, so there is no need to continue. + return nil + } + + cmd := exec.CommandContext(ctx, "kustomize", buildCommandArgs([]string{"-o", filepath.Join(pipeline, k.Dir)}, k.Dir)...) + if _, err := util.RunCmdOut(cmd); err != nil { + return err + } + + deps, err := dependenciesForKustomization(k.Dir) + if err != nil { + return fmt.Errorf("finding kustomization dependencies: %w", err) + } + + // Kustomize build outputs hydrated configs to .pipeline, so the dry configs must be removed. + for _, v := range deps { + if err := os.RemoveAll(filepath.Join(pipeline, v)); err != nil { + return err + } + } + + return nil +} + +// kptFnRun does a dry run with the specified kpt functions (fn-path XOR image) against .pipeline. +// If neither fn-path nor image are specified, functions will attempt to be discovered in .pipeline. +// An error occurs if both fn-path and image are specified. func (k *KptDeployer) kptFnRun(ctx context.Context) (deploy.ManifestList, error) { var manifests deploy.ManifestList // --dry-run sets the pipeline's output to STDOUT, otherwise output is set to sinkDir. // For now, k.Dir will be treated as sinkDir (and sourceDir). flags := []string{"--dry-run"} - specifiedFnPath := false + count := 0 if len(k.Fn.FnPath) > 0 { flags = append(flags, "--fn-path", k.Fn.FnPath) - specifiedFnPath = true + count++ } if len(k.Fn.Image) > 0 { - if specifiedFnPath { - return nil, errors.New("cannot specify both fn-path and image") - } - flags = append(flags, "--image", k.Fn.Image) + count++ + } + if count > 1 { + return nil, errors.New("only one of `fn-path` or `image` configs can be specified at most") } - cmd := exec.CommandContext(ctx, "kpt", kptCommandArgs(k.Dir, []string{"fn", "run"}, flags, nil)...) + cmd := exec.CommandContext(ctx, "kpt", kptCommandArgs(pipeline, []string{"fn", "run"}, flags, nil)...) out, err := util.RunCmdOut(cmd) if err != nil { return nil, err @@ -183,7 +261,7 @@ func (k *KptDeployer) getApplyDir(ctx context.Context) (string, error) { } // 0755 is a permission setting where the owner can read, write, and execute. - // Others can read and execute but not modify the file. + // Others can read and execute but not modify the directory. if err := os.MkdirAll(kptHydrated, 0755); err != nil { return "", fmt.Errorf("applyDir was unspecified. creating applyDir: %w", err) } @@ -224,8 +302,7 @@ func kptCommandArgs(dir string, commands, flags, globalFlags []string) []string return args } -// getResources returns a list of all file names in `root` that end in .yaml or .yml -// and all local kustomization dependencies under root. +// getResources returns a list of all file names in root that end in .yaml or .yml func getResources(root string) ([]string, error) { var files []string @@ -241,14 +318,7 @@ func getResources(root string) ([]string, error) { return fmt.Errorf("matching %s with regex: %w", filepath.Base(path), err) } - if info.IsDir() { - depsForKustomization, err := dependenciesForKustomization(path) - if err != nil { - return err - } - - files = append(files, depsForKustomization...) - } else if isResource { + if !info.IsDir() && isResource { files = append(files, path) } diff --git a/pkg/skaffold/deploy/kpt_test.go b/pkg/skaffold/deploy/kpt_test.go index a634a230c34..eaab1185850 100644 --- a/pkg/skaffold/deploy/kpt_test.go +++ b/pkg/skaffold/deploy/kpt_test.go @@ -20,8 +20,10 @@ import ( "bytes" "context" "errors" + "fmt" "io/ioutil" "os" + "path/filepath" "strings" "testing" @@ -213,6 +215,8 @@ func TestKpt_Cleanup(t *testing.T) { t.NewTempDir().Chdir() if test.applyDir == "valid_path" { + // 0755 is a permission setting where the owner can read, write, and execute. + // Others can read and execute but not modify the directory. os.Mkdir(test.applyDir, 0755) } @@ -241,6 +245,7 @@ func TestKpt_Cleanup(t *testing.T) { } func TestKpt_Render(t *testing.T) { + // The follow are outputs to `kpt fn run` commands. output1 := `apiVersion: v1 kind: Pod metadata: @@ -253,16 +258,6 @@ spec: output2 := `apiVersion: v1 kind: Pod -metadata: - namespace: default -spec: - containers: - - image: gcr.io/project/image2 - name: image2 -` - - output3 := `apiVersion: v1 -kind: Pod metadata: namespace: default spec: @@ -273,7 +268,7 @@ spec: name: image2 ` - output4 := `apiVersion: v1 + output3 := `apiVersion: v1 kind: Pod metadata: namespace: default @@ -293,13 +288,14 @@ spec: ` tests := []struct { - description string - builds []build.Artifact - labels map[string]string - cfg *latest.KptDeploy - commands util.Command - expected string - shouldErr bool + description string + builds []build.Artifact + labels map[string]string + cfg *latest.KptDeploy + commands util.Command + kustomizations map[string]string + expected string + shouldErr bool }{ { description: "no fnPath or image specified", @@ -312,7 +308,10 @@ spec: cfg: &latest.KptDeploy{ Dir: ".", }, - commands: testutil.CmdRunOut("kpt fn run . --dry-run", output1), + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOut("kpt fn run .pipeline --dry-run", output1), expected: `apiVersion: v1 kind: Pod metadata: @@ -324,21 +323,42 @@ spec: `, }, { - description: "fnPath specified", + description: "fnPath specified, multiple resources, and labels", builds: []build.Artifact{ + { + ImageName: "gcr.io/project/image1", + Tag: "gcr.io/project/image1:tag1", + }, { ImageName: "gcr.io/project/image2", Tag: "gcr.io/project/image2:tag2", }, }, + labels: map[string]string{"user/label": "test"}, cfg: &latest.KptDeploy{ Dir: "test", Fn: latest.KptFn{FnPath: "kpt-func.yaml"}, }, - commands: testutil.CmdRunOut("kpt fn run test --dry-run --fn-path kpt-func.yaml", output2), + commands: testutil. + CmdRunOut("kpt fn source test", ``). + AndRunOut(fmt.Sprintf("kpt fn sink %s", filepath.Join(".pipeline", "test")), ``). + AndRunOut("kpt fn run .pipeline --dry-run --fn-path kpt-func.yaml", output3), expected: `apiVersion: v1 kind: Pod metadata: + labels: + user/label: test + namespace: default +spec: + containers: + - image: gcr.io/project/image1:tag1 + name: image1 +--- +apiVersion: v1 +kind: Pod +metadata: + labels: + user/label: test namespace: default spec: containers: @@ -347,7 +367,7 @@ spec: `, }, { - description: "image specified", + description: "fn image specified, multiple images in resource", builds: []build.Artifact{ { ImageName: "gcr.io/project/image1", @@ -359,10 +379,13 @@ spec: }, }, cfg: &latest.KptDeploy{ - Dir: "test", + Dir: ".", Fn: latest.KptFn{Image: "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar"}, }, - commands: testutil.CmdRunOut("kpt fn run test --dry-run --image gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", output3), + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOut("kpt fn run .pipeline --dry-run --image gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", output2), expected: `apiVersion: v1 kind: Pod metadata: @@ -376,59 +399,57 @@ spec: `, }, { - description: "multiple resources outputted", + description: "empty output from pipeline", builds: []build.Artifact{ { ImageName: "gcr.io/project/image1", Tag: "gcr.io/project/image1:tag1", }, - { - ImageName: "gcr.io/project/image2", - Tag: "gcr.io/project/image2:tag2", - }, }, + labels: map[string]string{"user/label": "test"}, cfg: &latest.KptDeploy{ - Dir: "test", - Fn: latest.KptFn{FnPath: "kpt-func.yaml"}, + Dir: ".", }, - commands: testutil.CmdRunOut("kpt fn run test --dry-run --fn-path kpt-func.yaml", output4), - expected: `apiVersion: v1 -kind: Pod -metadata: - namespace: default -spec: - containers: - - image: gcr.io/project/image1:tag1 - name: image1 ---- -apiVersion: v1 -kind: Pod -metadata: - namespace: default -spec: - containers: - - image: gcr.io/project/image2:tag2 - name: image2 -`, + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOut("kpt fn run .pipeline --dry-run", ``), + expected: "\n", + }, + { + description: "both fnPath and image specified", + cfg: &latest.KptDeploy{ + Dir: ".", + Fn: latest.KptFn{ + FnPath: "kpt-func.yaml", + Image: "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar"}, + }, + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``), + shouldErr: true, }, { - description: "user labels", + description: "kustomization render", builds: []build.Artifact{ { ImageName: "gcr.io/project/image1", Tag: "gcr.io/project/image1:tag1", }, }, - labels: map[string]string{"user/label": "test"}, cfg: &latest.KptDeploy{ Dir: ".", }, - commands: testutil.CmdRunOut("kpt fn run . --dry-run", output1), + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOut("kustomize build -o .pipeline .", ``). + AndRunOut("kpt fn run .pipeline --dry-run", output1), + kustomizations: map[string]string{"kustomization.yaml": `resources: +- foo.yaml`}, expected: `apiVersion: v1 kind: Pod metadata: - labels: - user/label: test namespace: default spec: containers: @@ -437,42 +458,65 @@ spec: `, }, { - description: "empty output from pipeline", + description: "reading configs from sourceDir fails", + cfg: &latest.KptDeploy{ + Dir: ".", + }, + commands: testutil. + CmdRunOutErr("kpt fn source .", ``, errors.New("BUG")). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOut("kpt fn run .pipeline --dry-run", "invalid pipeline"), + shouldErr: true, + }, + { + description: "outputting configs to sinkDir fails", + cfg: &latest.KptDeploy{ + Dir: ".", + }, + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOutErr("kpt fn sink .pipeline", ``, errors.New("BUG")). + AndRunOut("kpt fn run .pipeline --dry-run", "invalid pipeline"), + shouldErr: true, + }, + { + description: "kustomize build fails (invalid kustomization config)", builds: []build.Artifact{ { ImageName: "gcr.io/project/image1", Tag: "gcr.io/project/image1:tag1", }, }, - labels: map[string]string{"user/label": "test"}, cfg: &latest.KptDeploy{ Dir: ".", }, - commands: testutil.CmdRunOut("kpt fn run . --dry-run", ``), - expected: "\n", + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOutErr("kustomize build -o .pipeline .", ``, errors.New("BUG")). + AndRunOut("kpt fn run .pipeline --dry-run", output1), + kustomizations: map[string]string{"kustomization.yaml": `resources: +- foo.yaml`}, + shouldErr: true, }, { description: "kpt fn run fails", cfg: &latest.KptDeploy{ Dir: ".", }, - commands: testutil.CmdRunOutErr("kpt fn run . --dry-run", "invalid pipeline", errors.New("BUG")), - shouldErr: true, - }, - { - description: "both fnPath and image specified", - cfg: &latest.KptDeploy{ - Dir: "test", - Fn: latest.KptFn{ - FnPath: "kpt-func.yaml", - Image: "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar"}, - }, + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOutErr("kpt fn run .pipeline --dry-run", "invalid pipeline", errors.New("BUG")), shouldErr: true, }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { t.Override(&util.DefaultExecCommand, test.commands) + tmpDir := t.NewTempDir().Chdir() + + tmpDir.WriteFiles(test.kustomizations) k := NewKptDeployer(&runcontext.RunContext{ WorkingDir: ".", @@ -531,6 +575,8 @@ func TestKpt_GetApplyDir(t *testing.T) { tmpDir := t.NewTempDir().Chdir() if test.applyDir == test.expected { + // 0755 is a permission setting where the owner can read, write, and execute. + // Others can read and execute but not modify the directory. os.Mkdir(test.applyDir, 0755) } From f54a62b42b70ce34825aa3b72a4dfd63735f77f2 Mon Sep 17 00:00:00 2001 From: Gaurav <39389231+gsquared94@users.noreply.github.com> Date: Thu, 27 Aug 2020 16:47:38 +0530 Subject: [PATCH 112/138] Move `DetectWSL` function into util package (#4721) * Move `DetectWSL` function into util package * add comments --- pkg/skaffold/docker/client.go | 20 +++-------------- pkg/skaffold/util/wsl.go | 41 +++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 17 deletions(-) create mode 100644 pkg/skaffold/util/wsl.go diff --git a/pkg/skaffold/docker/client.go b/pkg/skaffold/docker/client.go index 71294c92c6f..9b38463c1c5 100644 --- a/pkg/skaffold/docker/client.go +++ b/pkg/skaffold/docker/client.go @@ -20,7 +20,6 @@ import ( "context" "errors" "fmt" - "io/ioutil" "net/http" "os" "os/exec" @@ -164,22 +163,8 @@ func getUserAgentHeader() map[string]string { } } -func detectWsl() (bool, error) { - if _, err := os.Stat("/proc/version"); err == nil { - b, err := ioutil.ReadFile("/proc/version") - if err != nil { - return false, fmt.Errorf("read /proc/version: %w", err) - } - str := strings.ToLower(string(b)) - if strings.Contains(str, "microsoft") { - return true, nil - } - } - return false, nil -} - func getMiniKubeFilename() (string, error) { - if found, _ := detectWsl(); found { + if found, _ := util.DetectWSL(); found { filename, err := exec.LookPath("minikube.exe") if err != nil { return "", errors.New("unable to find minikube.exe. Please add it to PATH environment variable") @@ -221,7 +206,8 @@ func getMinikubeDockerEnv(minikubeProfile string) (map[string]string, error) { env[kv[0]] = kv[1] } - if found, _ := detectWsl(); found { + if found, _ := util.DetectWSL(); found { + // rewrite Unix path to Windows cmd := exec.Command("wslpath", env["DOCKER_CERT_PATH"]) out, err := util.RunCmdOut(cmd) if err == nil { diff --git a/pkg/skaffold/util/wsl.go b/pkg/skaffold/util/wsl.go new file mode 100644 index 00000000000..30f07ba1088 --- /dev/null +++ b/pkg/skaffold/util/wsl.go @@ -0,0 +1,41 @@ +/* +Copyright 2020 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 util + +import ( + "fmt" + "io/ioutil" + "os" + "strings" +) + +// DetectWSL checks for Windows Subsystem for Linux +func DetectWSL() (bool, error) { + if _, err := os.Stat("/proc/version"); err == nil { + b, err := ioutil.ReadFile("/proc/version") + if err != nil { + return false, fmt.Errorf("read /proc/version: %w", err) + } + + // Microsoft changed the case between WSL1 and WSL2 + str := strings.ToLower(string(b)) + if strings.Contains(str, "microsoft") { + return true, nil + } + } + return false, nil +} From fd0d8d27bc476c8ad90eb8dfa96829cbaa40d7d0 Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Thu, 27 Aug 2020 16:55:34 +0000 Subject: [PATCH 113/138] Kpt Deployer Deploy() Implementation/Tests (#4723) * deployer implementation and tests * nit --- pkg/skaffold/deploy/kpt.go | 45 +++++++++++-- pkg/skaffold/deploy/kpt_test.go | 114 +++++++++++++++++++++++++++++--- 2 files changed, 143 insertions(+), 16 deletions(-) diff --git a/pkg/skaffold/deploy/kpt.go b/pkg/skaffold/deploy/kpt.go index b54a8ba399e..d7d82d96934 100644 --- a/pkg/skaffold/deploy/kpt.go +++ b/pkg/skaffold/deploy/kpt.go @@ -31,6 +31,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" deploy "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" @@ -60,8 +61,40 @@ func NewKptDeployer(runCtx *runcontext.RunContext, labels map[string]string) *Kp } } +// Deploy hydrates the manifests using kustomizations and kpt functions as described in the render method, +// outputs them to the applyDir, and runs `kpt live apply` against applyDir to create resources in the cluster. +// `kpt live apply` supports automated pruning declaratively via resources in the applyDir. func (k *KptDeployer) Deploy(ctx context.Context, out io.Writer, builds []build.Artifact) ([]string, error) { - return nil, nil + manifests, err := k.renderManifests(ctx, out, builds) + if err != nil { + return nil, err + } + + if len(manifests) == 0 { + return nil, nil + } + + namespaces, err := manifests.CollectNamespaces() + if err != nil { + event.DeployInfoEvent(fmt.Errorf("could not fetch deployed resource namespace. "+ + "This might cause port-forward and deploy health-check to fail: %w", err)) + } + + applyDir, err := k.getApplyDir(ctx) + if err != nil { + return nil, fmt.Errorf("getting applyDir: %w", err) + } + + outputRenderedManifests(manifests.String(), filepath.Join(applyDir, "resources.yaml"), out) + + cmd := exec.CommandContext(ctx, "kpt", kptCommandArgs(applyDir, []string{"live", "apply"}, k.Live.Apply, nil)...) + cmd.Stdout = out + cmd.Stderr = out + if err := util.RunCmd(cmd); err != nil { + return nil, err + } + + return namespaces, nil } // Dependencies returns a list of files that the deployer depends on. This does NOT include applyDir. @@ -91,17 +124,17 @@ func (k *KptDeployer) Dependencies() ([]string, error) { } // Cleanup deletes what was deployed by calling `kpt live destroy`. -func (k *KptDeployer) Cleanup(ctx context.Context, _ io.Writer) error { +func (k *KptDeployer) Cleanup(ctx context.Context, out io.Writer) error { applyDir, err := k.getApplyDir(ctx) if err != nil { return fmt.Errorf("getting applyDir: %w", err) } cmd := exec.CommandContext(ctx, "kpt", kptCommandArgs(applyDir, []string{"live", "destroy"}, nil, nil)...) - out, err := util.RunCmdOut(cmd) - if err != nil { - // Kpt errors are written in STDOUT and surrounded by `\n`. - return fmt.Errorf("kpt live destroy: %s", strings.Trim(string(out), "\n")) + cmd.Stdout = out + cmd.Stderr = out + if err := util.RunCmd(cmd); err != nil { + return err } return nil diff --git a/pkg/skaffold/deploy/kpt_test.go b/pkg/skaffold/deploy/kpt_test.go index eaab1185850..489bbedc567 100644 --- a/pkg/skaffold/deploy/kpt_test.go +++ b/pkg/skaffold/deploy/kpt_test.go @@ -36,20 +36,114 @@ import ( ) func TestKpt_Deploy(t *testing.T) { + output := `apiVersion: v1 +kind: Pod +metadata: + namespace: default +spec: + containers: + - image: gcr.io/project/image1 + name: image1 +` tests := []struct { - description string - expected []string - shouldErr bool + description string + builds []build.Artifact + cfg *latest.KptDeploy + kustomizations map[string]string + commands util.Command + expected []string + shouldErr bool }{ { - description: "nil", + description: "no manifest", + cfg: &latest.KptDeploy{ + Dir: ".", + }, + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOut("kpt fn run .pipeline --dry-run", ``), + }, + { + description: "invalid manifest", + cfg: &latest.KptDeploy{ + Dir: ".", + }, + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOut("kpt fn run .pipeline --dry-run", `foo`), + shouldErr: true, + }, + { + description: "invalid user specified applyDir", + cfg: &latest.KptDeploy{ + Dir: ".", + ApplyDir: "invalid_path", + }, + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOut("kpt fn run .pipeline --dry-run", output), + shouldErr: true, + }, + { + description: "kustomization and specified kpt fn", + cfg: &latest.KptDeploy{ + Dir: ".", + Fn: latest.KptFn{FnPath: "kpt-func.yaml"}, + ApplyDir: "valid_path", + }, + kustomizations: map[string]string{"Kustomization": `resources: +- foo.yaml`}, + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOut("kustomize build -o .pipeline .", ``). + AndRunOut("kpt fn run .pipeline --dry-run --fn-path kpt-func.yaml", output). + AndRun("kpt live apply valid_path"), + expected: []string{"default"}, + }, + { + description: "kpt live apply fails", + cfg: &latest.KptDeploy{ + Dir: ".", + }, + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOut("kpt fn run .pipeline --dry-run", output). + AndRunOut("kpt live init .kpt-hydrated", ``). + AndRunErr("kpt live apply .kpt-hydrated", errors.New("BUG")), + shouldErr: true, }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - k := NewKptDeployer(&runcontext.RunContext{}, nil) - res, err := k.Deploy(context.Background(), nil, nil) - t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expected, res) + t.Override(&util.DefaultExecCommand, test.commands) + tmpDir := t.NewTempDir().Chdir() + + tmpDir.WriteFiles(test.kustomizations) + + k := NewKptDeployer(&runcontext.RunContext{ + Cfg: latest.Pipeline{ + Deploy: latest.DeployConfig{ + DeployType: latest.DeployType{ + KptDeploy: test.cfg, + }, + }, + }, + }, nil) + + if k.ApplyDir == "valid_path" { + // 0755 is a permission setting where the owner can read, write, and execute. + // Others can read and execute but not modify the directory. + os.Mkdir(k.ApplyDir, 0755) + } + + _, err := k.Deploy(context.Background(), ioutil.Discard, test.builds) + + t.CheckError(test.shouldErr, err) }) } } @@ -194,19 +288,19 @@ func TestKpt_Cleanup(t *testing.T) { { description: "valid user specified applyDir w/o template resource", applyDir: "valid_path", - commands: testutil.CmdRunOutErr("kpt live destroy valid_path", "", errors.New("BUG")), + commands: testutil.CmdRunErr("kpt live destroy valid_path", errors.New("BUG")), shouldErr: true, }, { description: "valid user specified applyDir w/ template resource (emulated)", applyDir: "valid_path", - commands: testutil.CmdRunOut("kpt live destroy valid_path", ""), + commands: testutil.CmdRun("kpt live destroy valid_path"), }, { description: "unspecified applyDir", commands: testutil. CmdRunOut("kpt live init .kpt-hydrated", ""). - AndRunOut("kpt live destroy .kpt-hydrated", ""), + AndRun("kpt live destroy .kpt-hydrated"), }, } for _, test := range tests { From abdbe1774fec475f1bf00d0ec7276d31561ba877 Mon Sep 17 00:00:00 2001 From: Brian de Alwis Date: Thu, 27 Aug 2020 12:58:12 -0400 Subject: [PATCH 114/138] Add SKAFFOLD_CMDLINE to encode command-line (#4704) --- cmd/skaffold/app/skaffold.go | 14 ++++++++++++++ cmd/skaffold/app/skaffold_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/cmd/skaffold/app/skaffold.go b/cmd/skaffold/app/skaffold.go index 41fd4b83c38..9f771cf9062 100644 --- a/cmd/skaffold/app/skaffold.go +++ b/cmd/skaffold/app/skaffold.go @@ -18,7 +18,12 @@ package app import ( "context" + "fmt" "io" + "os" + + shell "github.com/kballard/go-shellquote" + "github.com/sirupsen/logrus" "github.com/GoogleContainerTools/skaffold/cmd/skaffold/app/cmd" ) @@ -30,5 +35,14 @@ func Run(out, stderr io.Writer) error { catchCtrlC(cancel) c := cmd.NewSkaffoldCommand(out, stderr) + if cmdLine := os.Getenv("SKAFFOLD_CMDLINE"); cmdLine != "" && len(os.Args) == 1 { + parsed, err := shell.Split(cmdLine) + if err != nil { + return fmt.Errorf("SKAFFOLD_CMDLINE is invalid: %w", err) + } + // XXX logged before logrus.SetLevel is called in NewSkaffoldCommand's PersistentPreRunE + logrus.Debugf("Retrieving command line from SKAFFOLD_CMDLINE: %q", parsed) + c.SetArgs(parsed) + } return c.ExecuteContext(ctx) } diff --git a/cmd/skaffold/app/skaffold_test.go b/cmd/skaffold/app/skaffold_test.go index 3cc06500d81..1bdea7b1b4b 100644 --- a/cmd/skaffold/app/skaffold_test.go +++ b/cmd/skaffold/app/skaffold_test.go @@ -53,3 +53,33 @@ func TestMainUnknownCommand(t *testing.T) { t.CheckError(true, err) }) } + +func TestSkaffoldCmdline_MainHelp(t *testing.T) { + testutil.Run(t, "", func(t *testutil.T) { + var ( + output bytes.Buffer + errOutput bytes.Buffer + ) + + t.SetEnvs(map[string]string{"SKAFFOLD_CMDLINE": "help"}) + t.Override(&os.Args, []string{"skaffold"}) + + err := Run(&output, &errOutput) + + t.CheckNoError(err) + t.CheckContains("End-to-end pipelines", output.String()) + t.CheckContains("Getting started with a new project", output.String()) + t.CheckEmpty(errOutput.String()) + }) +} + +func TestSkaffoldCmdline_MainUnknownCommand(t *testing.T) { + testutil.Run(t, "", func(t *testutil.T) { + t.Override(&os.Args, []string{"skaffold"}) + t.SetEnvs(map[string]string{"SKAFFOLD_CMDLINE": "unknown"}) + + err := Run(ioutil.Discard, ioutil.Discard) + + t.CheckError(true, err) + }) +} From 67e81bce7eba453dcca2b57a92e8157dbca3ecc3 Mon Sep 17 00:00:00 2001 From: Gaurav <39389231+gsquared94@users.noreply.github.com> Date: Fri, 28 Aug 2020 11:24:52 +0530 Subject: [PATCH 115/138] Identify minikube cluster for any profile name (#4701) * Move `DetectWSL` function into util package * Add heuristics to detect minikube cluster * Remove label matching; add cluster cert path matching * change logs to trace level * simplify * Update pkg/skaffold/cluster/minikube.go Co-authored-by: Tejal Desai * appease linters * test failure Co-authored-by: Tejal Desai --- pkg/skaffold/build/local/docker_test.go | 10 + pkg/skaffold/cluster/minikube.go | 214 +++++++++++++++++++++ pkg/skaffold/cluster/minikube_test.go | 131 +++++++++++++ pkg/skaffold/config/util.go | 13 +- pkg/skaffold/docker/client.go | 36 +--- pkg/skaffold/docker/client_test.go | 32 +-- pkg/skaffold/kubernetes/context/context.go | 13 ++ pkg/skaffold/util/util.go | 9 + pkg/skaffold/util/util_test.go | 37 ++++ 9 files changed, 451 insertions(+), 44 deletions(-) create mode 100644 pkg/skaffold/cluster/minikube.go create mode 100644 pkg/skaffold/cluster/minikube_test.go diff --git a/pkg/skaffold/build/local/docker_test.go b/pkg/skaffold/build/local/docker_test.go index a7d1d505381..bdf8524bbd5 100644 --- a/pkg/skaffold/build/local/docker_test.go +++ b/pkg/skaffold/build/local/docker_test.go @@ -19,9 +19,11 @@ package local import ( "context" "io/ioutil" + "os/exec" "path/filepath" "testing" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/cluster" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" @@ -82,6 +84,7 @@ func TestDockerCLIBuild(t *testing.T) { "docker build . --file "+dockerfilePath+" -t tag --force-rm", test.expectedEnv, )) + t.Override(&cluster.GetClient, func() cluster.Client { return fakeMinikubeClient{} }) t.Override(&util.OSEnviron, func() []string { return []string{"KEY=VALUE"} }) t.Override(&docker.NewAPIClient, func(*runcontext.RunContext) (docker.LocalDaemon, error) { return docker.NewLocalDaemon(&testutil.FakeAPIClient{}, test.extraEnv, false, nil), nil @@ -104,3 +107,10 @@ func TestDockerCLIBuild(t *testing.T) { }) } } + +type fakeMinikubeClient struct{} + +func (fakeMinikubeClient) IsMinikube(kubeContext string) bool { return false } +func (fakeMinikubeClient) MinikubeExec(arg ...string) (*exec.Cmd, error) { + return exec.Command("minikube", arg...), nil +} diff --git a/pkg/skaffold/cluster/minikube.go b/pkg/skaffold/cluster/minikube.go new file mode 100644 index 00000000000..398b4a2eff8 --- /dev/null +++ b/pkg/skaffold/cluster/minikube.go @@ -0,0 +1,214 @@ +/* +Copyright 2020 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 cluster + +import ( + "encoding/json" + "errors" + "fmt" + "net/url" + "os" + "os/exec" + "path/filepath" + + "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/rest" + "k8s.io/client-go/util/homedir" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/context" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" +) + +var GetClient = getClient + +// To override during tests +var ( + minikubeBinaryFunc = minikubeBinary + getRestClientConfigFunc = context.GetRestClientConfig + getClusterInfo = context.GetClusterInfo +) + +const ( + VirtualBox = "virtualbox" + HyperKit = "hyperkit" +) + +type Client interface { + // IsMinikube returns true if the given kubeContext maps to a minikube cluster + IsMinikube(kubeContext string) bool + // MinikubeExec returns the Cmd struct to execute minikube with given arguments + MinikubeExec(arg ...string) (*exec.Cmd, error) +} + +type clientImpl struct{} + +func getClient() Client { + return clientImpl{} +} + +func (clientImpl) IsMinikube(kubeContext string) bool { + // short circuit if context is 'minikube' + if kubeContext == constants.DefaultMinikubeContext { + return true + } + if _, err := minikubeBinaryFunc(); err != nil { + logrus.Tracef("Minikube cluster not detected: %v", err) + return false + } + + if ok, err := matchClusterCertPath(kubeContext); err != nil { + logrus.Tracef("failed to match cluster certificate path: %v", err) + } else if ok { + logrus.Debugf("Minikube cluster detected: cluster certificate for context %q found inside the minikube directory", kubeContext) + return true + } + + if ok, err := matchProfileAndServerURL(kubeContext); err != nil { + logrus.Tracef("failed to match minikube profile: %v", err) + } else if ok { + logrus.Debugf("Minikube cluster detected: context %q has matching profile name or server url", kubeContext) + return true + } + logrus.Tracef("Minikube cluster not detected for context %q", kubeContext) + return false +} + +func (clientImpl) MinikubeExec(arg ...string) (*exec.Cmd, error) { + return minikubeExec(arg...) +} + +func minikubeExec(arg ...string) (*exec.Cmd, error) { + b, err := minikubeBinaryFunc() + if err != nil { + return nil, fmt.Errorf("getting minikube executable: %w", err) + } + return exec.Command(b, arg...), nil +} + +func minikubeBinary() (string, error) { + execName := "minikube" + if found, _ := util.DetectWSL(); found { + execName = "minikube.exe" + } + filename, err := exec.LookPath(execName) + if err != nil { + return "", errors.New("unable to find minikube executable. Please add it to PATH environment variable") + } + if _, err := os.Stat(filename); os.IsNotExist(err) { + return "", fmt.Errorf("unable to find minikube executable. File not found %s", filename) + } + return filename, nil +} + +// matchClusterCertPath checks if the cluster certificate for this context is from inside the minikube directory +func matchClusterCertPath(kubeContext string) (bool, error) { + c, err := getClusterInfo(kubeContext) + if err != nil { + return false, fmt.Errorf("getting kubernetes config: %w", err) + } + if c.CertificateAuthority == "" { + return false, nil + } + return util.IsSubPath(minikubePath(), c.CertificateAuthority), nil +} + +// matchProfileAndServerURL checks if kubecontext matches any valid minikube profile +// and for selected drivers if the k8s server url is same as any of the minikube nodes IPs +func matchProfileAndServerURL(kubeContext string) (bool, error) { + config, err := getRestClientConfigFunc() + if err != nil { + return false, fmt.Errorf("getting kubernetes config: %w", err) + } + apiServerURL, _, err := rest.DefaultServerURL(config.Host, config.APIPath, schema.GroupVersion{}, false) + + if err != nil { + return false, fmt.Errorf("getting kubernetes server url: %w", err) + } + + logrus.Tracef("kubernetes server url: %s", apiServerURL) + + ok, err := matchServerURLFor(kubeContext, apiServerURL) + if err != nil { + return false, fmt.Errorf("checking minikube node url: %w", err) + } + return ok, nil +} + +func matchServerURLFor(kubeContext string, serverURL *url.URL) (bool, error) { + cmd, _ := minikubeExec("profile", "list", "-o", "json") + out, err := util.RunCmdOut(cmd) + if err != nil { + return false, fmt.Errorf("getting minikube profiles: %w", err) + } + + var data data + if err = json.Unmarshal(out, &data); err != nil { + return false, fmt.Errorf("failed to unmarshal data: %w", err) + } + + for _, v := range data.Valid { + if v.Config.Name != kubeContext { + continue + } + + if v.Config.Driver != HyperKit && v.Config.Driver != VirtualBox { + // Since node IPs don't match server API for other drivers we assume profile name match is enough. + // TODO: Revisit once https://github.com/kubernetes/minikube/issues/6642 is fixed + return true, nil + } + for _, n := range v.Config.Nodes { + if serverURL.Host == fmt.Sprintf("%s:%d", n.IP, n.Port) { + return true, nil + } + } + } + return false, nil +} + +// minikubePath returns the path to the user's minikube dir +func minikubePath() string { + minikubeHomeEnv := os.Getenv("MINIKUBE_HOME") + if minikubeHomeEnv == "" { + return filepath.Join(homedir.HomeDir(), ".minikube") + } + if filepath.Base(minikubeHomeEnv) == ".minikube" { + return minikubeHomeEnv + } + return filepath.Join(minikubeHomeEnv, ".minikube") +} + +type data struct { + Valid []profile `json:"valid,omitempty"` + Invalid []profile `json:"invalid,omitempty"` +} + +type profile struct { + Config config +} + +type config struct { + Name string + Driver string + Nodes []node +} + +type node struct { + IP string + Port int32 +} diff --git a/pkg/skaffold/cluster/minikube_test.go b/pkg/skaffold/cluster/minikube_test.go new file mode 100644 index 00000000000..effa2a918d1 --- /dev/null +++ b/pkg/skaffold/cluster/minikube_test.go @@ -0,0 +1,131 @@ +/* +Copyright 2020 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 cluster + +import ( + "fmt" + "path/filepath" + "testing" + + "k8s.io/client-go/rest" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + "k8s.io/client-go/util/homedir" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" + "github.com/GoogleContainerTools/skaffold/testutil" +) + +func TestClientImpl_IsMinikube(t *testing.T) { + home := homedir.HomeDir() + tests := []struct { + description string + kubeContext string + clusterInfo clientcmdapi.Cluster + config rest.Config + minikubeProfileCmd util.Command + minikubeNotInPath bool + expected bool + }{ + { + description: "context is 'minikube'", + kubeContext: "minikube", + expected: true, + }, + { + description: "'minikube' binary not found", + kubeContext: "test-cluster", + minikubeNotInPath: true, + expected: false, + }, + { + description: "cluster cert inside minikube dir", + kubeContext: "test-cluster", + clusterInfo: clientcmdapi.Cluster{ + CertificateAuthority: filepath.Join(home, ".minikube", "ca.crt"), + }, + expected: true, + }, + { + description: "cluster cert outside minikube dir", + kubeContext: "test-cluster", + clusterInfo: clientcmdapi.Cluster{ + CertificateAuthority: filepath.Join(home, "foo", "ca.crt"), + }, + expected: false, + }, + { + description: "minikube profile name with docker driver matches kubeContext", + kubeContext: "test-cluster", + config: rest.Config{ + Host: "127.0.0.1:32768", + }, + minikubeProfileCmd: testutil.CmdRunOut("minikube profile list -o json", fmt.Sprintf(profileStr, "test-cluster", "docker", "172.17.0.3", 8443)), + expected: true, + }, + { + description: "minikube profile name with hyperkit driver node ip matches api server url", + kubeContext: "test-cluster", + config: rest.Config{ + Host: "192.168.64.10:8443", + }, + minikubeProfileCmd: testutil.CmdRunOut("minikube profile list -o json", fmt.Sprintf(profileStr, "test-cluster", "hyperkit", "192.168.64.10", 8443)), + expected: true, + }, + { + description: "minikube profile name different from kubeContext", + kubeContext: "test-cluster", + config: rest.Config{ + Host: "127.0.0.1:32768", + }, + minikubeProfileCmd: testutil.CmdRunOut("minikube profile list -o json", fmt.Sprintf(profileStr, "test-cluster2", "docker", "172.17.0.3", 8443)), + expected: false, + }, + { + description: "cannot parse minikube profile list", + kubeContext: "test-cluster", + minikubeProfileCmd: testutil.CmdRunOut("minikube profile list -o json", `{"foo":"bar"}`), + expected: false, + }, + { + description: "minikube with hyperkit driver node ip different from api server url", + kubeContext: "test-cluster", + config: rest.Config{ + Host: "192.168.64.10:8443", + }, + minikubeProfileCmd: testutil.CmdRunOut("minikube profile list -o json", fmt.Sprintf(profileStr, "test-cluster", "hyperkit", "192.168.64.11", 8443)), + expected: false, + }, + } + + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + if test.minikubeNotInPath { + t.Override(&minikubeBinaryFunc, func() (string, error) { return "", fmt.Errorf("minikube not in PATH") }) + } else { + t.Override(&minikubeBinaryFunc, func() (string, error) { return "minikube", nil }) + } + t.Override(&util.DefaultExecCommand, test.minikubeProfileCmd) + t.Override(&getRestClientConfigFunc, func() (*rest.Config, error) { return &test.config, nil }) + t.Override(&getClusterInfo, func(string) (*clientcmdapi.Cluster, error) { return &test.clusterInfo, nil }) + + ok := GetClient().IsMinikube(test.kubeContext) + t.CheckDeepEqual(test.expected, ok) + }) + } +} + +var profileStr = `{"invalid": [],"valid": [{"Name": "minikube","Status": "Stopped","Config": {"Name": "%s","Driver": "%s","Nodes": [{"Name": "","IP": "%s","Port": %d}]}}]}` diff --git a/pkg/skaffold/config/util.go b/pkg/skaffold/config/util.go index 49008d8a137..b86446bdda2 100644 --- a/pkg/skaffold/config/util.go +++ b/pkg/skaffold/config/util.go @@ -28,6 +28,7 @@ import ( "github.com/mitchellh/go-homedir" "github.com/sirupsen/logrus" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/cluster" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" kubectx "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/context" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" @@ -216,13 +217,11 @@ func GetDebugHelpersRegistry(configFile string) (string, error) { } func isDefaultLocal(kubeContext string) bool { - if kubeContext == constants.DefaultMinikubeContext || - kubeContext == constants.DefaultDockerForDesktopContext || - kubeContext == constants.DefaultDockerDesktopContext { - return true - } - - return IsKindCluster(kubeContext) || IsK3dCluster(kubeContext) + return kubeContext == constants.DefaultDockerForDesktopContext || + kubeContext == constants.DefaultDockerDesktopContext || + cluster.GetClient().IsMinikube(kubeContext) || + IsKindCluster(kubeContext) || + IsK3dCluster(kubeContext) } // IsImageLoadingRequired checks if the cluster requires loading images into it diff --git a/pkg/skaffold/docker/client.go b/pkg/skaffold/docker/client.go index 9b38463c1c5..f07a1c5e744 100644 --- a/pkg/skaffold/docker/client.go +++ b/pkg/skaffold/docker/client.go @@ -21,7 +21,6 @@ import ( "errors" "fmt" "net/http" - "os" "os/exec" "path/filepath" "sort" @@ -32,7 +31,7 @@ import ( "github.com/docker/go-connections/tlsconfig" "github.com/sirupsen/logrus" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/cluster" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/version" @@ -68,9 +67,12 @@ func NewAPIClientImpl(runCtx *runcontext.RunContext) (LocalDaemon, error) { // newAPIClient guesses the docker client to use based on current Kubernetes context. func newAPIClient(kubeContext string, minikubeProfile string) ([]string, client.CommonAPIClient, error) { - if kubeContext == constants.DefaultMinikubeContext || minikubeProfile != "" { + if minikubeProfile != "" { // skip validation if explicitly specifying minikubeProfile. return newMinikubeAPIClient(minikubeProfile) } + if cluster.GetClient().IsMinikube(kubeContext) { + return newMinikubeAPIClient(kubeContext) + } return newEnvAPIClient() } @@ -163,32 +165,14 @@ func getUserAgentHeader() map[string]string { } } -func getMiniKubeFilename() (string, error) { - if found, _ := util.DetectWSL(); found { - filename, err := exec.LookPath("minikube.exe") - if err != nil { - return "", errors.New("unable to find minikube.exe. Please add it to PATH environment variable") - } - if _, err := os.Stat(filename); os.IsNotExist(err) { - return "", fmt.Errorf("unable to find minikube.exe. File not found %s", filename) - } - return filename, nil - } - return "minikube", nil -} - func getMinikubeDockerEnv(minikubeProfile string) (map[string]string, error) { - miniKubeFilename, err := getMiniKubeFilename() - if err != nil { - return nil, fmt.Errorf("getting minikube filename: %w", err) + if minikubeProfile == "" { + return nil, fmt.Errorf("empty minikube profile") } - - args := []string{"docker-env", "--shell", "none"} - if minikubeProfile != "" { - args = append(args, "-p", minikubeProfile) + cmd, err := cluster.GetClient().MinikubeExec("docker-env", "--shell", "none", "-p", minikubeProfile) + if err != nil { + return nil, fmt.Errorf("executing minikube command: %w", err) } - - cmd := exec.Command(miniKubeFilename, args...) out, err := util.RunCmdOut(cmd) if err != nil { return nil, fmt.Errorf("getting minikube env: %w", err) diff --git a/pkg/skaffold/docker/client_test.go b/pkg/skaffold/docker/client_test.go index 248ab36195d..4dcefadf055 100644 --- a/pkg/skaffold/docker/client_test.go +++ b/pkg/skaffold/docker/client_test.go @@ -19,8 +19,10 @@ package docker import ( "errors" "fmt" + "os/exec" "testing" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/cluster" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/testutil" ) @@ -66,7 +68,7 @@ func TestNewMinikubeImageAPIClient(t *testing.T) { }{ { description: "correct client", - command: testutil.CmdRunOut("minikube docker-env --shell none", `DOCKER_TLS_VERIFY=1 + command: testutil.CmdRunOut("minikube docker-env --shell none -p minikube", `DOCKER_TLS_VERIFY=1 DOCKER_HOST=http://127.0.0.1:8080 DOCKER_CERT_PATH=testdata DOCKER_API_VERSION=1.23`), @@ -74,7 +76,7 @@ DOCKER_API_VERSION=1.23`), }, { description: "correct client - work around minikube #8615", - command: testutil.CmdRunOut("minikube docker-env --shell none", `DOCKER_TLS_VERIFY=1 + command: testutil.CmdRunOut("minikube docker-env --shell none -p minikube", `DOCKER_TLS_VERIFY=1 DOCKER_HOST=http://127.0.0.1:8080 DOCKER_CERT_PATH=testdata DOCKER_API_VERSION=1.23 @@ -86,7 +88,7 @@ DOCKER_API_VERSION=1.23 }, { description: "bad certificate", - command: testutil.CmdRunOut("minikube docker-env --shell none", `DOCKER_TLS_VERIFY=1 + command: testutil.CmdRunOut("minikube docker-env --shell none -p minikube", `DOCKER_TLS_VERIFY=1 DOCKER_HOST=http://127.0.0.1:8080 DOCKER_CERT_PATH=bad/cert/path DOCKER_API_VERSION=1.23`), @@ -94,21 +96,21 @@ DOCKER_API_VERSION=1.23`), }, { description: "missing host env, no error", - command: testutil.CmdRunOut("minikube docker-env --shell none", `DOCKER_TLS_VERIFY=1 + command: testutil.CmdRunOut("minikube docker-env --shell none -p minikube", `DOCKER_TLS_VERIFY=1 DOCKER_CERT_PATH=testdata DOCKER_API_VERSION=1.23`), expectedEnv: []string{"DOCKER_API_VERSION=1.23", "DOCKER_CERT_PATH=testdata", "DOCKER_TLS_VERIFY=1"}, }, { description: "missing version env, no error", - command: testutil.CmdRunOut("minikube docker-env --shell none", `DOCKER_TLS_VERIFY=1 + command: testutil.CmdRunOut("minikube docker-env --shell none -p minikube", `DOCKER_TLS_VERIFY=1 DOCKER_HOST=http://127.0.0.1:8080 DOCKER_CERT_PATH=testdata`), expectedEnv: []string{"DOCKER_CERT_PATH=testdata", "DOCKER_HOST=http://127.0.0.1:8080", "DOCKER_TLS_VERIFY=1"}, }, { description: "bad url", - command: testutil.CmdRunOut("minikube docker-env --shell none", `DOCKER_TLS_VERIFY=1 + command: testutil.CmdRunOut("minikube docker-env --shell none -p minikube", `DOCKER_TLS_VERIFY=1 DOCKER_HOST=badurl DOCKER_CERT_PATH=testdata DOCKER_API_VERSION=1.23`), @@ -116,7 +118,7 @@ DOCKER_API_VERSION=1.23`), }, { description: "allow `=` in urls", - command: testutil.CmdRunOut("minikube docker-env --shell none", `DOCKER_TLS_VERIFY=1 + command: testutil.CmdRunOut("minikube docker-env --shell none -p minikube", `DOCKER_TLS_VERIFY=1 DOCKER_HOST=http://127.0.0.1:8080?k=v DOCKER_CERT_PATH=testdata DOCKER_API_VERSION=1.23`), @@ -124,25 +126,26 @@ DOCKER_API_VERSION=1.23`), }, { description: "bad env output", - command: testutil.CmdRunOut("minikube docker-env --shell none", `DOCKER_TLS_VERIFY=1 + command: testutil.CmdRunOut("minikube docker-env --shell none -p minikube", `DOCKER_TLS_VERIFY=1 DOCKER_HOST`), shouldErr: true, }, { description: "command error", - command: testutil.CmdRunOutErr("minikube docker-env --shell none", "", errors.New("fail")), + command: testutil.CmdRunOutErr("minikube docker-env --shell none -p minikube", "", errors.New("fail")), shouldErr: true, }, { description: "minikube exit code 64 - fallback to host docker", - command: testutil.CmdRunOutErr("minikube docker-env --shell none", "", fmt.Errorf("fail: %w", &badUsageErr{})), + command: testutil.CmdRunOutErr("minikube docker-env --shell none -p minikube", "", fmt.Errorf("fail: %w", &badUsageErr{})), }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { t.Override(&util.DefaultExecCommand, test.command) + t.Override(&cluster.GetClient, func() cluster.Client { return fakeMinikubeClient{} }) - env, _, err := newMinikubeAPIClient("") + env, _, err := newMinikubeAPIClient("minikube") t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expectedEnv, env) }) @@ -153,3 +156,10 @@ type badUsageErr struct{} func (e *badUsageErr) Error() string { return "bad usage" } func (e *badUsageErr) ExitCode() int { return 64 } + +type fakeMinikubeClient struct{} + +func (fakeMinikubeClient) IsMinikube(string) bool { return false } +func (fakeMinikubeClient) MinikubeExec(arg ...string) (*exec.Cmd, error) { + return exec.Command("minikube", arg...), nil +} diff --git a/pkg/skaffold/kubernetes/context/context.go b/pkg/skaffold/kubernetes/context/context.go index 591d26cc021..56c75e433a0 100644 --- a/pkg/skaffold/kubernetes/context/context.go +++ b/pkg/skaffold/kubernetes/context/context.go @@ -70,6 +70,19 @@ func GetRestClientConfig() (*restclient.Config, error) { return getRestClientConfig(kubeContext, kubeConfigFile) } +// GetClusterInfo returns the Cluster information for the given kubeContext +func GetClusterInfo(kctx string) (*clientcmdapi.Cluster, error) { + rawConfig, err := getCurrentConfig() + if err != nil { + return nil, err + } + c, found := rawConfig.Clusters[kctx] + if !found { + return nil, fmt.Errorf("failed to get cluster info for kubeContext: `%s`", kctx) + } + return c, nil +} + func getRestClientConfig(kctx string, kcfg string) (*restclient.Config, error) { logrus.Debugf("getting client config for kubeContext: `%s`", kctx) diff --git a/pkg/skaffold/util/util.go b/pkg/skaffold/util/util.go index d1554865b43..2e6cb4426b1 100644 --- a/pkg/skaffold/util/util.go +++ b/pkg/skaffold/util/util.go @@ -329,6 +329,15 @@ func IsHiddenFile(filename string) bool { return hasHiddenPrefix(filename) } +// IsSubPath return true if targetpath is sub-path of basepath; doesn't check for symlinks +func IsSubPath(basepath string, targetpath string) bool { + rel, err := filepath.Rel(basepath, targetpath) + if err != nil { + return false + } + return rel != ".." && !strings.HasPrefix(rel, ".."+string(os.PathSeparator)) +} + func hasHiddenPrefix(s string) bool { return strings.HasPrefix(s, hiddenPrefix) } diff --git a/pkg/skaffold/util/util_test.go b/pkg/skaffold/util/util_test.go index 74cb471d862..d1c593463c4 100644 --- a/pkg/skaffold/util/util_test.go +++ b/pkg/skaffold/util/util_test.go @@ -20,6 +20,8 @@ import ( "path/filepath" "testing" + "github.com/mitchellh/go-homedir" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/testutil" ) @@ -369,6 +371,41 @@ func TestFormatMapToStringSlice2(t *testing.T) { } } +func TestIsSubPath(t *testing.T) { + home, _ := homedir.Dir() + tests := []struct { + description string + basePath string + targetPath string + expected bool + }{ + { + description: "target path within base path", + basePath: filepath.Join(home, ".minikube"), + targetPath: filepath.Join(home, ".minikube", "ca.crt"), + expected: true, + }, + { + description: "target path outside base path", + basePath: filepath.Join(home, "bar"), + targetPath: filepath.Join(home, "foo", "bar"), + expected: false, + }, + { + description: "base path inside target path", + basePath: filepath.Join(home, "foo", "bar"), + targetPath: filepath.Join(home, "foo"), + expected: false, + }, + } + + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + t.CheckDeepEqual(test.expected, IsSubPath(test.basePath, test.targetPath)) + }) + } +} + func TestIsFileIsDir(t *testing.T) { tmpDir := testutil.NewTempDir(t).Touch("file") From 7d8625404c92fb0f94b55f2152d9983498cc0a8e Mon Sep 17 00:00:00 2001 From: Yuwen Ma Date: Fri, 28 Aug 2020 10:50:35 -0700 Subject: [PATCH 116/138] Change kpt deployer doc from Beta to Alpha (#4728) * Change kpt deployer doc from Beta to Alpha * change config docstring instead of schema json. --- docs/content/en/schemas/v2beta7.json | 8 ++++---- pkg/skaffold/schema/latest/config.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/content/en/schemas/v2beta7.json b/docs/content/en/schemas/v2beta7.json index ef1aabff5da..b671b54a39b 100755 --- a/docs/content/en/schemas/v2beta7.json +++ b/docs/content/en/schemas/v2beta7.json @@ -830,8 +830,8 @@ }, "kpt": { "$ref": "#/definitions/KptDeploy", - "description": "*beta* uses the `kpt` CLI to manage and deploy manifests.", - "x-intellij-html-description": "beta uses the kpt CLI to manage and deploy manifests." + "description": "*alpha* uses the `kpt` CLI to manage and deploy manifests.", + "x-intellij-html-description": "alpha uses the kpt CLI to manage and deploy manifests." }, "kubeContext": { "type": "string", @@ -1629,8 +1629,8 @@ "live" ], "additionalProperties": false, - "description": "*beta* uses the `kpt` CLI to manage and deploy manifests.", - "x-intellij-html-description": "beta uses the kpt CLI to manage and deploy manifests." + "description": "*alpha* uses the `kpt` CLI to manage and deploy manifests.", + "x-intellij-html-description": "alpha uses the kpt CLI to manage and deploy manifests." }, "KptFn": { "properties": { diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 20a14fd5723..4c76c182c9b 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -447,7 +447,7 @@ type DeployType struct { // HelmDeploy *beta* uses the `helm` CLI to apply the charts to the cluster. HelmDeploy *HelmDeploy `yaml:"helm,omitempty"` - // KptDeploy *beta* uses the `kpt` CLI to manage and deploy manifests. + // KptDeploy *alpha* uses the `kpt` CLI to manage and deploy manifests. KptDeploy *KptDeploy `yaml:"kpt,omitempty"` // KubectlDeploy *beta* uses a client side `kubectl apply` to deploy manifests. @@ -526,7 +526,7 @@ type KustomizeDeploy struct { BuildArgs []string `yaml:"buildArgs,omitempty"` } -// KptDeploy *beta* uses the `kpt` CLI to manage and deploy manifests. +// KptDeploy *alpha* uses the `kpt` CLI to manage and deploy manifests. type KptDeploy struct { // Dir is the path to the directory to run kpt functions against. Dir string `yaml:"dir,omitempty"` From 53dffe1c9fbe488899635a8be43834a527f5f81c Mon Sep 17 00:00:00 2001 From: Tejal Desai Date: Fri, 28 Aug 2020 12:13:08 -0700 Subject: [PATCH 117/138] add pod initialization logic in diag and follow up some minor reporting changes. (#4690) * add pod initialization logic in diag. better reporting * mute logs for status check by default * add struct for statusChecker and fix/add tests * add struct for statusChecker and fix/add tests * fix windows unit tests * Apply suggestions from code review Co-authored-by: Nick Kubala * move to constant * address brian's comment * Rename to STATUSCHECK_USER_CANCELLED * fix test usage of StatusCode_STATUSCHECK_CONTEXT_CANCELLED * fix imports Co-authored-by: Nick Kubala --- docs/content/en/api/skaffold.swagger.json | 48 +-- docs/content/en/docs/references/api/grpc.md | 2 +- pkg/diag/validator/pod.go | 30 +- pkg/diag/validator/pod_test.go | 39 ++ pkg/skaffold/deploy/resource/deployment.go | 60 ++- .../deploy/resource/deployment_test.go | 170 ++++++--- pkg/skaffold/deploy/resource/logfile.go | 50 +++ pkg/skaffold/deploy/status_check.go | 129 ++++--- pkg/skaffold/deploy/status_check_test.go | 69 ++-- pkg/skaffold/runner/deploy.go | 4 +- pkg/skaffold/runner/deploy_test.go | 21 +- pkg/skaffold/runner/runner.go | 2 +- proto/skaffold.pb.go | 358 +++++++++--------- proto/skaffold.proto | 2 +- 14 files changed, 595 insertions(+), 389 deletions(-) create mode 100644 pkg/skaffold/deploy/resource/logfile.go diff --git a/docs/content/en/api/skaffold.swagger.json b/docs/content/en/api/skaffold.swagger.json index 9b53768d1b9..95fffcc473c 100644 --- a/docs/content/en/api/skaffold.swagger.json +++ b/docs/content/en/api/skaffold.swagger.json @@ -151,7 +151,7 @@ }, { "name": "event.buildEvent.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_USER_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -197,14 +197,14 @@ "DEVINIT_REGISTER_TEST_DEPS", "DEVINIT_REGISTER_DEPLOY_DEPS", "DEVINIT_REGISTER_CONFIG_DEP", - "STATUSCHECK_CONTEXT_CANCELLED", + "STATUSCHECK_USER_CANCELLED", "STATUSCHECK_DEADLINE_EXCEEDED" ], "default": "OK" }, { "name": "event.buildEvent.actionableErr.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_USER_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -250,7 +250,7 @@ "DEVINIT_REGISTER_TEST_DEPS", "DEVINIT_REGISTER_DEPLOY_DEPS", "DEVINIT_REGISTER_CONFIG_DEP", - "STATUSCHECK_CONTEXT_CANCELLED", + "STATUSCHECK_USER_CANCELLED", "STATUSCHECK_DEADLINE_EXCEEDED" ], "default": "OK" @@ -275,7 +275,7 @@ }, { "name": "event.deployEvent.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_USER_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -321,14 +321,14 @@ "DEVINIT_REGISTER_TEST_DEPS", "DEVINIT_REGISTER_DEPLOY_DEPS", "DEVINIT_REGISTER_CONFIG_DEP", - "STATUSCHECK_CONTEXT_CANCELLED", + "STATUSCHECK_USER_CANCELLED", "STATUSCHECK_DEADLINE_EXCEEDED" ], "default": "OK" }, { "name": "event.deployEvent.actionableErr.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_USER_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -374,7 +374,7 @@ "DEVINIT_REGISTER_TEST_DEPS", "DEVINIT_REGISTER_DEPLOY_DEPS", "DEVINIT_REGISTER_CONFIG_DEP", - "STATUSCHECK_CONTEXT_CANCELLED", + "STATUSCHECK_USER_CANCELLED", "STATUSCHECK_DEADLINE_EXCEEDED" ], "default": "OK" @@ -461,7 +461,7 @@ }, { "name": "event.statusCheckEvent.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_USER_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -507,14 +507,14 @@ "DEVINIT_REGISTER_TEST_DEPS", "DEVINIT_REGISTER_DEPLOY_DEPS", "DEVINIT_REGISTER_CONFIG_DEP", - "STATUSCHECK_CONTEXT_CANCELLED", + "STATUSCHECK_USER_CANCELLED", "STATUSCHECK_DEADLINE_EXCEEDED" ], "default": "OK" }, { "name": "event.statusCheckEvent.actionableErr.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_USER_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -560,7 +560,7 @@ "DEVINIT_REGISTER_TEST_DEPS", "DEVINIT_REGISTER_DEPLOY_DEPS", "DEVINIT_REGISTER_CONFIG_DEP", - "STATUSCHECK_CONTEXT_CANCELLED", + "STATUSCHECK_USER_CANCELLED", "STATUSCHECK_DEADLINE_EXCEEDED" ], "default": "OK" @@ -597,7 +597,7 @@ }, { "name": "event.resourceStatusCheckEvent.statusCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_USER_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -643,14 +643,14 @@ "DEVINIT_REGISTER_TEST_DEPS", "DEVINIT_REGISTER_DEPLOY_DEPS", "DEVINIT_REGISTER_CONFIG_DEP", - "STATUSCHECK_CONTEXT_CANCELLED", + "STATUSCHECK_USER_CANCELLED", "STATUSCHECK_DEADLINE_EXCEEDED" ], "default": "OK" }, { "name": "event.resourceStatusCheckEvent.actionableErr.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_USER_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -696,7 +696,7 @@ "DEVINIT_REGISTER_TEST_DEPS", "DEVINIT_REGISTER_DEPLOY_DEPS", "DEVINIT_REGISTER_CONFIG_DEP", - "STATUSCHECK_CONTEXT_CANCELLED", + "STATUSCHECK_USER_CANCELLED", "STATUSCHECK_DEADLINE_EXCEEDED" ], "default": "OK" @@ -734,7 +734,7 @@ }, { "name": "event.fileSyncEvent.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_USER_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -780,14 +780,14 @@ "DEVINIT_REGISTER_TEST_DEPS", "DEVINIT_REGISTER_DEPLOY_DEPS", "DEVINIT_REGISTER_CONFIG_DEP", - "STATUSCHECK_CONTEXT_CANCELLED", + "STATUSCHECK_USER_CANCELLED", "STATUSCHECK_DEADLINE_EXCEEDED" ], "default": "OK" }, { "name": "event.fileSyncEvent.actionableErr.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_USER_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -833,7 +833,7 @@ "DEVINIT_REGISTER_TEST_DEPS", "DEVINIT_REGISTER_DEPLOY_DEPS", "DEVINIT_REGISTER_CONFIG_DEP", - "STATUSCHECK_CONTEXT_CANCELLED", + "STATUSCHECK_USER_CANCELLED", "STATUSCHECK_DEADLINE_EXCEEDED" ], "default": "OK" @@ -901,7 +901,7 @@ }, { "name": "event.devLoopEvent.err.errCode", - "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", + "description": " - OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_USER_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded", "in": "query", "required": false, "type": "string", @@ -947,7 +947,7 @@ "DEVINIT_REGISTER_TEST_DEPS", "DEVINIT_REGISTER_DEPLOY_DEPS", "DEVINIT_REGISTER_CONFIG_DEP", - "STATUSCHECK_CONTEXT_CANCELLED", + "STATUSCHECK_USER_CANCELLED", "STATUSCHECK_DEADLINE_EXCEEDED" ], "default": "OK" @@ -1654,11 +1654,11 @@ "DEVINIT_REGISTER_TEST_DEPS", "DEVINIT_REGISTER_DEPLOY_DEPS", "DEVINIT_REGISTER_CONFIG_DEP", - "STATUSCHECK_CONTEXT_CANCELLED", + "STATUSCHECK_USER_CANCELLED", "STATUSCHECK_DEADLINE_EXCEEDED" ], "default": "OK", - "description": "Enum for Status codes\nThese error codes are prepended by Phase Name e.g.\nBUILD, DEPLOY, STATUSCHECK, DEVINIT\n- OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_CONTEXT_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded" + "description": "Enum for Status codes\nThese error codes are prepended by Phase Name e.g.\nBUILD, DEPLOY, STATUSCHECK, DEVINIT\n- OK: A default status code for events that do not have an associated phase.\nTypically seen with the DevEndEvent event on success.\n - STATUSCHECK_SUCCESS: Status Check Success\n - BUILD_SUCCESS: Build Success\n - DEPLOY_SUCCESS: Deploy Success\n - BUILD_PUSH_ACCESS_DENIED: Build error due to push access denied\n - BUILD_PROJECT_NOT_FOUND: Build error due to GCP project not found.\n - STATUSCHECK_IMAGE_PULL_ERR: Container image pull error\n - STATUSCHECK_CONTAINER_CREATING: Container creating error\n - STATUSCHECK_RUN_CONTAINER_ERR: Container run error\n - STATUSCHECK_CONTAINER_TERMINATED: Container is already terminated\n - STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING: Deployment waiting for rollout\n - STATUSCHECK_CONTAINER_RESTARTING: Container restarting error\n - STATUSCHECK_UNHEALTHY: Readiness probe failed\n - STATUSCHECK_NODE_MEMORY_PRESSURE: Node memory pressure error\n - STATUSCHECK_NODE_DISK_PRESSURE: Node disk pressure error\n - STATUSCHECK_NODE_NETWORK_UNAVAILABLE: Node network unavailable error\n - STATUSCHECK_NODE_PID_PRESSURE: Node PID pressure error\n - STATUSCHECK_NODE_UNSCHEDULABLE: Node unschedulable error\n - STATUSCHECK_NODE_UNREACHABLE: Node unreachable error\n - STATUSCHECK_NODE_NOT_READY: Node not ready error\n - STATUSCHECK_FAILED_SCHEDULING: Scheduler failure error\n - STATUSCHECK_KUBECTL_CONNECTION_ERR: Kubectl connection error\n - STATUSCHECK_KUBECTL_PID_KILLED: Kubectl process killed error\n - STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR: Kubectl client fetch err\n - STATUSCHECK_POD_INITIALIZING: Pod Initializing\n - UNKNOWN_ERROR: Could not determine error and phase\n - STATUSCHECK_UNKNOWN: Status Check error unknown\n - STATUSCHECK_UNKNOWN_UNSCHEDULABLE: Container is unschedulable due to unknown reasons\n - STATUSCHECK_CONTAINER_WAITING_UNKNOWN: Container is waiting due to unknown reason\n - STATUSCHECK_UNKNOWN_EVENT: Container event reason unknown\n - DEPLOY_UNKNOWN: Deploy failed due to unknown reason\n - SYNC_UNKNOWN: SYNC failed due to known reason\n - BUILD_UNKNOWN: Build failed due to unknown reason\n - DEVINIT_UNKNOWN: Dev Init failed due to unknown reason\n - CLEANUP_UNKNOWN: Cleanup failed due to unknown reason\n - SYNC_INIT_ERROR: File Sync Initialize failure\n - DEVINIT_REGISTER_BUILD_DEPS: Failed to configure watcher for build dependencies in dev loop\n - DEVINIT_REGISTER_TEST_DEPS: Failed to configure watcher for test dependencies in dev loop\n - DEVINIT_REGISTER_DEPLOY_DEPS: Failed to configure watcher for deploy dependencies in dev loop\n - DEVINIT_REGISTER_CONFIG_DEP: Failed to configure watcher for Skaffold configuration file.\n - STATUSCHECK_USER_CANCELLED: User cancelled the skaffold dev run\n - STATUSCHECK_DEADLINE_EXCEEDED: Deadline for status check exceeded" }, "protoSuggestion": { "type": "object", diff --git a/docs/content/en/docs/references/api/grpc.md b/docs/content/en/docs/references/api/grpc.md index cf936de0f7f..80374a073d9 100644 --- a/docs/content/en/docs/references/api/grpc.md +++ b/docs/content/en/docs/references/api/grpc.md @@ -790,7 +790,7 @@ BUILD, DEPLOY, STATUSCHECK, DEVINIT | DEVINIT_REGISTER_TEST_DEPS | 702 | Failed to configure watcher for test dependencies in dev loop | | DEVINIT_REGISTER_DEPLOY_DEPS | 703 | Failed to configure watcher for deploy dependencies in dev loop | | DEVINIT_REGISTER_CONFIG_DEP | 704 | Failed to configure watcher for Skaffold configuration file. | -| STATUSCHECK_CONTEXT_CANCELLED | 800 | User cancelled the skaffold dev run | +| STATUSCHECK_USER_CANCELLED | 800 | User cancelled the skaffold dev run | | STATUSCHECK_DEADLINE_EXCEEDED | 801 | Deadline for status check exceeded | diff --git a/pkg/diag/validator/pod.go b/pkg/diag/validator/pod.go index ed73b3570ff..ee7d22efab8 100644 --- a/pkg/diag/validator/pod.go +++ b/pkg/diag/validator/pod.go @@ -64,6 +64,7 @@ var ( proto.StatusCode_STATUSCHECK_CONTAINER_WAITING_UNKNOWN: {}, proto.StatusCode_STATUSCHECK_UNKNOWN_UNSCHEDULABLE: {}, proto.StatusCode_STATUSCHECK_SUCCESS: {}, + proto.StatusCode_STATUSCHECK_POD_INITIALIZING: {}, } ) @@ -131,7 +132,18 @@ func getPodStatus(pod *v1.Pod) (proto.StatusCode, []string, error) { // TODO(dgageot): Add EphemeralContainerStatuses cs := append(pod.Status.InitContainerStatuses, pod.Status.ContainerStatuses...) // See https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-states - return getContainerStatus(pod, cs) + statusCode, logs, err := getContainerStatus(pod, cs) + if statusCode == proto.StatusCode_STATUSCHECK_POD_INITIALIZING { + // Determine if an init container is still running and fetch the init logs. + for _, c := range pod.Status.InitContainerStatuses { + if c.State.Waiting != nil { + return statusCode, []string{}, fmt.Errorf("waiting for init container %s to start", c.Name) + } else if c.State.Running != nil { + return statusCode, getPodLogs(pod, c.Name), fmt.Errorf("waiting for init container %s to complete", c.Name) + } + } + } + return statusCode, logs, err case v1.ConditionUnknown: logrus.Debugf("Pod %q scheduling condition is unknown", pod.Name) return proto.StatusCode_STATUSCHECK_UNKNOWN, nil, fmt.Errorf(c.Message) @@ -288,7 +300,8 @@ func extractErrorMessageFromWaitingContainerStatus(po *v1.Pod, c v1.ContainerSta // Extract meaning full error out of container statuses. switch c.State.Waiting.Reason { case podInitializing: - // container is waiting to run + // container is waiting to run. This could be because one of the init containers is + // still not completed return proto.StatusCode_STATUSCHECK_POD_INITIALIZING, nil, nil case containerCreating: return proto.StatusCode_STATUSCHECK_CONTAINER_CREATING, nil, fmt.Errorf("creating container %s", c.Name) @@ -330,13 +343,14 @@ func getPodLogs(po *v1.Pod, c string) []string { if err != nil { return []string{fmt.Sprintf("Error retrieving logs for pod %s. Try `%s`", po.Name, strings.Join(logCommand, " "))} } - lines := strings.Split(string(logs), "\n") + output := strings.Split(string(logs), "\n") // remove spurious empty lines (empty string or from trailing newline) - if len(lines) > 0 && len(lines[len(lines)-1]) == 0 { - lines = lines[:len(lines)-1] - } - for i, s := range lines { - lines[i] = fmt.Sprintf("[%s %s] %s", po.Name, c, s) + lines := make([]string, 0, len(output)) + for _, s := range output { + if s == "" { + continue + } + lines = append(lines, fmt.Sprintf("[%s %s] %s", po.Name, c, s)) } return lines } diff --git a/pkg/diag/validator/pod_test.go b/pkg/diag/validator/pod_test.go index b3ca594e204..97fbea8e861 100644 --- a/pkg/diag/validator/pod_test.go +++ b/pkg/diag/validator/pod_test.go @@ -598,6 +598,45 @@ func TestRun(t *testing.T) { ErrCode: proto.StatusCode_STATUSCHECK_CONTAINER_RESTARTING, }, []string{"[foo foo-container] some panic"})}, }, + { + description: "pod condition with events when pod is in Initializing phase", + pods: []*v1.Pod{{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "test", + }, + TypeMeta: metav1.TypeMeta{Kind: "Pod"}, + Status: v1.PodStatus{ + Phase: v1.PodPending, + Conditions: []v1.PodCondition{{ + Type: v1.PodScheduled, + Status: v1.ConditionTrue, + }}, + ContainerStatuses: []v1.ContainerStatus{ + { + Name: "foo-container", + Image: "foo-image", + State: v1.ContainerState{ + Waiting: &v1.ContainerStateWaiting{Reason: "PodInitializing", + Message: "waiting to initialize", + }, + }, + }, + }, + }, + }}, + events: []v1.Event{ + { + ObjectMeta: metav1.ObjectMeta{Namespace: "test"}, + Reason: "eventCode", Type: "Warning", Message: "dummy event", + }, + }, + expected: []Resource{NewResource("test", "Pod", "foo", "Pending", + proto.ActionableErr{ + Message: "eventCode: dummy event", + ErrCode: proto.StatusCode_STATUSCHECK_UNKNOWN_EVENT, + }, nil)}, + }, } for _, test := range tests { diff --git a/pkg/skaffold/deploy/resource/deployment.go b/pkg/skaffold/deploy/resource/deployment.go index f9b068cd1e8..1ba4d5dab25 100644 --- a/pkg/skaffold/deploy/resource/deployment.go +++ b/pkg/skaffold/deploy/resource/deployment.go @@ -40,6 +40,7 @@ const ( defaultPodCheckDeadline = 30 * time.Second tabHeader = " -" tab = " " + maxLogLines = 3 ) var ( @@ -59,7 +60,7 @@ type Deployment struct { namespace string rType string status Status - StatusCode proto.StatusCode + statusCode proto.StatusCode done bool deadline time.Duration pods map[string]validator.Resource @@ -77,6 +78,7 @@ func (d *Deployment) UpdateStatus(ae proto.ActionableErr) { return } d.status = updated + d.statusCode = updated.ActionableError().ErrCode d.status.changed = true if ae.ErrCode == proto.StatusCode_STATUSCHECK_SUCCESS || isErrAndNotRetryAble(ae.ErrCode) { d.done = true @@ -136,19 +138,28 @@ func (d *Deployment) Status() Status { return d.status } -func (d *Deployment) IsStatusCheckComplete() bool { - return d.done +func (d *Deployment) IsStatusCheckCompleteOrCancelled() bool { + return d.done || d.statusCode == proto.StatusCode_STATUSCHECK_USER_CANCELLED +} + +func (d *Deployment) StatusMessage() string { + for _, p := range d.pods { + if s := p.ActionableError(); s.ErrCode != proto.StatusCode_STATUSCHECK_SUCCESS { + return fmt.Sprintf("%s\n", s.Message) + } + } + return d.status.String() } func (d *Deployment) MarkComplete() { d.done = true } -// This returns a string representing deployment status along with tab header +// ReportSinceLastUpdated returns a string representing deployment status along with tab header // e.g. // - testNs:deployment/leeroy-app: waiting for rollout to complete. (1/2) pending // - testNs:pod/leeroy-app-xvbg : error pulling container image -func (d *Deployment) ReportSinceLastUpdated() string { +func (d *Deployment) ReportSinceLastUpdated(isMuted bool) string { if d.status.reported && !d.status.changed { return "" } @@ -165,15 +176,24 @@ func (d *Deployment) ReportSinceLastUpdated() string { for _, p := range d.pods { if s := p.ActionableError().Message; s != "" { result.WriteString(fmt.Sprintf("%s %s %s: %s\n", tab, tabHeader, p, s)) - for _, l := range p.Logs() { - result.WriteString(fmt.Sprintf("%s\n", l)) + // if logs are muted, write container logs to file and last 3 lines to + // result. + out, writeTrimLines, err := withLogFile(p.Name(), &result, p.Logs(), isMuted) + if err != nil { + logrus.Debugf("could not create log file %v", err) + } + trimLines := []string{} + for i, l := range p.Logs() { + formattedLine := fmt.Sprintf("%s %s > %s\n", tab, tab, strings.TrimSuffix(l, "\n")) + if isMuted && i >= len(p.Logs())-maxLogLines { + trimLines = append(trimLines, formattedLine) + } + out.Write([]byte(formattedLine)) } + writeTrimLines(trimLines) } } - if result.String() == "" { - return "" - } - return fmt.Sprintf("%s %s: %s%s", tabHeader, d, d.status, result.String()) + return fmt.Sprintf("%s %s: %s%s", tabHeader, d, d.StatusMessage(), result.String()) } func (d *Deployment) cleanupStatus(msg string) string { @@ -253,7 +273,8 @@ func (d *Deployment) fetchPods(ctx context.Context) error { if !found || originalPod.StatusUpdated(p) { d.status.changed = true switch p.ActionableError().ErrCode { - case proto.StatusCode_STATUSCHECK_CONTAINER_CREATING: + case proto.StatusCode_STATUSCHECK_CONTAINER_CREATING, + proto.StatusCode_STATUSCHECK_POD_INITIALIZING: event.ResourceStatusCheckEventUpdated(p.String(), p.ActionableError()) default: event.ResourceStatusCheckEventCompleted(p.String(), p.ActionableError()) @@ -265,18 +286,21 @@ func (d *Deployment) fetchPods(ctx context.Context) error { return nil } -// Return first pod status in error. -// TODO: should we return all distinct error codes in future? -func (d *Deployment) FirstPodErrOccurred() proto.StatusCode { - if len(d.pods) == 0 { - return d.Status().ActionableError().ErrCode +// StatusCode() returns the deployment status code if the status check is cancelled +// or if no pod data exists for this deployment. +// If pods are fetched, this function returns the error code a pod container encountered. +func (d *Deployment) StatusCode() proto.StatusCode { + // do not process pod status codes if another deployment failed + // or the user aborted the run. + if d.statusCode == proto.StatusCode_STATUSCHECK_USER_CANCELLED { + return d.statusCode } for _, p := range d.pods { if s := p.ActionableError().ErrCode; s != proto.StatusCode_STATUSCHECK_SUCCESS { return s } } - return proto.StatusCode_STATUSCHECK_SUCCESS + return d.statusCode } func (d *Deployment) WithPodStatuses(scs []proto.StatusCode) *Deployment { diff --git a/pkg/skaffold/deploy/resource/deployment_test.go b/pkg/skaffold/deploy/resource/deployment_test.go index 4f904d9c69e..df4e3615e18 100644 --- a/pkg/skaffold/deploy/resource/deployment_test.go +++ b/pkg/skaffold/deploy/resource/deployment_test.go @@ -19,6 +19,9 @@ package resource import ( "context" "errors" + "fmt" + "os" + "path/filepath" "testing" "github.com/GoogleContainerTools/skaffold/pkg/diag/validator" @@ -35,6 +38,7 @@ func TestDeploymentCheckStatus(t *testing.T) { commands util.Command expectedErr string expectedDetails string + cancelled bool complete bool }{ { @@ -80,6 +84,17 @@ func TestDeploymentCheckStatus(t *testing.T) { ), expectedErr: MsgKubectlConnection, }, + { + description: "set status to cancel", + commands: testutil.CmdRunOutErr( + rolloutCmd, + "", + errors.New("waiting for replicas to be available"), + ), + cancelled: true, + complete: true, + expectedErr: "context cancelled", + }, } for _, test := range tests { @@ -91,7 +106,13 @@ func TestDeploymentCheckStatus(t *testing.T) { } r.CheckStatus(context.Background(), runCtx) - t.CheckDeepEqual(test.complete, r.IsStatusCheckComplete()) + if test.cancelled { + r.UpdateStatus(proto.ActionableErr{ + ErrCode: proto.StatusCode_STATUSCHECK_USER_CANCELLED, + Message: "context cancelled", + }) + } + t.CheckDeepEqual(test.complete, r.IsStatusCheckCompleteOrCancelled()) if test.expectedErr != "" { t.CheckErrorContains(test.expectedErr, r.Status().Error()) } else { @@ -171,7 +192,7 @@ func TestIsErrAndNotRetriable(t *testing.T) { }, { description: "rollout status parent context canceled", - statusCode: proto.StatusCode_STATUSCHECK_CONTEXT_CANCELLED, + statusCode: proto.StatusCode_STATUSCHECK_USER_CANCELLED, expected: true, }, { @@ -194,112 +215,151 @@ func TestIsErrAndNotRetriable(t *testing.T) { } func TestReportSinceLastUpdated(t *testing.T) { - pods := map[string]validator.Resource{ - "foo": validator.NewResource( - "test", - "pod", - "foo", - "Pending", - proto.ActionableErr{ - ErrCode: proto.StatusCode_STATUSCHECK_IMAGE_PULL_ERR, - Message: "image cant be pulled"}, - []string{}, - ), - } + tmpDir := filepath.Clean(os.TempDir()) var tests = []struct { - description string - ae proto.ActionableErr - pods map[string]validator.Resource - expected string + description string + ae proto.ActionableErr + logs []string + expected string + expectedMute string }{ { - description: "updating an error status", - ae: proto.ActionableErr{Message: "cannot pull image\n"}, - pods: pods, - expected: ` - test-ns:deployment/test: cannot pull image - - test:pod/foo: image cant be pulled + description: "logs more than 3 lines", + ae: proto.ActionableErr{Message: "waiting for 0/1 deplotment to rollout\n"}, + logs: []string{ + "[pod container] Waiting for mongodb to start...", + "[pod container] Waiting for connection for 2 sec", + "[pod container] Retrying 1st attempt ....", + "[pod container] Waiting for connection for 2 sec", + "[pod container] Terminating with exit code 11", + }, + expectedMute: fmt.Sprintf(` - test-ns:deployment/test: container terminated with exit code 11 + - test:pod/foo: container terminated with exit code 11 + > [pod container] Retrying 1st attempt .... + > [pod container] Waiting for connection for 2 sec + > [pod container] Terminating with exit code 11 + Full logs at %s +`, filepath.Join(tmpDir, "skaffold", "statuscheck", "foo.log")), + expected: ` - test-ns:deployment/test: container terminated with exit code 11 + - test:pod/foo: container terminated with exit code 11 + > [pod container] Waiting for mongodb to start... + > [pod container] Waiting for connection for 2 sec + > [pod container] Retrying 1st attempt .... + > [pod container] Waiting for connection for 2 sec + > [pod container] Terminating with exit code 11 `, }, { - description: "updating a non error status", - ae: proto.ActionableErr{Message: "waiting for container\n"}, - pods: pods, - expected: ` - test-ns:deployment/test: waiting for container - - test:pod/foo: image cant be pulled + description: "logs less than 3 lines", + ae: proto.ActionableErr{Message: "waiting for 0/1 deplotment to rollout\n"}, + logs: []string{ + "[pod container] Waiting for mongodb to start...", + "[pod container] Waiting for connection for 2 sec", + "[pod container] Terminating with exit code 11", + }, + expected: ` - test-ns:deployment/test: container terminated with exit code 11 + - test:pod/foo: container terminated with exit code 11 + > [pod container] Waiting for mongodb to start... + > [pod container] Waiting for connection for 2 sec + > [pod container] Terminating with exit code 11 +`, + expectedMute: ` - test-ns:deployment/test: container terminated with exit code 11 + - test:pod/foo: container terminated with exit code 11 + > [pod container] Waiting for mongodb to start... + > [pod container] Waiting for connection for 2 sec + > [pod container] Terminating with exit code 11 `, }, { - description: "updating a non error status", - ae: proto.ActionableErr{Message: "waiting for container\n"}, - expected: "", + description: "no logs or empty", + ae: proto.ActionableErr{Message: "waiting for 0/1 deplotment to rollout\n"}, + expected: ` - test-ns:deployment/test: container terminated with exit code 11 + - test:pod/foo: container terminated with exit code 11 +`, + expectedMute: ` - test-ns:deployment/test: container terminated with exit code 11 + - test:pod/foo: container terminated with exit code 11 +`, }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { dep := NewDeployment("test", "test-ns", 1) - dep.pods = test.pods + dep.pods = map[string]validator.Resource{ + "foo": validator.NewResource( + "test", + "pod", + "foo", + "Pending", + proto.ActionableErr{ + ErrCode: proto.StatusCode_STATUSCHECK_RUN_CONTAINER_ERR, + Message: "container terminated with exit code 11"}, + test.logs, + ), + } dep.UpdateStatus(test.ae) - t.CheckDeepEqual(test.expected, dep.ReportSinceLastUpdated()) + t.CheckDeepEqual(test.expectedMute, dep.ReportSinceLastUpdated(true)) t.CheckTrue(dep.status.changed) + // force report to false and Report again with mute logs false + dep.status.reported = false + t.CheckDeepEqual(test.expected, dep.ReportSinceLastUpdated(false)) }) } } func TestReportSinceLastUpdatedMultipleTimes(t *testing.T) { - pods := map[string]validator.Resource{ - "foo": validator.NewResource( - "test", - "pod", - "foo", - "Pending", - proto.ActionableErr{ - ErrCode: proto.StatusCode_STATUSCHECK_IMAGE_PULL_ERR, - Message: "image cant be pulled"}, - []string{}, - ), - } var tests = []struct { description string - statuses []string + podStatuses []string reportStatusSeq []bool expected string }{ { description: "report first time should return status", - statuses: []string{"cannot pull image\n"}, + podStatuses: []string{"cannot pull image"}, reportStatusSeq: []bool{true}, expected: ` - test-ns:deployment/test: cannot pull image - - test:pod/foo: image cant be pulled + - test:pod/foo: cannot pull image `, }, { description: "report 2nd time should not return when same status", - statuses: []string{"cannot pull image\n", "cannot pull image\n"}, + podStatuses: []string{"cannot pull image", "cannot pull image"}, reportStatusSeq: []bool{true, true}, expected: "", }, { description: "report called after multiple changes but last status was not changed.", - statuses: []string{"cannot pull image\n", "changed but not reported\n", "changed but not reported\n", "changed but not reported\n"}, + podStatuses: []string{"cannot pull image", "changed but not reported", "changed but not reported", "changed but not reported"}, reportStatusSeq: []bool{true, false, false, true}, expected: ` - test-ns:deployment/test: changed but not reported - - test:pod/foo: image cant be pulled + - test:pod/foo: changed but not reported `, }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { dep := NewDeployment("test", "test-ns", 1) - dep.pods = pods var actual string - for i, status := range test.statuses { - // update to same status + for i, status := range test.podStatuses { dep.UpdateStatus(proto.ActionableErr{ ErrCode: proto.StatusCode_STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING, Message: status, }) + dep.pods = map[string]validator.Resource{ + "foo": validator.NewResource( + "test", + "pod", + "foo", + "Pending", + proto.ActionableErr{ + ErrCode: proto.StatusCode_STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING, + Message: status, + }, + []string{}, + ), + } if test.reportStatusSeq[i] { - actual = dep.ReportSinceLastUpdated() + actual = dep.ReportSinceLastUpdated(false) } } t.CheckDeepEqual(test.expected, actual) diff --git a/pkg/skaffold/deploy/resource/logfile.go b/pkg/skaffold/deploy/resource/logfile.go new file mode 100644 index 00000000000..0ec444f9ec0 --- /dev/null +++ b/pkg/skaffold/deploy/resource/logfile.go @@ -0,0 +1,50 @@ +/* +Copyright 2020 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 resource + +import ( + "bytes" + "fmt" + "io" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/logfile" +) + +// withLogFile returns a multiwriter that writes both to a file and a buffer, with the buffer being written to the provided output buffer in case of error +func withLogFile(container string, out io.Writer, l []string, muted bool) (io.Writer, func([]string), error) { + if !muted || len(l) <= maxLogLines { + return out, func([]string) {}, nil + } + file, err := logfile.Create("statuscheck", container+".log") + if err != nil { + return out, func([]string) {}, fmt.Errorf("unable to create log file for statuscheck step: %w", err) + } + + // Print logs to a memory buffer and to a file. + var buf bytes.Buffer + w := io.MultiWriter(file, &buf) + + // After the status check updates finishes, close the log file. + return w, func(lines []string) { + file.Close() + // Write last few lines to out + for _, l := range lines { + out.Write([]byte(l)) + } + fmt.Fprintf(out, "%s %s Full logs at %s\n", tab, tab, file.Name()) + }, err +} diff --git a/pkg/skaffold/deploy/status_check.go b/pkg/skaffold/deploy/status_check.go index 6e600b04453..bf6c069128f 100644 --- a/pkg/skaffold/deploy/status_check.go +++ b/pkg/skaffold/deploy/status_check.go @@ -41,11 +41,11 @@ import ( var ( defaultStatusCheckDeadline = 2 * time.Minute - // Poll period for checking set to 100 milliseconds - defaultPollPeriodInMilliseconds = 100 + // Poll period for checking set to 1 second + defaultPollPeriodInMilliseconds = 1000 - // report resource status for pending resources 0.5 second. - reportStatusTime = 500 * time.Millisecond + // report resource status for pending resources 5 seconds. + reportStatusTime = 5 * time.Second ) const ( @@ -59,53 +59,84 @@ type counter struct { failed int32 } -func StatusCheck(ctx context.Context, defaultLabeller *DefaultLabeller, runCtx *runcontext.RunContext, out io.Writer) error { +// StatusChecker wait for the application to be totally deployed. +type StatusChecker interface { + Check(context.Context, io.Writer) error +} + +// StatusChecker runs status checks for pods and deployments +type statusChecker struct { + // TODO use an interface #4605 + runCtx *runcontext.RunContext + labeller *DefaultLabeller + deadlineSeconds int + muteLogs bool +} + +// NewStatusChecker returns a status checker which runs checks on deployments and pods. +func NewStatusChecker(runCtx *runcontext.RunContext, labeller *DefaultLabeller) StatusChecker { + return statusChecker{ + muteLogs: runCtx.Muted().MuteStatusCheck(), + runCtx: runCtx, + labeller: labeller, + deadlineSeconds: runCtx.Pipeline().Deploy.StatusCheckDeadlineSeconds, + } +} + +// Run runs the status checks on deployments and pods deployed in current skaffold dev iteration. +func (s statusChecker) Check(ctx context.Context, out io.Writer) error { event.StatusCheckEventStarted() - errCode, err := statusCheck(ctx, defaultLabeller, runCtx, out) + errCode, err := s.statusCheck(ctx, out) event.StatusCheckEventEnded(errCode, err) return err } -func statusCheck(ctx context.Context, defaultLabeller *DefaultLabeller, runCtx *runcontext.RunContext, out io.Writer) (proto.StatusCode, error) { +func (s statusChecker) statusCheck(ctx context.Context, out io.Writer) (proto.StatusCode, error) { client, err := kubernetesclient.Client() if err != nil { return proto.StatusCode_STATUSCHECK_KUBECTL_CLIENT_FETCH_ERR, fmt.Errorf("getting Kubernetes client: %w", err) } deployments := make([]*resource.Deployment, 0) - for _, n := range runCtx.GetNamespaces() { - newDeployments, err := getDeployments(client, n, defaultLabeller, - getDeadline(runCtx.Pipeline().Deploy.StatusCheckDeadlineSeconds)) + for _, n := range s.runCtx.GetNamespaces() { + newDeployments, err := getDeployments(client, n, s.labeller, + getDeadline(s.runCtx.Pipeline().Deploy.StatusCheckDeadlineSeconds)) if err != nil { return proto.StatusCode_STATUSCHECK_DEPLOYMENT_FETCH_ERR, fmt.Errorf("could not fetch deployments: %w", err) } deployments = append(deployments, newDeployments...) } - deadline := statusCheckMaxDeadline(runCtx.Pipeline().Deploy.StatusCheckDeadlineSeconds, deployments) - var wg sync.WaitGroup c := newCounter(len(deployments)) + ctx, cancel := context.WithCancel(ctx) + defer cancel() + for _, d := range deployments { wg.Add(1) go func(r *resource.Deployment) { defer wg.Done() // keep updating the resource status until it fails/succeeds/times out - pollDeploymentStatus(ctx, runCtx, r) + pollDeploymentStatus(ctx, s.runCtx, r) rcCopy := c.markProcessed(r.Status().Error()) - printStatusCheckSummary(out, r, rcCopy) + s.printStatusCheckSummary(out, r, rcCopy) + // if one deployment fails, cancel status checks for all deployments. + if r.Status().Error() != nil && r.StatusCode() != proto.StatusCode_STATUSCHECK_USER_CANCELLED { + cancel() + } }(d) } // Retrieve pending deployments statuses go func() { - printDeploymentStatus(ctx, out, deployments, deadline) + s.printDeploymentStatus(ctx, out, deployments) }() // Wait for all deployment statuses to be fetched wg.Wait() + cancel() return getSkaffoldDeployStatus(c, deployments) } @@ -147,13 +178,22 @@ func pollDeploymentStatus(ctx context.Context, runCtx *runcontext.RunContext, r for { select { case <-timeoutContext.Done(): - msg := fmt.Sprintf("could not stabilize within %v: %v", r.Deadline(), timeoutContext.Err()) - r.UpdateStatus(proto.ActionableErr{ErrCode: proto.StatusCode_STATUSCHECK_DEADLINE_EXCEEDED, - Message: msg}) + switch c := timeoutContext.Err(); c { + case context.Canceled: + r.UpdateStatus(proto.ActionableErr{ + ErrCode: proto.StatusCode_STATUSCHECK_USER_CANCELLED, + Message: "check cancelled\n", + }) + case context.DeadlineExceeded: + r.UpdateStatus(proto.ActionableErr{ + ErrCode: proto.StatusCode_STATUSCHECK_DEADLINE_EXCEEDED, + Message: fmt.Sprintf("could not stabilize within %v\n", r.Deadline()), + }) + } return case <-time.After(pollDuration): r.CheckStatus(timeoutContext, runCtx) - if r.IsStatusCheckComplete() { + if r.IsStatusCheckCompleteOrCancelled() { return } // Fail immediately if any pod container errors cannot be recovered. @@ -173,8 +213,8 @@ func getSkaffoldDeployStatus(c *counter, rs []*resource.Deployment) (proto.Statu if c.failed > 0 { err := fmt.Errorf("%d/%d deployment(s) failed", c.failed, c.total) for _, r := range rs { - if r.StatusCode != proto.StatusCode_STATUSCHECK_SUCCESS { - return r.FirstPodErrOccurred(), err + if r.StatusCode() != proto.StatusCode_STATUSCHECK_SUCCESS { + return r.StatusCode(), err } } } @@ -188,22 +228,22 @@ func getDeadline(d int) time.Duration { return defaultStatusCheckDeadline } -func printStatusCheckSummary(out io.Writer, r *resource.Deployment, c counter) { +func (s statusChecker) printStatusCheckSummary(out io.Writer, r *resource.Deployment, c counter) { ae := r.Status().ActionableError() - if ae.ErrCode == proto.StatusCode_STATUSCHECK_CONTEXT_CANCELLED { - // Don't print the status summary if the user ctrl-C + if r.StatusCode() == proto.StatusCode_STATUSCHECK_USER_CANCELLED { + // Don't print the status summary if the user ctrl-C or + // another deployment failed return } - event.ResourceStatusCheckEventCompleted(r.String(), r.Status().ActionableError()) + event.ResourceStatusCheckEventCompleted(r.String(), ae) status := fmt.Sprintf("%s %s", tabHeader, r) if ae.ErrCode != proto.StatusCode_STATUSCHECK_SUCCESS { - if str := r.ReportSinceLastUpdated(); str != "" { + if str := r.ReportSinceLastUpdated(s.muteLogs); str != "" { fmt.Fprintln(out, trimNewLine(str)) } - status = fmt.Sprintf("%s failed.%s Error: %s.", + status = fmt.Sprintf("%s failed. Error: %s.", status, - trimNewLine(getPendingMessage(c.pending, c.total)), - trimNewLine(ae.Message), + trimNewLine(r.StatusMessage()), ) } else { status = fmt.Sprintf("%s is ready.%s", status, getPendingMessage(c.pending, c.total)) @@ -212,17 +252,15 @@ func printStatusCheckSummary(out io.Writer, r *resource.Deployment, c counter) { fmt.Fprintln(out, status) } -// Print resource statuses until all status check are completed or context is cancelled. -func printDeploymentStatus(ctx context.Context, out io.Writer, deployments []*resource.Deployment, deadline time.Duration) { - timeoutContext, cancel := context.WithTimeout(ctx, deadline) - defer cancel() +// printDeploymentStatus prints resource statuses until all status check are completed or context is cancelled. +func (s statusChecker) printDeploymentStatus(ctx context.Context, out io.Writer, deployments []*resource.Deployment) { for { var allDone bool select { - case <-timeoutContext.Done(): + case <-ctx.Done(): return case <-time.After(reportStatusTime): - allDone = printStatus(deployments, out) + allDone = s.printStatus(deployments, out) } if allDone { return @@ -230,14 +268,14 @@ func printDeploymentStatus(ctx context.Context, out io.Writer, deployments []*re } } -func printStatus(deployments []*resource.Deployment, out io.Writer) bool { +func (s statusChecker) printStatus(deployments []*resource.Deployment, out io.Writer) bool { allDone := true for _, r := range deployments { - if r.IsStatusCheckComplete() { + if r.IsStatusCheckCompleteOrCancelled() { continue } allDone = false - if str := r.ReportSinceLastUpdated(); str != "" { + if str := r.ReportSinceLastUpdated(s.muteLogs); str != "" { event.ResourceStatusCheckEventUpdated(r.String(), r.Status().ActionableError()) fmt.Fprintln(out, trimNewLine(str)) } @@ -264,7 +302,7 @@ func newCounter(i int) *counter { } func (c *counter) markProcessed(err error) counter { - if err != nil { + if err != nil && err != context.Canceled { atomic.AddInt32(&c.failed, 1) } atomic.AddInt32(&c.pending, -1) @@ -278,16 +316,3 @@ func (c *counter) copy() counter { failed: c.failed, } } - -func statusCheckMaxDeadline(value int, deployments []*resource.Deployment) time.Duration { - if value > 0 { - return time.Duration(value) * time.Second - } - d := time.Duration(0) - for _, r := range deployments { - if r.Deadline() > d { - d = r.Deadline() - } - } - return d -} diff --git a/pkg/skaffold/deploy/status_check_test.go b/pkg/skaffold/deploy/status_check_test.go index 6a3f798c160..36e49a134d7 100644 --- a/pkg/skaffold/deploy/status_check_test.go +++ b/pkg/skaffold/deploy/status_check_test.go @@ -303,6 +303,7 @@ func TestGetDeployStatus(t *testing.T) { } func TestPrintSummaryStatus(t *testing.T) { + labeller := NewLabeller(true, nil) tests := []struct { description string namespace string @@ -349,27 +350,37 @@ func TestPrintSummaryStatus(t *testing.T) { deployment: "dep", pending: 8, ae: proto.ActionableErr{ErrCode: proto.StatusCode_STATUSCHECK_DEADLINE_EXCEEDED, Message: "context deadline expired"}, - expected: " - test:deployment/dep failed. [8/10 deployment(s) still pending] Error: context deadline expired.\n", + expected: " - test:deployment/dep failed. Error: context deadline expired.\n", + }, + { + description: "skip printing if status check is cancelled", + namespace: "test", + deployment: "dep", + pending: 4, + ae: proto.ActionableErr{ErrCode: proto.StatusCode_STATUSCHECK_USER_CANCELLED}, + expected: "", }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { + checker := statusChecker{labeller: labeller} out := new(bytes.Buffer) rc := newCounter(10) rc.pending = test.pending event.InitializeState(latest.Pipeline{}, "test", true, true, true) r := withStatus(resource.NewDeployment(test.deployment, test.namespace, 0), test.ae) // report status once and set it changed to false. - r.ReportSinceLastUpdated() + r.ReportSinceLastUpdated(false) r.UpdateStatus(test.ae) - printStatusCheckSummary(out, r, *rc) + checker.printStatusCheckSummary(out, r, *rc) t.CheckDeepEqual(test.expected, out.String()) }) } } func TestPrintStatus(t *testing.T) { + labeller := NewLabeller(true, nil) tests := []struct { description string rs []*resource.Deployment @@ -410,7 +421,7 @@ func TestPrintStatus(t *testing.T) { Message: "pending\n"}, ), }, - expectedOut: ` - test:deployment/r2: pending + expectedOut: ` - test:deployment/r2: pod failed - test:pod/foo: pod failed `, }, @@ -422,24 +433,34 @@ func TestPrintStatus(t *testing.T) { proto.ActionableErr{ErrCode: proto.StatusCode_STATUSCHECK_SUCCESS}, ), withStatus( - resource.NewDeployment("r2", "test", 1). - WithPodStatuses([]proto.StatusCode{proto.StatusCode_STATUSCHECK_IMAGE_PULL_ERR}), + resource.NewDeployment("r2", "test", 1), proto.ActionableErr{ ErrCode: proto.StatusCode_STATUSCHECK_KUBECTL_CONNECTION_ERR, Message: resource.MsgKubectlConnection}, ), }, expectedOut: ` - test:deployment/r2: kubectl connection error - - test:pod/foo: pod failed `, }, + { + description: "skip printing if status check is cancelled", + rs: []*resource.Deployment{ + withStatus( + resource.NewDeployment("r1", "test", 1), + proto.ActionableErr{ErrCode: proto.StatusCode_STATUSCHECK_USER_CANCELLED}, + ), + }, + expected: true, + expectedOut: "", + }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { out := new(bytes.Buffer) event.InitializeState(latest.Pipeline{}, "test", true, true, true) - actual := printStatus(test.rs, out) + checker := statusChecker{labeller: labeller} + actual := checker.printStatus(test.rs, out) t.CheckDeepEqual(test.expectedOut, out.String()) t.CheckDeepEqual(test.expected, actual) }) @@ -511,38 +532,6 @@ func TestResourceMarkProcessed(t *testing.T) { } } -func TestGetStatusCheckDeadline(t *testing.T) { - tests := []struct { - description string - value int - deps []*resource.Deployment - expected time.Duration - }{ - { - description: "no value specified", - deps: []*resource.Deployment{ - resource.NewDeployment("dep1", "test", 10*time.Second), - resource.NewDeployment("dep2", "test", 20*time.Second), - }, - expected: 20 * time.Second, - }, - { - description: "value specified less than all other resources", - value: 5, - deps: []*resource.Deployment{ - resource.NewDeployment("dep1", "test", 10*time.Second), - resource.NewDeployment("dep2", "test", 20*time.Second), - }, - expected: 5 * time.Second, - }, - } - for _, test := range tests { - testutil.Run(t, test.description, func(t *testutil.T) { - t.CheckDeepEqual(test.expected, statusCheckMaxDeadline(test.value, test.deps)) - }) - } -} - func TestPollDeployment(t *testing.T) { rolloutCmd := "kubectl --context kubecontext rollout status deployment dep --namespace test --watch=false" tests := []struct { diff --git a/pkg/skaffold/runner/deploy.go b/pkg/skaffold/runner/deploy.go index 569d89675dc..1e38084bae2 100644 --- a/pkg/skaffold/runner/deploy.go +++ b/pkg/skaffold/runner/deploy.go @@ -146,8 +146,8 @@ func (r *SkaffoldRunner) performStatusCheck(ctx context.Context, out io.Writer) start := time.Now() color.Default.Fprintln(out, "Waiting for deployments to stabilize...") - err := statusCheck(ctx, r.labeller, r.runCtx, out) - if err != nil { + s := newStatusCheck(r.runCtx, r.labeller) + if err := s.Check(ctx, out); err != nil { return err } diff --git a/pkg/skaffold/runner/deploy_test.go b/pkg/skaffold/runner/deploy_test.go index e7acd2e8403..7c19b8a49dd 100644 --- a/pkg/skaffold/runner/deploy_test.go +++ b/pkg/skaffold/runner/deploy_test.go @@ -63,14 +63,14 @@ func TestDeploy(t *testing.T) { }, } - dummyStatusCheck := func(context.Context, *deploy.DefaultLabeller, *runcontext.RunContext, io.Writer) error { - return nil - } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { t.SetupFakeKubernetesContext(api.Config{CurrentContext: "cluster1"}) + t.Override(&client.Client, mockK8sClient) - t.Override(&statusCheck, dummyStatusCheck) + t.Override(&newStatusCheck, func(*runcontext.RunContext, *deploy.DefaultLabeller) deploy.StatusChecker { + return dummyStatusChecker{} + }) runner := createRunner(t, test.testBench, nil) runner.runCtx.Opts.StatusCheck = test.statusCheck @@ -114,14 +114,13 @@ func TestDeployNamespace(t *testing.T) { }, } - dummyStatusCheck := func(context.Context, *deploy.DefaultLabeller, *runcontext.RunContext, io.Writer) error { - return nil - } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { t.SetupFakeKubernetesContext(api.Config{CurrentContext: "cluster1"}) t.Override(&client.Client, mockK8sClient) - t.Override(&statusCheck, dummyStatusCheck) + t.Override(&newStatusCheck, func(*runcontext.RunContext, *deploy.DefaultLabeller) deploy.StatusChecker { + return dummyStatusChecker{} + }) runner := createRunner(t, test.testBench, nil) runner.runCtx.Namespaces = test.Namespaces @@ -158,3 +157,9 @@ func TestSkaffoldDeployRenderOnly(t *testing.T) { t.CheckNoError(err) }) } + +type dummyStatusChecker struct{} + +func (d dummyStatusChecker) Check(_ context.Context, _ io.Writer) error { + return nil +} diff --git a/pkg/skaffold/runner/runner.go b/pkg/skaffold/runner/runner.go index 45ecfc55251..a772f4b5391 100644 --- a/pkg/skaffold/runner/runner.go +++ b/pkg/skaffold/runner/runner.go @@ -81,7 +81,7 @@ type SkaffoldRunner struct { // for testing var ( - statusCheck = deploy.StatusCheck + newStatusCheck = deploy.NewStatusChecker ) // HasDeployed returns true if this runner has deployed something. diff --git a/proto/skaffold.pb.go b/proto/skaffold.pb.go index e8e09c5e8da..4abf8040047 100644 --- a/proto/skaffold.pb.go +++ b/proto/skaffold.pb.go @@ -272,7 +272,7 @@ const ( // Failed to configure watcher for Skaffold configuration file. StatusCode_DEVINIT_REGISTER_CONFIG_DEP StatusCode = 704 // User cancelled the skaffold dev run - StatusCode_STATUSCHECK_CONTEXT_CANCELLED StatusCode = 800 + StatusCode_STATUSCHECK_USER_CANCELLED StatusCode = 800 // Deadline for status check exceeded StatusCode_STATUSCHECK_DEADLINE_EXCEEDED StatusCode = 801 ) @@ -319,7 +319,7 @@ var StatusCode_name = map[int32]string{ 702: "DEVINIT_REGISTER_TEST_DEPS", 703: "DEVINIT_REGISTER_DEPLOY_DEPS", 704: "DEVINIT_REGISTER_CONFIG_DEP", - 800: "STATUSCHECK_CONTEXT_CANCELLED", + 800: "STATUSCHECK_USER_CANCELLED", 801: "STATUSCHECK_DEADLINE_EXCEEDED", } @@ -365,7 +365,7 @@ var StatusCode_value = map[string]int32{ "DEVINIT_REGISTER_TEST_DEPS": 702, "DEVINIT_REGISTER_DEPLOY_DEPS": 703, "DEVINIT_REGISTER_CONFIG_DEP": 704, - "STATUSCHECK_CONTEXT_CANCELLED": 800, + "STATUSCHECK_USER_CANCELLED": 800, "STATUSCHECK_DEADLINE_EXCEEDED": 801, } @@ -2440,187 +2440,187 @@ func init() { func init() { proto.RegisterFile("skaffold.proto", fileDescriptor_4f2d38e344f9dbf5) } var fileDescriptor_4f2d38e344f9dbf5 = []byte{ - // 2869 bytes of a gzipped FileDescriptorProto + // 2866 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0x4b, 0x8c, 0xdb, 0xc6, 0x19, 0x5e, 0x89, 0x92, 0x56, 0xfa, 0xf7, 0x61, 0x7a, 0xec, 0xb5, 0x65, 0x79, 0x63, 0xaf, 0x59, 0xdb, 0x71, 0x36, 0xe9, 0x3a, 0x89, 0x8b, 0x22, 0x75, 0x93, 0x16, 0x5c, 0x72, 0xbc, 0xa2, 0x97, 0x4b, 0x0a, 0x14, 0x95, 0xc4, 0x06, 0x0a, 0x81, 0x96, 0xb8, 0x8a, 0x60, 0xad, 0xb8, 0xa5, 0x28, 0xa7, 0xdb, 0x43, 0x0f, 0xbd, 0xf6, 0xd2, 0x36, 0x4d, 0xdf, 0x87, 0xf4, 0xd0, 0x5b, 0x5f, 0xd7, - 0x22, 0x68, 0x53, 0xa0, 0x87, 0x3e, 0xae, 0x05, 0x0a, 0xf4, 0x54, 0x04, 0x48, 0x0e, 0xbd, 0xf4, - 0x94, 0xf4, 0x5d, 0xa0, 0x98, 0x07, 0xc9, 0xa1, 0x1e, 0xde, 0x38, 0x45, 0xd1, 0xd3, 0x6a, 0xfe, - 0xf9, 0xfe, 0xe7, 0xfc, 0xf3, 0xff, 0x3f, 0x67, 0x61, 0x75, 0x74, 0xdf, 0xdb, 0xdf, 0x0f, 0x06, - 0xdd, 0xad, 0xc3, 0x30, 0x88, 0x02, 0x54, 0xa4, 0x7f, 0x6a, 0xeb, 0xbd, 0x20, 0xe8, 0x0d, 0xfc, - 0xeb, 0xde, 0x61, 0xff, 0xba, 0x37, 0x1c, 0x06, 0x91, 0x17, 0xf5, 0x83, 0xe1, 0x88, 0x81, 0x6a, - 0x17, 0xf9, 0x2e, 0x5d, 0xdd, 0x1b, 0xef, 0x5f, 0x8f, 0xfa, 0x07, 0xfe, 0x28, 0xf2, 0x0e, 0x0e, - 0x39, 0xe0, 0xfc, 0x24, 0xc0, 0x3f, 0x38, 0x8c, 0x8e, 0xd8, 0xa6, 0x72, 0x03, 0x56, 0x9a, 0x91, - 0x17, 0xf9, 0x8e, 0x3f, 0x3a, 0x0c, 0x86, 0x23, 0x1f, 0x29, 0x50, 0x1c, 0x11, 0x42, 0x35, 0xb7, - 0x91, 0xbb, 0xb6, 0xf4, 0xec, 0x32, 0xc3, 0x6d, 0x31, 0x10, 0xdb, 0x52, 0xd6, 0xa1, 0x9c, 0xe0, - 0x65, 0x90, 0x0e, 0x46, 0x3d, 0x8a, 0xae, 0x38, 0xe4, 0xa7, 0xf2, 0x18, 0x2c, 0x3a, 0xfe, 0x67, - 0xc7, 0xfe, 0x28, 0x42, 0x08, 0x0a, 0x43, 0xef, 0xc0, 0xe7, 0xbb, 0xf4, 0xb7, 0xf2, 0x7a, 0x01, - 0x8a, 0x54, 0x1a, 0x7a, 0x06, 0xe0, 0xde, 0xb8, 0x3f, 0xe8, 0x36, 0x05, 0x7d, 0x27, 0xb9, 0xbe, - 0xed, 0x64, 0xc3, 0x11, 0x40, 0xe8, 0x63, 0xb0, 0xd4, 0xf5, 0x0f, 0x07, 0xc1, 0x11, 0xe3, 0xc9, - 0x53, 0x1e, 0xc4, 0x79, 0xf4, 0x74, 0xc7, 0x11, 0x61, 0xa8, 0x0e, 0xab, 0xfb, 0x41, 0xf8, 0xaa, - 0x17, 0x76, 0xfd, 0x6e, 0x23, 0x08, 0xa3, 0x51, 0xb5, 0xb0, 0x21, 0x5d, 0x5b, 0x7a, 0x76, 0x43, - 0x74, 0x6e, 0xeb, 0x56, 0x06, 0x82, 0x87, 0x51, 0x78, 0xe4, 0x4c, 0xf0, 0x21, 0x0d, 0x64, 0x12, - 0x82, 0xf1, 0x48, 0x7b, 0xc5, 0xef, 0xdc, 0x67, 0x46, 0x14, 0xa9, 0x11, 0x67, 0x05, 0x59, 0xe2, - 0xb6, 0x33, 0xc5, 0x80, 0x6e, 0xc2, 0xca, 0x7e, 0x7f, 0xe0, 0x37, 0x8f, 0x86, 0x1d, 0x26, 0xa1, - 0x44, 0x25, 0x9c, 0xe6, 0x12, 0x6e, 0x89, 0x7b, 0x4e, 0x16, 0x8a, 0x1a, 0x70, 0xaa, 0xeb, 0xdf, - 0x1b, 0xf7, 0x7a, 0xfd, 0x61, 0x4f, 0x0b, 0x86, 0x91, 0xd7, 0x1f, 0xfa, 0xe1, 0xa8, 0xba, 0x48, - 0xfd, 0xb9, 0x90, 0x04, 0x62, 0x12, 0x81, 0x1f, 0xf8, 0xc3, 0xc8, 0x99, 0xc5, 0x8a, 0x9e, 0x84, - 0xf2, 0x81, 0x1f, 0x79, 0x5d, 0x2f, 0xf2, 0xaa, 0x65, 0x6a, 0xc8, 0x09, 0x2e, 0x66, 0x8f, 0x93, - 0x9d, 0x04, 0x50, 0x6b, 0xc2, 0xa9, 0x19, 0x61, 0x22, 0x49, 0x70, 0xdf, 0x3f, 0xa2, 0x47, 0x58, - 0x74, 0xc8, 0x4f, 0x74, 0x15, 0x8a, 0x0f, 0xbc, 0xc1, 0x38, 0x3e, 0x22, 0x99, 0x8b, 0x24, 0x3c, - 0xcc, 0x16, 0xb6, 0x7d, 0x33, 0xff, 0x5c, 0xee, 0x76, 0xa1, 0x2c, 0xc9, 0x05, 0xe5, 0xdd, 0x1c, - 0x94, 0x63, 0x8d, 0x68, 0x13, 0x8a, 0xf4, 0xd4, 0x79, 0x56, 0x9c, 0x16, 0xb3, 0x22, 0x31, 0x8b, - 0x41, 0xd0, 0x47, 0xa1, 0xc4, 0x0e, 0x9b, 0xeb, 0x5a, 0xcb, 0xa4, 0x43, 0x82, 0xe6, 0x20, 0xf4, - 0x69, 0x00, 0xaf, 0xdb, 0xed, 0x93, 0x2b, 0xe4, 0x0d, 0xaa, 0x1d, 0x1a, 0xb8, 0x8b, 0x13, 0x1e, - 0x6f, 0xa9, 0x09, 0x82, 0xe5, 0x81, 0xc0, 0x52, 0x7b, 0x01, 0x4e, 0x4c, 0x6c, 0x8b, 0xfe, 0x57, - 0x98, 0xff, 0xa7, 0x45, 0xff, 0x2b, 0x82, 0xb7, 0xca, 0xfb, 0x79, 0x58, 0xc9, 0xf8, 0x81, 0x9e, - 0x82, 0x93, 0xc3, 0xf1, 0xc1, 0x3d, 0x3f, 0xb4, 0xf7, 0xd5, 0x30, 0xea, 0xef, 0x7b, 0x9d, 0x68, - 0xc4, 0x63, 0x39, 0xbd, 0x81, 0x5e, 0x80, 0x32, 0xf5, 0x9b, 0x1c, 0x7b, 0x9e, 0x5a, 0x7f, 0x69, - 0x56, 0x74, 0xb6, 0x8c, 0x03, 0xaf, 0xe7, 0x6f, 0x33, 0xa4, 0x93, 0xb0, 0xa0, 0xcb, 0x50, 0x88, - 0x8e, 0x0e, 0xfd, 0xaa, 0xb4, 0x91, 0xbb, 0xb6, 0x9a, 0x9c, 0x0b, 0xc5, 0xb9, 0x47, 0x87, 0xbe, - 0x43, 0x77, 0x91, 0x3e, 0x23, 0x48, 0x97, 0x67, 0xaa, 0x79, 0x58, 0xa4, 0x4c, 0x58, 0x16, 0xad, - 0x40, 0x57, 0xb9, 0xee, 0x1c, 0xd5, 0x8d, 0x44, 0x79, 0x7e, 0x28, 0x68, 0x3f, 0x0d, 0xc5, 0x4e, - 0x30, 0x1e, 0x46, 0x34, 0x78, 0x45, 0x87, 0x2d, 0xfe, 0xdb, 0xb8, 0xff, 0x2a, 0x07, 0xab, 0xd9, - 0x94, 0x40, 0xcf, 0x43, 0x85, 0x25, 0x05, 0x89, 0x65, 0x6e, 0xe2, 0x0a, 0x89, 0x48, 0xbe, 0xf4, - 0x43, 0x27, 0x65, 0x40, 0x4f, 0xc1, 0x62, 0x67, 0x30, 0x1e, 0x45, 0x7e, 0x48, 0x95, 0xa5, 0x0e, - 0x69, 0x8c, 0x4a, 0x1d, 0x8a, 0x21, 0x35, 0x03, 0xca, 0xb1, 0x10, 0xf4, 0x78, 0x26, 0x0e, 0xa7, - 0x32, 0x2a, 0x8f, 0x0f, 0x84, 0xf2, 0xc7, 0x1c, 0x40, 0x5a, 0x1f, 0xd1, 0xa7, 0xa0, 0xe2, 0x09, - 0x69, 0x23, 0x16, 0xb6, 0x14, 0xb5, 0x95, 0x24, 0x10, 0x3b, 0xa6, 0x94, 0x05, 0x6d, 0xc0, 0x92, - 0x37, 0x8e, 0x02, 0x37, 0xec, 0xf7, 0x7a, 0xdc, 0x97, 0xb2, 0x23, 0x92, 0x48, 0xa1, 0xe6, 0x45, - 0x2c, 0xe8, 0xc6, 0x99, 0x73, 0x32, 0x5b, 0xef, 0x82, 0xae, 0xef, 0x08, 0xa0, 0xda, 0xf3, 0xb0, - 0x9a, 0xd5, 0xf8, 0x48, 0x67, 0xf5, 0x79, 0x58, 0x12, 0x8a, 0x39, 0x3a, 0x03, 0x25, 0x26, 0x9a, - 0x73, 0xf3, 0xd5, 0xff, 0xc4, 0x72, 0xe5, 0xed, 0x1c, 0xc8, 0x93, 0x45, 0x7c, 0xae, 0x05, 0x3a, - 0x54, 0x42, 0x7f, 0x14, 0x8c, 0xc3, 0x8e, 0x1f, 0xdf, 0xc6, 0xab, 0x73, 0x1a, 0xc1, 0x96, 0x13, - 0x03, 0xf9, 0x09, 0x24, 0x8c, 0x1f, 0x32, 0xbe, 0x59, 0x79, 0x8f, 0x14, 0x5f, 0x03, 0x56, 0x32, - 0x5d, 0xe6, 0xc3, 0x47, 0x58, 0x79, 0xbb, 0x00, 0x45, 0x5a, 0xd1, 0xd1, 0xd3, 0x50, 0x21, 0x7d, - 0x82, 0x2e, 0x78, 0xdd, 0x96, 0x85, 0xba, 0x4a, 0xe9, 0xf5, 0x05, 0x27, 0x05, 0xa1, 0x1b, 0x7c, - 0x00, 0x60, 0x2c, 0xf9, 0xe9, 0x01, 0x20, 0xe6, 0x11, 0x60, 0xe8, 0xe3, 0xf1, 0x08, 0xc0, 0xb8, - 0xa4, 0x19, 0x23, 0x40, 0xcc, 0x26, 0x02, 0x89, 0x79, 0x87, 0x71, 0xf7, 0xa9, 0x16, 0x66, 0x77, - 0x25, 0x62, 0x5e, 0x02, 0x42, 0x38, 0xd3, 0xec, 0x19, 0xe3, 0xdc, 0x66, 0x1f, 0xf3, 0x4f, 0xb1, - 0xa0, 0xcf, 0x40, 0x35, 0x3e, 0xea, 0x49, 0x3c, 0xef, 0xfc, 0x71, 0xfb, 0x71, 0xe6, 0xc0, 0xea, - 0x0b, 0xce, 0x5c, 0x11, 0xe8, 0xf9, 0x74, 0x9a, 0x60, 0x32, 0x17, 0x67, 0x4e, 0x13, 0xb1, 0xa0, - 0x2c, 0x18, 0xdd, 0x85, 0xb3, 0xdd, 0xd9, 0xd3, 0x02, 0x1f, 0x06, 0x8e, 0x99, 0x29, 0xea, 0x0b, - 0xce, 0x3c, 0x01, 0xe8, 0x13, 0xb0, 0xdc, 0xf5, 0x1f, 0x98, 0x41, 0x70, 0xc8, 0x04, 0x56, 0xa8, - 0xc0, 0xb4, 0xdc, 0xa5, 0x5b, 0xf5, 0x05, 0x27, 0x03, 0xdd, 0x5e, 0x06, 0xf0, 0xc9, 0x8f, 0x36, - 0x29, 0x83, 0xca, 0x00, 0x96, 0x45, 0x34, 0x5a, 0x87, 0x4a, 0x3f, 0xf2, 0x43, 0x3a, 0x06, 0xf3, - 0x46, 0x99, 0x12, 0x84, 0x5c, 0xce, 0x67, 0x72, 0xf9, 0x2a, 0x48, 0x7e, 0x18, 0xf2, 0x84, 0x89, - 0xc3, 0xa3, 0x76, 0x68, 0x3f, 0xb9, 0x37, 0xf0, 0x71, 0x18, 0x3a, 0x04, 0xa0, 0x7c, 0x29, 0x07, - 0x2b, 0x19, 0x32, 0x7a, 0x12, 0x16, 0xfd, 0x30, 0xa4, 0x97, 0x33, 0x37, 0xef, 0x72, 0xc6, 0x08, - 0x54, 0x85, 0xc5, 0x03, 0x7f, 0x34, 0xf2, 0x7a, 0xf1, 0xbd, 0x8b, 0x97, 0xe8, 0x06, 0x2c, 0x8d, - 0xc6, 0xbd, 0x9e, 0x3f, 0xa2, 0xe3, 0x7b, 0x55, 0xa2, 0xe5, 0x22, 0x11, 0x95, 0xec, 0x38, 0x22, - 0x4a, 0xb1, 0xa0, 0x92, 0xdc, 0x1e, 0x72, 0xa3, 0x7d, 0x72, 0xd9, 0xf9, 0x2d, 0x65, 0x8b, 0xcc, - 0x04, 0x97, 0x3f, 0x66, 0x82, 0x53, 0xde, 0x8c, 0x9b, 0x07, 0x93, 0x58, 0x83, 0x72, 0xdc, 0x09, - 0xb8, 0xd0, 0x64, 0x3d, 0x37, 0x90, 0x72, 0x1a, 0xc8, 0x0a, 0x0d, 0x99, 0x18, 0xa0, 0xc2, 0xb1, - 0x01, 0xba, 0x09, 0x2b, 0x9e, 0x18, 0x5e, 0x7e, 0xa7, 0x66, 0x9f, 0x48, 0x16, 0xaa, 0xbc, 0x91, - 0x8b, 0x3b, 0x03, 0x33, 0x7f, 0x5e, 0xdd, 0xe2, 0x26, 0xe6, 0x67, 0x9a, 0x28, 0x3d, 0xba, 0x89, - 0x85, 0x0f, 0x6e, 0xe2, 0x5b, 0xd9, 0xfe, 0xf1, 0x70, 0x3b, 0xe7, 0x27, 0xcb, 0xff, 0x31, 0xc8, - 0x7f, 0xca, 0x41, 0x75, 0x5e, 0x29, 0x22, 0x09, 0x13, 0x97, 0xa2, 0x38, 0x61, 0xe2, 0xf5, 0xdc, - 0x84, 0x11, 0xbc, 0x94, 0x66, 0x7a, 0x59, 0x48, 0xbd, 0xcc, 0xf6, 0xc2, 0xe2, 0x07, 0xe8, 0x85, - 0xd3, 0xbe, 0x96, 0x3e, 0xb8, 0xaf, 0xdf, 0xcf, 0x43, 0x25, 0x29, 0xff, 0xa4, 0xb0, 0x0c, 0x82, - 0x8e, 0x37, 0x20, 0x94, 0xb8, 0xb0, 0x24, 0x04, 0x74, 0x01, 0x20, 0xf4, 0x0f, 0x82, 0xc8, 0xa7, - 0xdb, 0x6c, 0x24, 0x13, 0x28, 0xc4, 0xcd, 0xc3, 0xa0, 0x6b, 0x91, 0x0f, 0x5e, 0xee, 0x26, 0x5f, - 0xa2, 0xcb, 0xb0, 0xd2, 0x89, 0x6b, 0x23, 0xdd, 0x67, 0x0e, 0x67, 0x89, 0x44, 0x3b, 0xf9, 0x42, - 0x1e, 0x1d, 0x7a, 0x1d, 0xe6, 0x79, 0xc5, 0x49, 0x09, 0x24, 0xf0, 0xa4, 0x35, 0x51, 0xf6, 0x12, - 0x0b, 0x7c, 0xbc, 0x46, 0x0a, 0x2c, 0xc7, 0x87, 0x40, 0xa6, 0x47, 0xda, 0x02, 0x2a, 0x4e, 0x86, - 0x26, 0x62, 0xa8, 0x8c, 0x72, 0x16, 0x43, 0xe5, 0x54, 0x61, 0xd1, 0xeb, 0x76, 0x43, 0x7f, 0x34, - 0xa2, 0xc5, 0xba, 0xe2, 0xc4, 0x4b, 0xe5, 0xf7, 0xb9, 0x74, 0x64, 0x48, 0x62, 0x45, 0x5a, 0x89, - 0x46, 0xe7, 0x53, 0x1e, 0xab, 0x84, 0x40, 0x2a, 0x55, 0xff, 0x20, 0x4d, 0x6b, 0xb6, 0x10, 0x12, - 0x44, 0x9a, 0x75, 0x5d, 0x0b, 0x33, 0x93, 0xbd, 0xf8, 0xe8, 0xc9, 0xfe, 0x08, 0x09, 0xf0, 0x5e, - 0x1e, 0xce, 0xce, 0xe9, 0x6d, 0x0f, 0xbb, 0xb5, 0xf1, 0x41, 0xe7, 0x8f, 0x39, 0x68, 0xe9, 0xd8, - 0x83, 0x2e, 0xcc, 0x38, 0xe8, 0xa4, 0x24, 0x17, 0x27, 0x4a, 0x72, 0x15, 0x16, 0xc3, 0xf1, 0x30, - 0xea, 0x27, 0x39, 0x10, 0x2f, 0x49, 0x72, 0xbe, 0x1a, 0x84, 0xf7, 0xfb, 0xc3, 0x9e, 0xde, 0x0f, - 0x79, 0x02, 0x08, 0x14, 0x64, 0x01, 0xd0, 0x3e, 0xcd, 0xde, 0x3f, 0xca, 0xb4, 0xf7, 0x6c, 0x3d, - 0xbc, 0xb7, 0x33, 0xba, 0xf0, 0x1a, 0x22, 0x48, 0x20, 0x5f, 0x63, 0x13, 0xdb, 0xc7, 0x4d, 0xa0, - 0x2b, 0xe2, 0x04, 0xfa, 0x05, 0x28, 0x9b, 0x41, 0x8f, 0xf1, 0x3d, 0x07, 0x95, 0xe4, 0xcd, 0x8a, - 0x0f, 0x8e, 0xb5, 0x2d, 0xf6, 0x68, 0xb5, 0x15, 0x3f, 0x5a, 0x6d, 0xb9, 0x31, 0xc2, 0x49, 0xc1, - 0x48, 0x81, 0xa2, 0x2f, 0xcc, 0x8e, 0xf1, 0x63, 0x15, 0x7f, 0x61, 0xf0, 0xb3, 0x3d, 0x53, 0x12, - 0x7a, 0xa6, 0x72, 0x13, 0x4e, 0xb6, 0x46, 0x7e, 0x68, 0x0c, 0x23, 0x02, 0xe5, 0xcf, 0x55, 0x57, - 0xa0, 0xd4, 0xa7, 0x04, 0x6e, 0xc5, 0x0a, 0x97, 0xc7, 0x51, 0x7c, 0x53, 0xf9, 0x24, 0xac, 0xf2, - 0xe9, 0x37, 0x66, 0x7c, 0x22, 0xfb, 0x68, 0x16, 0x8f, 0x38, 0x1c, 0x95, 0x79, 0x3b, 0x7b, 0x06, - 0x96, 0x45, 0x32, 0xaa, 0xc1, 0xa2, 0x4f, 0x93, 0x91, 0xbd, 0x75, 0x94, 0xeb, 0x0b, 0x4e, 0x4c, - 0xd8, 0x2e, 0x82, 0xf4, 0xc0, 0x1b, 0x28, 0xb7, 0xa1, 0xc4, 0x2c, 0x20, 0xbe, 0xa4, 0xcf, 0x22, - 0xe5, 0xf8, 0x01, 0x04, 0x41, 0x61, 0x74, 0x34, 0xec, 0xf0, 0xe9, 0x9c, 0xfe, 0x26, 0xa9, 0xcb, - 0x1f, 0x45, 0x24, 0x4a, 0xe5, 0x2b, 0xa5, 0x03, 0x90, 0x4e, 0x1a, 0xe8, 0x05, 0x58, 0x4d, 0x67, - 0x0d, 0x61, 0xbe, 0x59, 0x9b, 0x1a, 0x4a, 0xe8, 0x85, 0x9b, 0x00, 0x13, 0x25, 0xec, 0x32, 0xc5, - 0xf5, 0x9e, 0xad, 0x36, 0x03, 0x58, 0x12, 0x3e, 0xea, 0x51, 0x15, 0x4e, 0xb7, 0xac, 0x5d, 0xcb, - 0x7e, 0xc9, 0x6a, 0x6f, 0xb7, 0x0c, 0x53, 0xc7, 0x4e, 0xdb, 0xbd, 0xd3, 0xc0, 0xf2, 0x02, 0x5a, - 0x04, 0xe9, 0xb6, 0xb1, 0x2d, 0xe7, 0x50, 0x05, 0x8a, 0xdb, 0xea, 0x5d, 0x6c, 0xca, 0x79, 0xb4, - 0x0a, 0x40, 0x51, 0x0d, 0x55, 0xdb, 0x6d, 0xca, 0x12, 0x02, 0x28, 0x69, 0xad, 0xa6, 0x6b, 0xef, - 0xc9, 0x05, 0xf2, 0x7b, 0x57, 0xb5, 0x8c, 0x5d, 0x5b, 0x2e, 0x92, 0xdf, 0xba, 0xad, 0xed, 0x62, - 0x47, 0x2e, 0x6d, 0xea, 0x50, 0x49, 0x5e, 0x30, 0xd0, 0x19, 0x40, 0x19, 0x75, 0xb1, 0xb2, 0x25, - 0x58, 0xd4, 0xcc, 0x56, 0xd3, 0xc5, 0x8e, 0x9c, 0x23, 0x9a, 0x77, 0xb4, 0x6d, 0x39, 0x4f, 0x34, - 0x9b, 0xb6, 0xa6, 0x9a, 0xb2, 0xb4, 0x69, 0x93, 0x31, 0x33, 0xfd, 0x06, 0x47, 0xe7, 0x60, 0x2d, - 0x16, 0xa4, 0xe3, 0x86, 0x69, 0xdf, 0x49, 0x0d, 0x2f, 0x43, 0xa1, 0x8e, 0xcd, 0x3d, 0x39, 0x87, - 0x56, 0xa0, 0xb2, 0x4b, 0xcd, 0x33, 0xee, 0x62, 0x39, 0x4f, 0x94, 0xec, 0xb6, 0xb6, 0xb1, 0xe6, - 0x12, 0x81, 0x06, 0x2c, 0x09, 0x6f, 0x01, 0x62, 0x1c, 0xb8, 0x21, 0xb1, 0xb8, 0x65, 0x28, 0xef, - 0x19, 0x96, 0x41, 0x38, 0xb9, 0x6d, 0xbb, 0x98, 0xd9, 0x66, 0xbb, 0x75, 0xec, 0xc8, 0xd2, 0xe6, - 0x9f, 0x01, 0x20, 0x2d, 0x7d, 0xa8, 0x04, 0x79, 0x7b, 0x57, 0x5e, 0x40, 0x55, 0x38, 0xd5, 0x74, - 0x55, 0xb7, 0xd5, 0xd4, 0xea, 0x58, 0xdb, 0x6d, 0x37, 0x5b, 0x9a, 0x86, 0x9b, 0x4d, 0xf9, 0xd7, - 0x39, 0x84, 0x60, 0x85, 0x79, 0x1f, 0xd3, 0x7e, 0x93, 0x43, 0xa7, 0x60, 0x95, 0x39, 0x92, 0x10, - 0x7f, 0x9b, 0x43, 0xeb, 0x50, 0x65, 0xc0, 0x46, 0xab, 0x59, 0x6f, 0xab, 0x94, 0xde, 0xd6, 0xb1, - 0x65, 0x60, 0x5d, 0xf6, 0xd1, 0x79, 0x38, 0xcb, 0x77, 0x1d, 0xfb, 0x36, 0xd6, 0xdc, 0xb6, 0x65, - 0xbb, 0xed, 0x5b, 0x76, 0xcb, 0xd2, 0xe5, 0x7d, 0x74, 0x11, 0x6a, 0xa2, 0x76, 0x63, 0x4f, 0xdd, - 0xc1, 0xed, 0x46, 0xcb, 0x34, 0xdb, 0xd8, 0x71, 0xe4, 0x1f, 0xe4, 0xd1, 0x47, 0xe0, 0x82, 0x08, - 0xd0, 0x6c, 0xcb, 0x55, 0x0d, 0x0b, 0x3b, 0x6d, 0xcd, 0xc1, 0xaa, 0x6b, 0x58, 0x3b, 0xf2, 0x0f, - 0xf3, 0x48, 0x81, 0xc7, 0x44, 0x90, 0xd3, 0xb2, 0x04, 0x20, 0x11, 0xf4, 0xa3, 0x3c, 0xba, 0x02, - 0x1b, 0xb3, 0x05, 0xb9, 0xd8, 0xd9, 0x33, 0x2c, 0xd5, 0xc5, 0xba, 0xfc, 0xe3, 0x3c, 0x7a, 0x12, - 0xae, 0x8a, 0x30, 0xe6, 0xec, 0x1e, 0xb6, 0xdc, 0xb6, 0x63, 0x9b, 0xa6, 0xdd, 0x72, 0xdb, 0x0d, - 0x6c, 0xe9, 0x44, 0xef, 0x4f, 0x1e, 0x22, 0xd3, 0xc1, 0x4d, 0x57, 0x75, 0xa8, 0x79, 0xef, 0xe4, - 0x51, 0x0d, 0xd6, 0x44, 0x58, 0xcb, 0xaa, 0x63, 0xd5, 0x74, 0xeb, 0x77, 0xe4, 0x77, 0xa7, 0x44, - 0x58, 0xb6, 0x8e, 0xdb, 0x7b, 0x78, 0xcf, 0x76, 0xee, 0xb4, 0x1b, 0x0e, 0x6e, 0x36, 0x5b, 0x0e, - 0x96, 0xbf, 0x2c, 0x4d, 0x86, 0x81, 0xc2, 0x74, 0xa3, 0xb9, 0x9b, 0x82, 0xbe, 0x22, 0xa1, 0x27, - 0xe0, 0xf2, 0x14, 0xc8, 0xc2, 0xee, 0x4b, 0xb6, 0x43, 0x94, 0xaa, 0x2f, 0xaa, 0x86, 0xa9, 0x6e, - 0x9b, 0x58, 0xfe, 0xaa, 0x34, 0x19, 0x31, 0x0a, 0x6d, 0x18, 0x7a, 0x2a, 0xee, 0xb5, 0xd9, 0x3a, - 0x5b, 0x16, 0x59, 0xe9, 0x2d, 0x26, 0xe8, 0x6b, 0x12, 0xba, 0x04, 0xeb, 0x33, 0x40, 0x0e, 0x56, - 0xb5, 0x3a, 0x85, 0xbc, 0x2e, 0x4d, 0x9e, 0x31, 0x33, 0xcb, 0x76, 0xdb, 0x0e, 0x56, 0xf5, 0x3b, - 0xf2, 0xd7, 0xa7, 0x8c, 0xb9, 0xa5, 0x1a, 0x26, 0xd6, 0xdb, 0x5c, 0x11, 0x89, 0xe1, 0x37, 0x24, - 0xf4, 0x38, 0x28, 0x22, 0x86, 0xdf, 0x10, 0x12, 0x72, 0x0b, 0x6b, 0xae, 0x61, 0x5b, 0xf4, 0x9c, - 0xbf, 0x35, 0x65, 0x75, 0x0c, 0x24, 0xce, 0xed, 0x1a, 0xa6, 0x89, 0x75, 0xf9, 0xdb, 0x53, 0x91, - 0x4a, 0xa4, 0x99, 0x06, 0x39, 0xe9, 0x5b, 0xd8, 0xd5, 0xea, 0x54, 0xde, 0x77, 0xa4, 0xc9, 0x03, - 0x12, 0x12, 0x22, 0x85, 0x7d, 0x77, 0x2a, 0x0e, 0x0d, 0x5b, 0x6f, 0x1b, 0x96, 0xe1, 0x1a, 0xaa, - 0x69, 0xdc, 0x25, 0x2e, 0xfc, 0x52, 0x22, 0xf7, 0x29, 0xbe, 0xbc, 0xd8, 0x71, 0x6c, 0x47, 0x7e, - 0x4f, 0x9a, 0xbc, 0x7d, 0x7c, 0x5f, 0x7e, 0x5f, 0x42, 0x57, 0xe1, 0xd2, 0x8c, 0x9d, 0x89, 0x03, - 0xf8, 0x8b, 0x84, 0x36, 0xe1, 0xca, 0xec, 0x1c, 0x7c, 0x49, 0x35, 0x48, 0x02, 0x26, 0x32, 0xff, - 0x2a, 0xa1, 0x0b, 0x70, 0x6e, 0x96, 0x4c, 0xfc, 0x22, 0xb6, 0x5c, 0xf9, 0xdf, 0x92, 0x70, 0xbb, - 0x63, 0xa6, 0xbf, 0x49, 0xe8, 0x24, 0x2c, 0x37, 0xef, 0x58, 0x5a, 0x42, 0xfa, 0xbb, 0x94, 0x56, - 0x86, 0x98, 0xf6, 0x0f, 0x09, 0x9d, 0x86, 0x13, 0x3a, 0x7e, 0x91, 0xf8, 0x9c, 0x50, 0xff, 0x49, - 0xa9, 0x9a, 0x89, 0x55, 0xab, 0xd5, 0x48, 0xa8, 0xff, 0xa2, 0x54, 0x2a, 0x92, 0xa2, 0x59, 0x2c, - 0xfe, 0x50, 0x40, 0x1b, 0x70, 0x3e, 0x96, 0xe0, 0xe0, 0x1d, 0x83, 0x56, 0x37, 0xa6, 0x46, 0xc7, - 0x8d, 0xa6, 0xfc, 0xb3, 0x22, 0xc9, 0xa4, 0x29, 0x84, 0x8b, 0x9b, 0x2e, 0x03, 0xfc, 0xbc, 0x48, - 0x4e, 0x61, 0x0a, 0xc0, 0x3d, 0xa2, 0x90, 0xb7, 0x8a, 0x33, 0xb5, 0x68, 0xb6, 0x75, 0xcb, 0xd8, - 0x21, 0x10, 0xf9, 0x17, 0xc5, 0xc9, 0x74, 0x24, 0x11, 0xc5, 0x2f, 0xbb, 0x6d, 0x4d, 0xb5, 0x34, - 0x4c, 0x13, 0xe8, 0x8d, 0xd2, 0x24, 0x46, 0xc7, 0xaa, 0x6e, 0x1a, 0x16, 0x6e, 0xe3, 0x97, 0x35, - 0x8c, 0x75, 0xac, 0xcb, 0xdf, 0x2b, 0x6d, 0xbe, 0x59, 0x80, 0xd5, 0x6c, 0xfb, 0x23, 0x75, 0xd9, - 0x32, 0x4c, 0x79, 0x01, 0x9d, 0x06, 0x59, 0xd5, 0x89, 0x63, 0xb7, 0xd4, 0x96, 0x49, 0x2c, 0x69, - 0xd8, 0x72, 0x97, 0xf4, 0x9d, 0x58, 0x9e, 0x40, 0x27, 0x33, 0xe1, 0xc6, 0x34, 0xbd, 0xbd, 0x63, - 0xda, 0xdb, 0xaa, 0xc9, 0x8d, 0x97, 0xf7, 0xd1, 0x06, 0xac, 0xef, 0x68, 0xa6, 0xdd, 0xd2, 0xdb, - 0xac, 0xab, 0xb5, 0xd5, 0x96, 0x5b, 0xe7, 0xdb, 0xe4, 0x4a, 0xf7, 0x48, 0x3b, 0x9a, 0xbd, 0xf5, - 0x0a, 0xe9, 0x2c, 0x4c, 0x05, 0x17, 0xc1, 0x8b, 0xb5, 0xdc, 0x47, 0xe7, 0xe2, 0x9d, 0x34, 0xb5, - 0x4c, 0x7b, 0xa7, 0x49, 0xea, 0x6e, 0x0d, 0xd6, 0x78, 0xc5, 0xc5, 0xaa, 0x6e, 0x58, 0xa4, 0xec, - 0x37, 0x1c, 0x7b, 0x1b, 0x93, 0x7a, 0x9b, 0xec, 0xa5, 0x6c, 0xb4, 0xba, 0x93, 0x22, 0x7b, 0x09, - 0xd6, 0x55, 0x5d, 0x27, 0xa5, 0x66, 0x6e, 0xc1, 0xbb, 0x08, 0xb5, 0x0c, 0x64, 0xaa, 0xd8, 0x5d, - 0x81, 0x8d, 0x0c, 0x60, 0x4e, 0xa1, 0xbb, 0x00, 0xe7, 0x32, 0xb0, 0xc9, 0x22, 0x37, 0xa9, 0x67, - 0xaa, 0xc0, 0x3d, 0x06, 0xd5, 0x09, 0x40, 0xa6, 0xb8, 0x9d, 0x87, 0x33, 0x59, 0x33, 0xc4, 0xc2, - 0x26, 0x28, 0x9f, 0x59, 0xd4, 0x92, 0x18, 0xd5, 0xed, 0xa6, 0x2b, 0xd4, 0x32, 0xf9, 0x9b, 0xd2, - 0xb3, 0x3f, 0x2d, 0xc2, 0x89, 0x26, 0xff, 0x67, 0x6e, 0xd3, 0x0f, 0x1f, 0xf4, 0x3b, 0x3e, 0xd2, - 0xa0, 0xbc, 0xe3, 0x47, 0xfc, 0xbd, 0x75, 0x6a, 0xbe, 0xc5, 0x07, 0x87, 0xd1, 0x51, 0x2d, 0xf3, - 0xef, 0x56, 0xe5, 0xe4, 0x17, 0x7f, 0xf7, 0xce, 0x6b, 0xf9, 0x25, 0x54, 0xb9, 0xfe, 0xe0, 0x99, - 0xeb, 0x74, 0x7c, 0x44, 0x3b, 0x50, 0xa6, 0xd3, 0xad, 0x19, 0xf4, 0x50, 0xfc, 0xca, 0x13, 0x0f, - 0xd2, 0xb5, 0x49, 0x82, 0xb2, 0x46, 0x05, 0x9c, 0x40, 0x2b, 0x44, 0x00, 0x7b, 0x50, 0x1b, 0x04, - 0xbd, 0x6b, 0xb9, 0xa7, 0x73, 0x68, 0x07, 0x4a, 0x54, 0xd0, 0x68, 0xae, 0x2d, 0x53, 0xd2, 0x10, - 0x95, 0xb6, 0x8c, 0x20, 0x91, 0x36, 0x7a, 0x3a, 0x87, 0x5e, 0x86, 0x45, 0xfc, 0x39, 0xbf, 0x33, - 0x8e, 0x7c, 0x54, 0xe5, 0x1c, 0x53, 0x93, 0x75, 0x6d, 0x8e, 0x0e, 0xe5, 0x3c, 0x15, 0xb9, 0xa6, - 0x2c, 0x51, 0x91, 0x4c, 0xcc, 0x4d, 0x3e, 0x67, 0x23, 0x0f, 0x2a, 0xea, 0x38, 0x0a, 0xe8, 0x64, - 0x87, 0xd6, 0xb2, 0x33, 0xf5, 0x71, 0x82, 0xaf, 0x50, 0xc1, 0x17, 0x6b, 0x67, 0x88, 0x60, 0x3a, - 0x26, 0x5f, 0xf7, 0xc6, 0x51, 0xd0, 0x8e, 0x75, 0xb0, 0x69, 0x1c, 0xb5, 0xa1, 0x4c, 0x54, 0x90, - 0xaf, 0xda, 0x47, 0xd5, 0x70, 0x99, 0x6a, 0xb8, 0x50, 0x5b, 0xa3, 0x87, 0x73, 0x34, 0xec, 0xcc, - 0x54, 0xd0, 0x01, 0x20, 0x0a, 0xd8, 0x5c, 0xf9, 0xa8, 0x2a, 0xae, 0x52, 0x15, 0x1b, 0xb5, 0xb3, - 0x44, 0x05, 0x1b, 0xe0, 0x67, 0x2a, 0x31, 0xa1, 0x54, 0xf7, 0x86, 0xdd, 0x81, 0x8f, 0x32, 0x5f, - 0x40, 0x73, 0xe5, 0xae, 0x53, 0xb9, 0x67, 0x94, 0x93, 0xe9, 0x41, 0x5e, 0x7f, 0x85, 0x0a, 0xb8, - 0x99, 0xdb, 0xbc, 0x57, 0xa2, 0xe8, 0x1b, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0xfa, 0xb4, 0x59, - 0x34, 0x8e, 0x20, 0x00, 0x00, + 0x22, 0x68, 0x53, 0xa0, 0x87, 0x3e, 0xae, 0x05, 0x0a, 0xf4, 0x54, 0x04, 0x48, 0x0e, 0x45, 0xaf, + 0x49, 0xdf, 0x05, 0x8a, 0x79, 0x90, 0x1c, 0xea, 0xe1, 0x8d, 0x53, 0x14, 0x3d, 0xad, 0xe6, 0x9f, + 0xef, 0x7f, 0xce, 0x3f, 0xff, 0xff, 0x73, 0x16, 0x56, 0x47, 0xf7, 0xbd, 0xfd, 0xfd, 0x60, 0xd0, + 0xdd, 0x3a, 0x0c, 0x83, 0x28, 0x40, 0x45, 0xfa, 0xa7, 0xb6, 0xde, 0x0b, 0x82, 0xde, 0xc0, 0xbf, + 0xee, 0x1d, 0xf6, 0xaf, 0x7b, 0xc3, 0x61, 0x10, 0x79, 0x51, 0x3f, 0x18, 0x8e, 0x18, 0xa8, 0x76, + 0x91, 0xef, 0xd2, 0xd5, 0xbd, 0xf1, 0xfe, 0xf5, 0xa8, 0x7f, 0xe0, 0x8f, 0x22, 0xef, 0xe0, 0x90, + 0x03, 0xce, 0x4f, 0x02, 0xfc, 0x83, 0xc3, 0xe8, 0x88, 0x6d, 0x2a, 0x37, 0x60, 0xa5, 0x19, 0x79, + 0x91, 0xef, 0xf8, 0xa3, 0xc3, 0x60, 0x38, 0xf2, 0x91, 0x02, 0xc5, 0x11, 0x21, 0x54, 0x73, 0x1b, + 0xb9, 0x6b, 0x4b, 0xcf, 0x2e, 0x33, 0xdc, 0x16, 0x03, 0xb1, 0x2d, 0x65, 0x1d, 0xca, 0x09, 0x5e, + 0x06, 0xe9, 0x60, 0xd4, 0xa3, 0xe8, 0x8a, 0x43, 0x7e, 0x2a, 0x8f, 0xc1, 0xa2, 0xe3, 0x7f, 0x76, + 0xec, 0x8f, 0x22, 0x84, 0xa0, 0x30, 0xf4, 0x0e, 0x7c, 0xbe, 0x4b, 0x7f, 0x2b, 0xaf, 0x17, 0xa0, + 0x48, 0xa5, 0xa1, 0x67, 0x00, 0xee, 0x8d, 0xfb, 0x83, 0x6e, 0x53, 0xd0, 0x77, 0x92, 0xeb, 0xdb, + 0x4e, 0x36, 0x1c, 0x01, 0x84, 0x3e, 0x06, 0x4b, 0x5d, 0xff, 0x70, 0x10, 0x1c, 0x31, 0x9e, 0x3c, + 0xe5, 0x41, 0x9c, 0x47, 0x4f, 0x77, 0x1c, 0x11, 0x86, 0xea, 0xb0, 0xba, 0x1f, 0x84, 0xaf, 0x7a, + 0x61, 0xd7, 0xef, 0x36, 0x82, 0x30, 0x1a, 0x55, 0x0b, 0x1b, 0xd2, 0xb5, 0xa5, 0x67, 0x37, 0x44, + 0xe7, 0xb6, 0x6e, 0x65, 0x20, 0x78, 0x18, 0x85, 0x47, 0xce, 0x04, 0x1f, 0xd2, 0x40, 0x26, 0x21, + 0x18, 0x8f, 0xb4, 0x57, 0xfc, 0xce, 0x7d, 0x66, 0x44, 0x91, 0x1a, 0x71, 0x56, 0x90, 0x25, 0x6e, + 0x3b, 0x53, 0x0c, 0xe8, 0x26, 0xac, 0xec, 0xf7, 0x07, 0x7e, 0xf3, 0x68, 0xd8, 0x61, 0x12, 0x4a, + 0x54, 0xc2, 0x69, 0x2e, 0xe1, 0x96, 0xb8, 0xe7, 0x64, 0xa1, 0xa8, 0x01, 0xa7, 0xba, 0xfe, 0xbd, + 0x71, 0xaf, 0xd7, 0x1f, 0xf6, 0xb4, 0x60, 0x18, 0x79, 0xfd, 0xa1, 0x1f, 0x8e, 0xaa, 0x8b, 0xd4, + 0x9f, 0x0b, 0x49, 0x20, 0x26, 0x11, 0xf8, 0x81, 0x3f, 0x8c, 0x9c, 0x59, 0xac, 0xe8, 0x49, 0x28, + 0x1f, 0xf8, 0x91, 0xd7, 0xf5, 0x22, 0xaf, 0x5a, 0xa6, 0x86, 0x9c, 0xe0, 0x62, 0xf6, 0x38, 0xd9, + 0x49, 0x00, 0xb5, 0x26, 0x9c, 0x9a, 0x11, 0x26, 0x92, 0x04, 0xf7, 0xfd, 0x23, 0x7a, 0x84, 0x45, + 0x87, 0xfc, 0x44, 0x57, 0xa1, 0xf8, 0xc0, 0x1b, 0x8c, 0xe3, 0x23, 0x92, 0xb9, 0x48, 0xc2, 0xc3, + 0x6c, 0x61, 0xdb, 0x37, 0xf3, 0xcf, 0xe5, 0x6e, 0x17, 0xca, 0x92, 0x5c, 0x50, 0xde, 0xcd, 0x41, + 0x39, 0xd6, 0x88, 0x36, 0xa1, 0x48, 0x4f, 0x9d, 0x67, 0xc5, 0x69, 0x31, 0x2b, 0x12, 0xb3, 0x18, + 0x04, 0x7d, 0x14, 0x4a, 0xec, 0xb0, 0xb9, 0xae, 0xb5, 0x4c, 0x3a, 0x24, 0x68, 0x0e, 0x42, 0x9f, + 0x06, 0xf0, 0xba, 0xdd, 0x3e, 0xb9, 0x42, 0xde, 0xa0, 0xda, 0xa1, 0x81, 0xbb, 0x38, 0xe1, 0xf1, + 0x96, 0x9a, 0x20, 0x58, 0x1e, 0x08, 0x2c, 0xb5, 0x17, 0xe0, 0xc4, 0xc4, 0xb6, 0xe8, 0x7f, 0x85, + 0xf9, 0x7f, 0x5a, 0xf4, 0xbf, 0x22, 0x78, 0xab, 0xbc, 0x9f, 0x87, 0x95, 0x8c, 0x1f, 0xe8, 0x29, + 0x38, 0x39, 0x1c, 0x1f, 0xdc, 0xf3, 0x43, 0x7b, 0x5f, 0x0d, 0xa3, 0xfe, 0xbe, 0xd7, 0x89, 0x46, + 0x3c, 0x96, 0xd3, 0x1b, 0xe8, 0x05, 0x28, 0x53, 0xbf, 0xc9, 0xb1, 0xe7, 0xa9, 0xf5, 0x97, 0x66, + 0x45, 0x67, 0xcb, 0x38, 0xf0, 0x7a, 0xfe, 0x36, 0x43, 0x3a, 0x09, 0x0b, 0xba, 0x0c, 0x85, 0xe8, + 0xe8, 0xd0, 0xaf, 0x4a, 0x1b, 0xb9, 0x6b, 0xab, 0xc9, 0xb9, 0x50, 0x9c, 0x7b, 0x74, 0xe8, 0x3b, + 0x74, 0x17, 0xe9, 0x33, 0x82, 0x74, 0x79, 0xa6, 0x9a, 0x87, 0x45, 0xca, 0x84, 0x65, 0xd1, 0x0a, + 0x74, 0x95, 0xeb, 0xce, 0x51, 0xdd, 0x48, 0x94, 0xe7, 0x87, 0x82, 0xf6, 0xd3, 0x50, 0xec, 0x04, + 0xe3, 0x61, 0x44, 0x83, 0x57, 0x74, 0xd8, 0xe2, 0xbf, 0x8d, 0xfb, 0xaf, 0x72, 0xb0, 0x9a, 0x4d, + 0x09, 0xf4, 0x3c, 0x54, 0x58, 0x52, 0x90, 0x58, 0xe6, 0x26, 0xae, 0x90, 0x88, 0xe4, 0x4b, 0x3f, + 0x74, 0x52, 0x06, 0xf4, 0x14, 0x2c, 0x76, 0x06, 0xe3, 0x51, 0xe4, 0x87, 0x54, 0x59, 0xea, 0x90, + 0xc6, 0xa8, 0xd4, 0xa1, 0x18, 0x52, 0x33, 0xa0, 0x1c, 0x0b, 0x41, 0x8f, 0x67, 0xe2, 0x70, 0x2a, + 0xa3, 0xf2, 0xf8, 0x40, 0x28, 0x7f, 0xcc, 0x01, 0xa4, 0xf5, 0x11, 0x7d, 0x0a, 0x2a, 0x9e, 0x90, + 0x36, 0x62, 0x61, 0x4b, 0x51, 0x5b, 0x49, 0x02, 0xb1, 0x63, 0x4a, 0x59, 0xd0, 0x06, 0x2c, 0x79, + 0xe3, 0x28, 0x70, 0xc3, 0x7e, 0xaf, 0xc7, 0x7d, 0x29, 0x3b, 0x22, 0x89, 0x14, 0x6a, 0x5e, 0xc4, + 0x82, 0x6e, 0x9c, 0x39, 0x27, 0xb3, 0xf5, 0x2e, 0xe8, 0xfa, 0x8e, 0x00, 0xaa, 0x3d, 0x0f, 0xab, + 0x59, 0x8d, 0x8f, 0x74, 0x56, 0x9f, 0x87, 0x25, 0xa1, 0x98, 0xa3, 0x33, 0x50, 0x62, 0xa2, 0x39, + 0x37, 0x5f, 0xfd, 0x4f, 0x2c, 0x57, 0xde, 0xce, 0x81, 0x3c, 0x59, 0xc4, 0xe7, 0x5a, 0xa0, 0x43, + 0x25, 0xf4, 0x47, 0xc1, 0x38, 0xec, 0xf8, 0xf1, 0x6d, 0xbc, 0x3a, 0xa7, 0x11, 0x6c, 0x39, 0x31, + 0x90, 0x9f, 0x40, 0xc2, 0xf8, 0x21, 0xe3, 0x9b, 0x95, 0xf7, 0x48, 0xf1, 0x35, 0x60, 0x25, 0xd3, + 0x65, 0x3e, 0x7c, 0x84, 0x95, 0xb7, 0x0b, 0x50, 0xa4, 0x15, 0x1d, 0x3d, 0x0d, 0x15, 0xd2, 0x27, + 0xe8, 0x82, 0xd7, 0x6d, 0x59, 0xa8, 0xab, 0x94, 0x5e, 0x5f, 0x70, 0x52, 0x10, 0xba, 0xc1, 0x07, + 0x00, 0xc6, 0x92, 0x9f, 0x1e, 0x00, 0x62, 0x1e, 0x01, 0x86, 0x3e, 0x1e, 0x8f, 0x00, 0x8c, 0x4b, + 0x9a, 0x31, 0x02, 0xc4, 0x6c, 0x22, 0x90, 0x98, 0x77, 0x18, 0x77, 0x9f, 0x6a, 0x61, 0x76, 0x57, + 0x22, 0xe6, 0x25, 0x20, 0x84, 0x33, 0xcd, 0x9e, 0x31, 0xce, 0x6d, 0xf6, 0x31, 0xff, 0x14, 0x0b, + 0xfa, 0x0c, 0x54, 0xe3, 0xa3, 0x9e, 0xc4, 0xf3, 0xce, 0x1f, 0xb7, 0x1f, 0x67, 0x0e, 0xac, 0xbe, + 0xe0, 0xcc, 0x15, 0x81, 0x9e, 0x4f, 0xa7, 0x09, 0x26, 0x73, 0x71, 0xe6, 0x34, 0x11, 0x0b, 0xca, + 0x82, 0xd1, 0x5d, 0x38, 0xdb, 0x9d, 0x3d, 0x2d, 0xf0, 0x61, 0xe0, 0x98, 0x99, 0xa2, 0xbe, 0xe0, + 0xcc, 0x13, 0x80, 0x3e, 0x01, 0xcb, 0x5d, 0xff, 0x81, 0x19, 0x04, 0x87, 0x4c, 0x60, 0x85, 0x0a, + 0x4c, 0xcb, 0x5d, 0xba, 0x55, 0x5f, 0x70, 0x32, 0xd0, 0xed, 0x65, 0x00, 0x9f, 0xfc, 0x68, 0x93, + 0x32, 0xa8, 0x0c, 0x60, 0x59, 0x44, 0xa3, 0x75, 0xa8, 0xf4, 0x23, 0x3f, 0xa4, 0x63, 0x30, 0x6f, + 0x94, 0x29, 0x41, 0xc8, 0xe5, 0x7c, 0x26, 0x97, 0xaf, 0x82, 0xe4, 0x87, 0x21, 0x4f, 0x98, 0x38, + 0x3c, 0x6a, 0x87, 0xf6, 0x93, 0x7b, 0x03, 0x1f, 0x87, 0xa1, 0x43, 0x00, 0xca, 0x97, 0x72, 0xb0, + 0x92, 0x21, 0xa3, 0x27, 0x61, 0xd1, 0x0f, 0x43, 0x7a, 0x39, 0x73, 0xf3, 0x2e, 0x67, 0x8c, 0x40, + 0x55, 0x58, 0x3c, 0xf0, 0x47, 0x23, 0xaf, 0x17, 0xdf, 0xbb, 0x78, 0x89, 0x6e, 0xc0, 0xd2, 0x68, + 0xdc, 0xeb, 0xf9, 0x23, 0x3a, 0xbe, 0x57, 0x25, 0x5a, 0x2e, 0x12, 0x51, 0xc9, 0x8e, 0x23, 0xa2, + 0x14, 0x0b, 0x2a, 0xc9, 0xed, 0x21, 0x37, 0xda, 0x27, 0x97, 0x9d, 0xdf, 0x52, 0xb6, 0xc8, 0x4c, + 0x70, 0xf9, 0x63, 0x26, 0x38, 0xe5, 0xcd, 0xb8, 0x79, 0x30, 0x89, 0x35, 0x28, 0xc7, 0x9d, 0x80, + 0x0b, 0x4d, 0xd6, 0x73, 0x03, 0x29, 0xa7, 0x81, 0xac, 0xd0, 0x90, 0x89, 0x01, 0x2a, 0x1c, 0x1b, + 0xa0, 0x9b, 0xb0, 0xe2, 0x89, 0xe1, 0xe5, 0x77, 0x6a, 0xf6, 0x89, 0x64, 0xa1, 0xca, 0x1b, 0xb9, + 0xb8, 0x33, 0x30, 0xf3, 0xe7, 0xd5, 0x2d, 0x6e, 0x62, 0x7e, 0xa6, 0x89, 0xd2, 0xa3, 0x9b, 0x58, + 0xf8, 0xe0, 0x26, 0xbe, 0x95, 0xed, 0x1f, 0x0f, 0xb7, 0x73, 0x7e, 0xb2, 0xfc, 0x1f, 0x83, 0xfc, + 0xa7, 0x1c, 0x54, 0xe7, 0x95, 0x22, 0x92, 0x30, 0x71, 0x29, 0x8a, 0x13, 0x26, 0x5e, 0xcf, 0x4d, + 0x18, 0xc1, 0x4b, 0x69, 0xa6, 0x97, 0x85, 0xd4, 0xcb, 0x6c, 0x2f, 0x2c, 0x7e, 0x80, 0x5e, 0x38, + 0xed, 0x6b, 0xe9, 0x83, 0xfb, 0xfa, 0xfd, 0x3c, 0x54, 0x92, 0xf2, 0x4f, 0x0a, 0xcb, 0x20, 0xe8, + 0x78, 0x03, 0x42, 0x89, 0x0b, 0x4b, 0x42, 0x40, 0x17, 0x00, 0x42, 0xff, 0x20, 0x88, 0x7c, 0xba, + 0xcd, 0x46, 0x32, 0x81, 0x42, 0xdc, 0x3c, 0x0c, 0xba, 0x16, 0xf9, 0xe0, 0xe5, 0x6e, 0xf2, 0x25, + 0xba, 0x0c, 0x2b, 0x9d, 0xb8, 0x36, 0xd2, 0x7d, 0xe6, 0x70, 0x96, 0x48, 0xb4, 0x93, 0x2f, 0xe4, + 0xd1, 0xa1, 0xd7, 0x61, 0x9e, 0x57, 0x9c, 0x94, 0x40, 0x02, 0x4f, 0x5a, 0x13, 0x65, 0x2f, 0xb1, + 0xc0, 0xc7, 0x6b, 0xa4, 0xc0, 0x72, 0x7c, 0x08, 0x64, 0x7a, 0xa4, 0x2d, 0xa0, 0xe2, 0x64, 0x68, + 0x22, 0x86, 0xca, 0x28, 0x67, 0x31, 0x54, 0x4e, 0x15, 0x16, 0xbd, 0x6e, 0x37, 0xf4, 0x47, 0x23, + 0x5a, 0xac, 0x2b, 0x4e, 0xbc, 0x54, 0x7e, 0x9f, 0x4b, 0x47, 0x86, 0x24, 0x56, 0xa4, 0x95, 0x68, + 0x74, 0x3e, 0xe5, 0xb1, 0x4a, 0x08, 0xa4, 0x52, 0xf5, 0x0f, 0xd2, 0xb4, 0x66, 0x0b, 0x21, 0x41, + 0xa4, 0x59, 0xd7, 0xb5, 0x30, 0x33, 0xd9, 0x8b, 0x8f, 0x9e, 0xec, 0x8f, 0x90, 0x00, 0xef, 0xe5, + 0xe1, 0xec, 0x9c, 0xde, 0xf6, 0xb0, 0x5b, 0x1b, 0x1f, 0x74, 0xfe, 0x98, 0x83, 0x96, 0x8e, 0x3d, + 0xe8, 0xc2, 0x8c, 0x83, 0x4e, 0x4a, 0x72, 0x71, 0xa2, 0x24, 0x57, 0x61, 0x31, 0x1c, 0x0f, 0xa3, + 0x7e, 0x92, 0x03, 0xf1, 0x92, 0x24, 0xe7, 0xab, 0x41, 0x78, 0xbf, 0x3f, 0xec, 0xe9, 0xfd, 0x90, + 0x27, 0x80, 0x40, 0x41, 0x16, 0x00, 0xed, 0xd3, 0xec, 0xfd, 0xa3, 0x4c, 0x7b, 0xcf, 0xd6, 0xc3, + 0x7b, 0x3b, 0xa3, 0x0b, 0xaf, 0x21, 0x82, 0x04, 0xf2, 0x35, 0x36, 0xb1, 0x7d, 0xdc, 0x04, 0xba, + 0x22, 0x4e, 0xa0, 0x5f, 0x80, 0xb2, 0x19, 0xf4, 0x18, 0xdf, 0x73, 0x50, 0x49, 0xde, 0xac, 0xf8, + 0xe0, 0x58, 0xdb, 0x62, 0x8f, 0x56, 0x5b, 0xf1, 0xa3, 0xd5, 0x96, 0x1b, 0x23, 0x9c, 0x14, 0x8c, + 0x14, 0x28, 0xfa, 0xc2, 0xec, 0x18, 0x3f, 0x56, 0xf1, 0x17, 0x06, 0x3f, 0xdb, 0x33, 0x25, 0xa1, + 0x67, 0x2a, 0x37, 0xe1, 0x64, 0x6b, 0xe4, 0x87, 0xc6, 0x30, 0x22, 0x50, 0xfe, 0x5c, 0x75, 0x05, + 0x4a, 0x7d, 0x4a, 0xe0, 0x56, 0xac, 0x70, 0x79, 0x1c, 0xc5, 0x37, 0x95, 0x4f, 0xc2, 0x2a, 0x9f, + 0x7e, 0x63, 0xc6, 0x27, 0xb2, 0x8f, 0x66, 0xf1, 0x88, 0xc3, 0x51, 0x99, 0xb7, 0xb3, 0x67, 0x60, + 0x59, 0x24, 0xa3, 0x1a, 0x2c, 0xfa, 0x34, 0x19, 0xd9, 0x5b, 0x47, 0xb9, 0xbe, 0xe0, 0xc4, 0x84, + 0xed, 0x22, 0x48, 0x0f, 0xbc, 0x81, 0x72, 0x1b, 0x4a, 0xcc, 0x02, 0xe2, 0x4b, 0xfa, 0x2c, 0x52, + 0x8e, 0x1f, 0x40, 0x10, 0x14, 0x46, 0x47, 0xc3, 0x0e, 0x9f, 0xce, 0xe9, 0x6f, 0x92, 0xba, 0xfc, + 0x51, 0x44, 0xa2, 0x54, 0xbe, 0x52, 0x3a, 0x00, 0xe9, 0xa4, 0x81, 0x5e, 0x80, 0xd5, 0x74, 0xd6, + 0x10, 0xe6, 0x9b, 0xb5, 0xa9, 0xa1, 0x84, 0x5e, 0xb8, 0x09, 0x30, 0x51, 0xc2, 0x2e, 0x53, 0x5c, + 0xef, 0xd9, 0x6a, 0x33, 0x80, 0x25, 0xe1, 0xa3, 0x1e, 0x55, 0xe1, 0x74, 0xcb, 0xda, 0xb5, 0xec, + 0x97, 0xac, 0xf6, 0x76, 0xcb, 0x30, 0x75, 0xec, 0xb4, 0xdd, 0x3b, 0x0d, 0x2c, 0x2f, 0xa0, 0x45, + 0x90, 0x6e, 0x1b, 0xdb, 0x72, 0x0e, 0x55, 0xa0, 0xb8, 0xad, 0xde, 0xc5, 0xa6, 0x9c, 0x47, 0xab, + 0x00, 0x14, 0xd5, 0x50, 0xb5, 0xdd, 0xa6, 0x2c, 0x21, 0x80, 0x92, 0xd6, 0x6a, 0xba, 0xf6, 0x9e, + 0x5c, 0x20, 0xbf, 0x77, 0x55, 0xcb, 0xd8, 0xb5, 0xe5, 0x22, 0xf9, 0xad, 0xdb, 0xda, 0x2e, 0x76, + 0xe4, 0xd2, 0xa6, 0x0e, 0x95, 0xe4, 0x05, 0x03, 0x9d, 0x01, 0x94, 0x51, 0x17, 0x2b, 0x5b, 0x82, + 0x45, 0xcd, 0x6c, 0x35, 0x5d, 0xec, 0xc8, 0x39, 0xa2, 0x79, 0x47, 0xdb, 0x96, 0xf3, 0x44, 0xb3, + 0x69, 0x6b, 0xaa, 0x29, 0x4b, 0x9b, 0x36, 0x19, 0x33, 0xd3, 0x6f, 0x70, 0x74, 0x0e, 0xd6, 0x62, + 0x41, 0x3a, 0x6e, 0x98, 0xf6, 0x9d, 0xd4, 0xf0, 0x32, 0x14, 0xea, 0xd8, 0xdc, 0x93, 0x73, 0x68, + 0x05, 0x2a, 0xbb, 0xd4, 0x3c, 0xe3, 0x2e, 0x96, 0xf3, 0x44, 0xc9, 0x6e, 0x6b, 0x1b, 0x6b, 0x2e, + 0x11, 0x68, 0xc0, 0x92, 0xf0, 0x16, 0x20, 0xc6, 0x81, 0x1b, 0x12, 0x8b, 0x5b, 0x86, 0xf2, 0x9e, + 0x61, 0x19, 0x84, 0x93, 0xdb, 0xb6, 0x8b, 0x99, 0x6d, 0xb6, 0x5b, 0xc7, 0x8e, 0x2c, 0x6d, 0xfe, + 0x19, 0x00, 0xd2, 0xd2, 0x87, 0x4a, 0x90, 0xb7, 0x77, 0xe5, 0x05, 0x54, 0x85, 0x53, 0x4d, 0x57, + 0x75, 0x5b, 0x4d, 0xad, 0x8e, 0xb5, 0xdd, 0x76, 0xb3, 0xa5, 0x69, 0xb8, 0xd9, 0x94, 0x7f, 0x9d, + 0x43, 0x08, 0x56, 0x98, 0xf7, 0x31, 0xed, 0x37, 0x39, 0x74, 0x0a, 0x56, 0x99, 0x23, 0x09, 0xf1, + 0xb7, 0x39, 0xb4, 0x0e, 0x55, 0x06, 0x6c, 0xb4, 0x9a, 0xf5, 0xb6, 0x4a, 0xe9, 0x6d, 0x1d, 0x5b, + 0x06, 0xd6, 0x65, 0x1f, 0x9d, 0x87, 0xb3, 0x7c, 0xd7, 0xb1, 0x6f, 0x63, 0xcd, 0x6d, 0x5b, 0xb6, + 0xdb, 0xbe, 0x65, 0xb7, 0x2c, 0x5d, 0xde, 0x47, 0x17, 0xa1, 0x26, 0x6a, 0x37, 0xf6, 0xd4, 0x1d, + 0xdc, 0x6e, 0xb4, 0x4c, 0xb3, 0x8d, 0x1d, 0x47, 0xfe, 0x41, 0x1e, 0x7d, 0x04, 0x2e, 0x88, 0x00, + 0xcd, 0xb6, 0x5c, 0xd5, 0xb0, 0xb0, 0xd3, 0xd6, 0x1c, 0xac, 0xba, 0x86, 0xb5, 0x23, 0xff, 0x30, + 0x8f, 0x14, 0x78, 0x4c, 0x04, 0x39, 0x2d, 0x4b, 0x00, 0x12, 0x41, 0x3f, 0xca, 0xa3, 0x2b, 0xb0, + 0x31, 0x5b, 0x90, 0x8b, 0x9d, 0x3d, 0xc3, 0x52, 0x5d, 0xac, 0xcb, 0x3f, 0xce, 0xa3, 0x27, 0xe1, + 0xaa, 0x08, 0x63, 0xce, 0xee, 0x61, 0xcb, 0x6d, 0x3b, 0xb6, 0x69, 0xda, 0x2d, 0xb7, 0xdd, 0xc0, + 0x96, 0x4e, 0xf4, 0xfe, 0xe4, 0x21, 0x32, 0x1d, 0xdc, 0x74, 0x55, 0x87, 0x9a, 0xf7, 0x4e, 0x1e, + 0xd5, 0x60, 0x4d, 0x84, 0xb5, 0xac, 0x3a, 0x56, 0x4d, 0xb7, 0x7e, 0x47, 0x7e, 0x77, 0x4a, 0x84, + 0x65, 0xeb, 0xb8, 0xbd, 0x87, 0xf7, 0x6c, 0xe7, 0x4e, 0xbb, 0xe1, 0xe0, 0x66, 0xb3, 0xe5, 0x60, + 0xf9, 0xcb, 0xd2, 0x64, 0x18, 0x28, 0x4c, 0x37, 0x9a, 0xbb, 0x29, 0xe8, 0x2b, 0x12, 0x7a, 0x02, + 0x2e, 0x4f, 0x81, 0x2c, 0xec, 0xbe, 0x64, 0x3b, 0x44, 0xa9, 0xfa, 0xa2, 0x6a, 0x98, 0xea, 0xb6, + 0x89, 0xe5, 0xaf, 0x4a, 0x93, 0x11, 0xa3, 0xd0, 0x86, 0xa1, 0xa7, 0xe2, 0x5e, 0x9b, 0xad, 0xb3, + 0x65, 0x91, 0x95, 0xde, 0x62, 0x82, 0xbe, 0x26, 0xa1, 0x4b, 0xb0, 0x3e, 0x03, 0xe4, 0x60, 0x55, + 0xab, 0x53, 0xc8, 0xeb, 0xd2, 0xe4, 0x19, 0x33, 0xb3, 0x6c, 0xb7, 0xed, 0x60, 0x55, 0xbf, 0x23, + 0x7f, 0x7d, 0xca, 0x98, 0x5b, 0xaa, 0x61, 0x62, 0xbd, 0xcd, 0x15, 0x91, 0x18, 0x7e, 0x43, 0x42, + 0x8f, 0x83, 0x22, 0x62, 0xf8, 0x0d, 0x21, 0x21, 0xb7, 0xb0, 0xe6, 0x1a, 0xb6, 0x45, 0xcf, 0xf9, + 0x5b, 0x53, 0x56, 0xc7, 0x40, 0xe2, 0xdc, 0xae, 0x61, 0x9a, 0x58, 0x97, 0xbf, 0x3d, 0x15, 0xa9, + 0x44, 0x9a, 0x69, 0x90, 0x93, 0xbe, 0x85, 0x5d, 0xad, 0x4e, 0xe5, 0x7d, 0x47, 0x9a, 0x3c, 0x20, + 0x21, 0x21, 0x52, 0xd8, 0x77, 0xa7, 0xe2, 0xd0, 0xb0, 0xf5, 0xb6, 0x61, 0x19, 0xae, 0xa1, 0x9a, + 0xc6, 0x5d, 0xe2, 0xc2, 0x2f, 0x25, 0x72, 0x9f, 0xe2, 0xcb, 0x8b, 0x1d, 0xc7, 0x76, 0xe4, 0xf7, + 0xa4, 0xc9, 0xdb, 0xc7, 0xf7, 0xe5, 0xf7, 0x25, 0x74, 0x15, 0x2e, 0xcd, 0xd8, 0x99, 0x38, 0x80, + 0xbf, 0x48, 0x68, 0x13, 0xae, 0xcc, 0xce, 0xc1, 0x97, 0x54, 0x83, 0x24, 0x60, 0x22, 0xf3, 0xaf, + 0x12, 0xba, 0x00, 0xe7, 0x66, 0xc9, 0xc4, 0x2f, 0x62, 0xcb, 0x95, 0xff, 0x2d, 0x09, 0xb7, 0x3b, + 0x66, 0xfa, 0x9b, 0x84, 0x4e, 0xc2, 0x72, 0xf3, 0x8e, 0xa5, 0x25, 0xa4, 0xbf, 0x4b, 0x69, 0x65, + 0x88, 0x69, 0xff, 0x90, 0xd0, 0x69, 0x38, 0xa1, 0xe3, 0x17, 0x89, 0xcf, 0x09, 0xf5, 0x9f, 0x94, + 0xaa, 0x99, 0x58, 0xb5, 0x5a, 0x8d, 0x84, 0xfa, 0x2f, 0x4a, 0xa5, 0x22, 0x29, 0x9a, 0xc5, 0xe2, + 0x0f, 0x05, 0xb4, 0x01, 0xe7, 0x63, 0x09, 0x0e, 0xde, 0x31, 0x68, 0x75, 0x63, 0x6a, 0x74, 0xdc, + 0x68, 0xca, 0x3f, 0x2b, 0x92, 0x4c, 0x9a, 0x42, 0xb8, 0xb8, 0xe9, 0x32, 0xc0, 0xcf, 0x8b, 0xe4, + 0x14, 0xa6, 0x00, 0xdc, 0x23, 0x0a, 0x79, 0xab, 0x38, 0x53, 0x8b, 0x66, 0x5b, 0xb7, 0x8c, 0x1d, + 0x02, 0x91, 0x7f, 0x51, 0x9c, 0xcc, 0xd7, 0x56, 0x93, 0x20, 0x54, 0x4b, 0xc3, 0x34, 0x7b, 0xde, + 0x28, 0x4d, 0xe6, 0xab, 0x8e, 0x55, 0xdd, 0x34, 0x2c, 0xdc, 0xc6, 0x2f, 0x6b, 0x18, 0xeb, 0x58, + 0x97, 0xbf, 0x57, 0xda, 0x7c, 0xb3, 0x00, 0xab, 0xd9, 0xde, 0x47, 0x8a, 0xb2, 0x65, 0x98, 0xf2, + 0x02, 0x3a, 0x0d, 0xb2, 0xaa, 0x13, 0xaf, 0x6e, 0xa9, 0x2d, 0x93, 0x98, 0xd1, 0xb0, 0xe5, 0x2e, + 0x69, 0x3a, 0xb1, 0x3c, 0x81, 0x4e, 0x06, 0xc2, 0x8d, 0x69, 0x7a, 0x7b, 0xc7, 0xb4, 0xb7, 0x55, + 0x93, 0x5b, 0x2e, 0xef, 0xa3, 0x0d, 0x58, 0xdf, 0xd1, 0x4c, 0xbb, 0xa5, 0xb7, 0x59, 0x4b, 0x6b, + 0xab, 0x2d, 0xb7, 0xce, 0xb7, 0xc9, 0x7d, 0xee, 0x91, 0x5e, 0x34, 0x7b, 0xeb, 0x15, 0xd2, 0x56, + 0x98, 0x0a, 0x2e, 0x82, 0x57, 0x6a, 0xb9, 0x8f, 0xce, 0xc5, 0x3b, 0x69, 0x5e, 0x99, 0xf6, 0x4e, + 0x93, 0x14, 0xdd, 0x1a, 0xac, 0xf1, 0x72, 0x8b, 0x55, 0xdd, 0xb0, 0x48, 0xcd, 0x6f, 0x38, 0xf6, + 0x36, 0x26, 0xc5, 0x36, 0xd9, 0x4b, 0xd9, 0x68, 0x69, 0x27, 0x15, 0xf6, 0x12, 0xac, 0xab, 0xba, + 0x4e, 0xea, 0xcc, 0xdc, 0x6a, 0x77, 0x11, 0x6a, 0x19, 0xc8, 0x54, 0xa5, 0xbb, 0x02, 0x1b, 0x19, + 0xc0, 0x9c, 0x2a, 0x77, 0x01, 0xce, 0x65, 0x60, 0x93, 0x15, 0x6e, 0x52, 0xcf, 0x54, 0x75, 0x7b, + 0x0c, 0xaa, 0x13, 0x80, 0x4c, 0x65, 0x3b, 0x0f, 0x67, 0xb2, 0x66, 0x88, 0x55, 0x4d, 0x50, 0x3e, + 0xb3, 0xa2, 0x25, 0x31, 0xaa, 0xdb, 0x4d, 0x57, 0x28, 0x64, 0xf2, 0x37, 0xa5, 0x67, 0x7f, 0x5a, + 0x84, 0x13, 0x4d, 0xfe, 0x9f, 0xdc, 0xa6, 0x1f, 0x3e, 0xe8, 0x77, 0x7c, 0xa4, 0x41, 0x79, 0xc7, + 0x8f, 0xf8, 0x63, 0xeb, 0xd4, 0x70, 0x8b, 0x0f, 0x0e, 0xa3, 0xa3, 0x5a, 0xe6, 0x7f, 0xad, 0xca, + 0xc9, 0x2f, 0xfe, 0xee, 0x9d, 0xd7, 0xf2, 0x4b, 0xa8, 0x72, 0xfd, 0xc1, 0x33, 0xd7, 0xe9, 0xec, + 0x88, 0x76, 0xa0, 0x4c, 0x47, 0x5b, 0x33, 0xe8, 0xa1, 0xf8, 0x89, 0x27, 0x9e, 0xa2, 0x6b, 0x93, + 0x04, 0x65, 0x8d, 0x0a, 0x38, 0x81, 0x56, 0x88, 0x00, 0xf6, 0x9a, 0x36, 0x08, 0x7a, 0xd7, 0x72, + 0x4f, 0xe7, 0xd0, 0x0e, 0x94, 0xa8, 0xa0, 0xd1, 0x5c, 0x5b, 0xa6, 0xa4, 0x21, 0x2a, 0x6d, 0x19, + 0x41, 0x22, 0x6d, 0xf4, 0x74, 0x0e, 0xbd, 0x0c, 0x8b, 0xf8, 0x73, 0x7e, 0x67, 0x1c, 0xf9, 0xa8, + 0xca, 0x39, 0xa6, 0xc6, 0xea, 0xda, 0x1c, 0x1d, 0xca, 0x79, 0x2a, 0x72, 0x4d, 0x59, 0xa2, 0x22, + 0x99, 0x98, 0x9b, 0x7c, 0xc8, 0x46, 0x1e, 0x54, 0xd4, 0x71, 0x14, 0xd0, 0xb1, 0x0e, 0xad, 0x65, + 0x07, 0xea, 0xe3, 0x04, 0x5f, 0xa1, 0x82, 0x2f, 0xd6, 0xce, 0x10, 0xc1, 0x74, 0x46, 0xbe, 0xee, + 0x8d, 0xa3, 0xa0, 0x1d, 0xeb, 0x60, 0xa3, 0x38, 0x6a, 0x43, 0x99, 0xa8, 0x20, 0x9f, 0xb4, 0x8f, + 0xaa, 0xe1, 0x32, 0xd5, 0x70, 0xa1, 0xb6, 0x46, 0x0f, 0xe7, 0x68, 0xd8, 0x99, 0xa9, 0xa0, 0x03, + 0x40, 0x14, 0xb0, 0xa1, 0xf2, 0x51, 0x55, 0x5c, 0xa5, 0x2a, 0x36, 0x6a, 0x67, 0x89, 0x0a, 0x36, + 0xbd, 0xcf, 0x54, 0x62, 0x42, 0xa9, 0xee, 0x0d, 0xbb, 0x03, 0x1f, 0x65, 0x3e, 0x7f, 0xe6, 0xca, + 0x5d, 0xa7, 0x72, 0xcf, 0x28, 0x27, 0xd3, 0x83, 0xbc, 0xfe, 0x0a, 0x15, 0x70, 0x33, 0xb7, 0x79, + 0xaf, 0x44, 0xd1, 0x37, 0xfe, 0x13, 0x00, 0x00, 0xff, 0xff, 0xe2, 0x15, 0xc3, 0x84, 0x8b, 0x20, + 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/proto/skaffold.proto b/proto/skaffold.proto index e79277309c1..965fea60537 100644 --- a/proto/skaffold.proto +++ b/proto/skaffold.proto @@ -492,7 +492,7 @@ enum StatusCode { // Timeout or User Cancellation Errors // User cancelled the skaffold dev run - STATUSCHECK_CONTEXT_CANCELLED = 800; + STATUSCHECK_USER_CANCELLED = 800; // Deadline for status check exceeded STATUSCHECK_DEADLINE_EXCEEDED = 801; From 8d8379d9be853c3d719cea5efa68ac9da9458daf Mon Sep 17 00:00:00 2001 From: Felix Tran <43281010+felixtran39@users.noreply.github.com> Date: Fri, 28 Aug 2020 23:19:26 +0000 Subject: [PATCH 118/138] Extending Workflow for Kpt Deployer (accepting additional arguments) (#4736) * extending workflow options for kpt deployer * generate schemas and optimize config layout * fix field name of globalScope in config * fix nits, additional tests to catch corner cases Co-authored-by: Felix --- docs/content/en/schemas/v2beta7.json | 95 ++++++++++++++--- pkg/skaffold/deploy/kpt.go | 101 +++++++++++++++--- pkg/skaffold/deploy/kpt_test.go | 149 ++++++++++++++++++++++++++- pkg/skaffold/schema/latest/config.go | 50 +++++++-- 4 files changed, 357 insertions(+), 38 deletions(-) diff --git a/docs/content/en/schemas/v2beta7.json b/docs/content/en/schemas/v2beta7.json index b671b54a39b..30c4d0bc965 100755 --- a/docs/content/en/schemas/v2beta7.json +++ b/docs/content/en/schemas/v2beta7.json @@ -1623,8 +1623,8 @@ } }, "preferredOrder": [ - "dir", "applyDir", + "dir", "fn", "live" ], @@ -1639,15 +1639,45 @@ "description": "a directory to read functions from instead of the configuration directory.", "x-intellij-html-description": "a directory to read functions from instead of the configuration directory." }, + "globalScope": { + "type": "boolean", + "description": "sets global scope for functions.", + "x-intellij-html-description": "sets global scope for functions.", + "default": "false" + }, "image": { "type": "string", "description": "an image to be run as a function in lieu of running functions from a directory.", "x-intellij-html-description": "an image to be run as a function in lieu of running functions from a directory." + }, + "mount": { + "items": { + "type": "string" + }, + "type": "array", + "description": "a list of storage options to mount to the fn image.", + "x-intellij-html-description": "a list of storage options to mount to the fn image.", + "default": "[]" + }, + "network": { + "type": "boolean", + "description": "enables network access for functions that declare it.", + "x-intellij-html-description": "enables network access for functions that declare it.", + "default": "false" + }, + "networkName": { + "type": "string", + "description": "docker network to run the container in (default \"bridge\").", + "x-intellij-html-description": "docker network to run the container in (default "bridge")." } }, "preferredOrder": [ "fnPath", - "image" + "image", + "networkName", + "globalScope", + "network", + "mount" ], "additionalProperties": false, "description": "adds additional configurations used when calling `kpt fn`.", @@ -1656,21 +1686,62 @@ "KptLive": { "properties": { "apply": { - "items": { - "type": "string" - }, - "type": "array", - "description": "additional flags passed on creations (`kpt live apply`).", - "x-intellij-html-description": "additional flags passed on creations (kpt live apply).", - "default": "[]" + "$ref": "#/definitions/KptLiveApply", + "description": "adds additional configurations for `kpt live apply` commands.", + "x-intellij-html-description": "adds additional configurations for kpt live apply commands." + }, + "inventoryID": { + "type": "string", + "description": "identifier for a group of applied resources. This configuration is used when users do not specify `KptDeploy.ApplyDir` and `.kpt-hydrated/inventory-template.yaml` does not exist.", + "x-intellij-html-description": "identifier for a group of applied resources. This configuration is used when users do not specify KptDeploy.ApplyDir and .kpt-hydrated/inventory-template.yaml does not exist." + }, + "inventoryNamespace": { + "type": "string", + "description": "sets the namespace scope for `kpt live init`.", + "x-intellij-html-description": "sets the namespace scope for kpt live init." + } + }, + "preferredOrder": [ + "apply", + "inventoryID", + "inventoryNamespace" + ], + "additionalProperties": false, + "description": "adds additional configurations used when calling `kpt live`.", + "x-intellij-html-description": "adds additional configurations used when calling kpt live." + }, + "KptLiveApply": { + "properties": { + "pollPeriod": { + "type": "string", + "description": "sets for the polling period for resource statuses. Default to 2s.", + "x-intellij-html-description": "sets for the polling period for resource statuses. Default to 2s." + }, + "prunePropagationPolicy": { + "type": "string", + "description": "sets the propagation policy for pruning. Possible settings are Background, Foreground, Orphan. Default to \"Background\".", + "x-intellij-html-description": "sets the propagation policy for pruning. Possible settings are Background, Foreground, Orphan. Default to "Background"." + }, + "pruneTimeout": { + "type": "string", + "description": "sets the time threshold to wait for all pruned resources to be deleted.", + "x-intellij-html-description": "sets the time threshold to wait for all pruned resources to be deleted." + }, + "reconcileTimeout": { + "type": "string", + "description": "sets the time threshold to wait for all resources to reach the current status.", + "x-intellij-html-description": "sets the time threshold to wait for all resources to reach the current status." } }, "preferredOrder": [ - "apply" + "pollPeriod", + "prunePropagationPolicy", + "pruneTimeout", + "reconcileTimeout" ], "additionalProperties": false, - "description": "adds additional configurations used when calling `kpt live` on every command (Global), creations (Apply), or deletions (Destroy).", - "x-intellij-html-description": "adds additional configurations used when calling kpt live on every command (Global), creations (Apply), or deletions (Destroy)." + "description": "adds additional configurations used when calling `kpt live apply`.", + "x-intellij-html-description": "adds additional configurations used when calling kpt live apply." }, "KubectlDeploy": { "properties": { diff --git a/pkg/skaffold/deploy/kpt.go b/pkg/skaffold/deploy/kpt.go index d7d82d96934..b77f9390928 100644 --- a/pkg/skaffold/deploy/kpt.go +++ b/pkg/skaffold/deploy/kpt.go @@ -87,7 +87,7 @@ func (k *KptDeployer) Deploy(ctx context.Context, out io.Writer, builds []build. outputRenderedManifests(manifests.String(), filepath.Join(applyDir, "resources.yaml"), out) - cmd := exec.CommandContext(ctx, "kpt", kptCommandArgs(applyDir, []string{"live", "apply"}, k.Live.Apply, nil)...) + cmd := exec.CommandContext(ctx, "kpt", kptCommandArgs(applyDir, []string{"live", "apply"}, k.getKptLiveApplyArgs(), nil)...) cmd.Stdout = out cmd.Stderr = out if err := util.RunCmd(cmd); err != nil { @@ -253,21 +253,9 @@ func (k *KptDeployer) kustomizeBuild(ctx context.Context) error { func (k *KptDeployer) kptFnRun(ctx context.Context) (deploy.ManifestList, error) { var manifests deploy.ManifestList - // --dry-run sets the pipeline's output to STDOUT, otherwise output is set to sinkDir. - // For now, k.Dir will be treated as sinkDir (and sourceDir). - flags := []string{"--dry-run"} - count := 0 - - if len(k.Fn.FnPath) > 0 { - flags = append(flags, "--fn-path", k.Fn.FnPath) - count++ - } - if len(k.Fn.Image) > 0 { - flags = append(flags, "--image", k.Fn.Image) - count++ - } - if count > 1 { - return nil, errors.New("only one of `fn-path` or `image` configs can be specified at most") + flags, err := k.getKptFnRunArgs() + if err != nil { + return nil, fmt.Errorf("getting kpt fn run args: %w", err) } cmd := exec.CommandContext(ctx, "kpt", kptCommandArgs(pipeline, []string{"fn", "run"}, flags, nil)...) @@ -300,7 +288,7 @@ func (k *KptDeployer) getApplyDir(ctx context.Context) (string, error) { } if _, err := os.Stat(filepath.Join(kptHydrated, inventoryTemplate)); os.IsNotExist(err) { - cmd := exec.CommandContext(ctx, "kpt", kptCommandArgs(kptHydrated, []string{"live", "init"}, nil, nil)...) + cmd := exec.CommandContext(ctx, "kpt", kptCommandArgs(kptHydrated, []string{"live", "init"}, k.getKptLiveInitArgs(), nil)...) if _, err := util.RunCmdOut(cmd); err != nil { return "", err } @@ -360,3 +348,82 @@ func getResources(root string) ([]string, error) { return files, err } + +// getKptFnRunArgs returns a list of arguments that the user specified for the `kpt fn run` command. +func (k *KptDeployer) getKptFnRunArgs() ([]string, error) { + // --dry-run sets the pipeline's output to STDOUT, otherwise output is set to sinkDir. + // For now, k.Dir will be treated as sinkDir (and sourceDir). + flags := []string{"--dry-run"} + + if k.Fn.GlobalScope { + flags = append(flags, "--global-scope") + } + + if len(k.Fn.Mount) > 0 { + flags = append(flags, "--mount", strings.Join(k.Fn.Mount, ",")) + } + + if k.Fn.Network { + flags = append(flags, "--network") + } + + if len(k.Fn.NetworkName) > 0 { + flags = append(flags, "--network-name", k.Fn.NetworkName) + } + + count := 0 + + if len(k.Fn.FnPath) > 0 { + flags = append(flags, "--fn-path", k.Fn.FnPath) + count++ + } + + if len(k.Fn.Image) > 0 { + flags = append(flags, "--image", k.Fn.Image) + count++ + } + + if count > 1 { + return nil, errors.New("only one of `fn-path` or `image` may be specified") + } + + return flags, nil +} + +// getKptLiveApplyArgs returns a list of arguments that the user specified for the `kpt live apply` command. +func (k *KptDeployer) getKptLiveApplyArgs() []string { + var flags []string + + if len(k.Live.Apply.PollPeriod) > 0 { + flags = append(flags, "--poll-period", k.Live.Apply.PollPeriod) + } + + if len(k.Live.Apply.PrunePropagationPolicy) > 0 { + flags = append(flags, "--prune-propagation-policy", k.Live.Apply.PrunePropagationPolicy) + } + + if len(k.Live.Apply.PruneTimeout) > 0 { + flags = append(flags, "--prune-timeout", k.Live.Apply.PruneTimeout) + } + + if len(k.Live.Apply.ReconcileTimeout) > 0 { + flags = append(flags, "--reconcile-timeout", k.Live.Apply.ReconcileTimeout) + } + + return flags +} + +// getKptLiveInitArgs returns a list of arguments that the user specified for the `kpt live init` command. +func (k *KptDeployer) getKptLiveInitArgs() []string { + var flags []string + + if len(k.Live.InventoryID) > 0 { + flags = append(flags, "--inventory-id", k.Live.InventoryID) + } + + if len(k.Live.InventoryNamespace) > 0 { + flags = append(flags, "--namespace", k.Live.InventoryNamespace) + } + + return flags +} diff --git a/pkg/skaffold/deploy/kpt_test.go b/pkg/skaffold/deploy/kpt_test.go index 489bbedc567..c35bae8b5b0 100644 --- a/pkg/skaffold/deploy/kpt_test.go +++ b/pkg/skaffold/deploy/kpt_test.go @@ -117,6 +117,78 @@ spec: AndRunErr("kpt live apply .kpt-hydrated", errors.New("BUG")), shouldErr: true, }, + { + description: "user specifies reconcile timeout and poll period", + cfg: &latest.KptDeploy{ + Dir: ".", + ApplyDir: "valid_path", + Live: latest.KptLive{ + Apply: latest.KptLiveApply{ + PollPeriod: "5s", + ReconcileTimeout: "2m", + }, + }, + }, + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOut("kpt fn run .pipeline --dry-run", output). + AndRun("kpt live apply valid_path --poll-period 5s --reconcile-timeout 2m"), + }, + { + description: "user specifies invalid reconcile timeout and poll period", + cfg: &latest.KptDeploy{ + Dir: ".", + ApplyDir: "valid_path", + Live: latest.KptLive{ + Apply: latest.KptLiveApply{ + PollPeriod: "foo", + ReconcileTimeout: "bar", + }, + }, + }, + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOut("kpt fn run .pipeline --dry-run", output). + AndRun("kpt live apply valid_path --poll-period foo --reconcile-timeout bar"), + }, + { + description: "user specifies prune propagation policy and prune timeout", + cfg: &latest.KptDeploy{ + Dir: ".", + ApplyDir: "valid_path", + Live: latest.KptLive{ + Apply: latest.KptLiveApply{ + PrunePropagationPolicy: "Orphan", + PruneTimeout: "2m", + }, + }, + }, + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOut("kpt fn run .pipeline --dry-run", output). + AndRun("kpt live apply valid_path --prune-propagation-policy Orphan --prune-timeout 2m"), + }, + { + description: "user specifies invalid prune propagation policy and prune timeout", + cfg: &latest.KptDeploy{ + Dir: ".", + ApplyDir: "valid_path", + Live: latest.KptLive{ + Apply: latest.KptLiveApply{ + PrunePropagationPolicy: "foo", + PruneTimeout: "bar", + }, + }, + }, + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOut("kpt fn run .pipeline --dry-run", output). + AndRun("kpt live apply valid_path --prune-propagation-policy foo --prune-timeout bar"), + }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { @@ -277,6 +349,7 @@ func TestKpt_Cleanup(t *testing.T) { tests := []struct { description string applyDir string + globalFlags []string commands util.Command shouldErr bool }{ @@ -604,6 +677,67 @@ spec: AndRunOutErr("kpt fn run .pipeline --dry-run", "invalid pipeline", errors.New("BUG")), shouldErr: true, }, + { + description: "kpt fn run with --global-scope", + cfg: &latest.KptDeploy{ + Dir: ".", + Fn: latest.KptFn{ + Image: "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", + GlobalScope: true, + }, + }, + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOut("kpt fn run .pipeline --dry-run --global-scope --image gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", ``), + expected: "\n", + }, + { + description: "kpt fn run with --mount arguments", + cfg: &latest.KptDeploy{ + Dir: ".", + Fn: latest.KptFn{ + Image: "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", + Mount: []string{"type=bind", "src=$(pwd)", "dst=/source"}, + }, + }, + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOut("kpt fn run .pipeline --dry-run --mount type=bind,src=$(pwd),dst=/source --image gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", ``), + expected: "\n", + }, + { + description: "kpt fn run with invalid --mount arguments", + cfg: &latest.KptDeploy{ + Dir: ".", + Fn: latest.KptFn{ + Image: "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", + Mount: []string{"foo", "", "bar"}, + }, + }, + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOut("kpt fn run .pipeline --dry-run --mount foo,,bar --image gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", ``), + expected: "\n", + }, + { + description: "kpt fn run flag with --network and --network-name arguments", + cfg: &latest.KptDeploy{ + Dir: ".", + Fn: latest.KptFn{ + Image: "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", + Network: true, + NetworkName: "foo", + }, + }, + commands: testutil. + CmdRunOut("kpt fn source .", ``). + AndRunOut("kpt fn sink .pipeline", ``). + AndRunOut("kpt fn run .pipeline --dry-run --network --network-name foo --image gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", ``), + expected: "\n", + }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { @@ -639,6 +773,7 @@ func TestKpt_GetApplyDir(t *testing.T) { tests := []struct { description string applyDir string + live latest.KptLive expected string commands util.Command shouldErr bool @@ -658,6 +793,15 @@ func TestKpt_GetApplyDir(t *testing.T) { expected: ".kpt-hydrated", commands: testutil.CmdRunOut("kpt live init .kpt-hydrated", ""), }, + { + description: "unspecified applyDir with specified inventory-id and namespace", + live: latest.KptLive{ + InventoryID: "1a23bcde-4f56-7891-a2bc-de34fabcde5f6", + InventoryNamespace: "foo", + }, + expected: ".kpt-hydrated", + commands: testutil.CmdRunOut("kpt live init .kpt-hydrated --inventory-id 1a23bcde-4f56-7891-a2bc-de34fabcde5f6 --namespace foo", ""), + }, { description: "existing template resource in .kpt-hydrated", expected: ".kpt-hydrated", @@ -685,6 +829,7 @@ func TestKpt_GetApplyDir(t *testing.T) { DeployType: latest.DeployType{ KptDeploy: &latest.KptDeploy{ ApplyDir: test.applyDir, + Live: test.live, }, }, }, @@ -722,8 +867,8 @@ func TestKpt_KptCommandArgs(t *testing.T) { description: "empty dir", commands: []string{"live", "apply"}, flags: []string{"--fn-path", "kpt-func.yaml"}, - globalFlags: []string{"-h"}, - expected: strings.Split("live apply --fn-path kpt-func.yaml -h", " "), + globalFlags: []string{"-v", "3"}, + expected: strings.Split("live apply --fn-path kpt-func.yaml -v 3", " "), }, { description: "empty commands", diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 4c76c182c9b..8311fdd72c8 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -528,12 +528,12 @@ type KustomizeDeploy struct { // KptDeploy *alpha* uses the `kpt` CLI to manage and deploy manifests. type KptDeploy struct { - // Dir is the path to the directory to run kpt functions against. - Dir string `yaml:"dir,omitempty"` - // ApplyDir is the path to the directory to deploy to the cluster. ApplyDir string `yaml:"applyDir,omitempty"` + // Dir is the path to the directory to run kpt functions against. + Dir string `yaml:"dir,omitempty"` + // Fn adds additional configurations for `kpt fn`. Fn KptFn `yaml:"fn,omitempty"` @@ -548,13 +548,49 @@ type KptFn struct { // Image is an image to be run as a function in lieu of running functions from a directory. Image string `yaml:"image,omitempty"` + + // NetworkName is the docker network to run the container in (default "bridge"). + NetworkName string `yaml:"networkName,omitempty"` + + // GlobalScope sets global scope for functions. + GlobalScope bool `yaml:"globalScope,omitempty"` + + // Network enables network access for functions that declare it. + Network bool `yaml:"network,omitempty"` + + // Mount is a list of storage options to mount to the fn image. + Mount []string `yaml:"mount,omitempty"` } -// KptLive adds additional configurations used when calling `kpt live` -// on every command (Global), creations (Apply), or deletions (Destroy). +// KptLive adds additional configurations used when calling `kpt live`. type KptLive struct { - // Apply are additional flags passed on creations (`kpt live apply`). - Apply []string `yaml:"apply,omitempty"` + // Apply adds additional configurations for `kpt live apply` commands. + Apply KptLiveApply `yaml:"apply,omitempty"` + + // InventoryID is the identifier for a group of applied resources. + // This configuration is used when users do not specify `KptDeploy.ApplyDir` + // and `.kpt-hydrated/inventory-template.yaml` does not exist. + InventoryID string `yaml:"inventoryID,omitempty"` + + // InventoryNamespace sets the namespace scope for `kpt live init`. + InventoryNamespace string `yaml:"inventoryNamespace,omitempty"` +} + +// KptLiveApply adds additional configurations used when calling `kpt live apply`. +type KptLiveApply struct { + // PollPeriod sets for the polling period for resource statuses. Default to 2s. + PollPeriod string `yaml:"pollPeriod,omitempty"` + + // PrunePropagationPolicy sets the propagation policy for pruning. + // Possible settings are Background, Foreground, Orphan. + // Default to "Background". + PrunePropagationPolicy string `yaml:"prunePropagationPolicy,omitempty"` + + // PruneTimeout sets the time threshold to wait for all pruned resources to be deleted. + PruneTimeout string `yaml:"pruneTimeout,omitempty"` + + // ReconcileTimeout sets the time threshold to wait for all resources to reach the current status. + ReconcileTimeout string `yaml:"reconcileTimeout,omitempty"` } // HelmRelease describes a helm release to be deployed. From e4ec8993a086a03428773847edce7e843b384560 Mon Sep 17 00:00:00 2001 From: Marlon Gamez Date: Mon, 31 Aug 2020 03:47:36 -0700 Subject: [PATCH 119/138] add test for using helm setFiles in skaffold.yaml (#4735) --- pkg/skaffold/deploy/helm_test.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/pkg/skaffold/deploy/helm_test.go b/pkg/skaffold/deploy/helm_test.go index cc68c01158f..86e2bf09b90 100644 --- a/pkg/skaffold/deploy/helm_test.go +++ b/pkg/skaffold/deploy/helm_test.go @@ -104,6 +104,20 @@ var testDeployConfigValuesFilesTemplated = latest.HelmDeploy{ }}, } +var testDeployConfigSetFiles = latest.HelmDeploy{ + Releases: []latest.HelmRelease{{ + Name: "skaffold-helm", + ChartPath: "examples/test", + ArtifactOverrides: map[string]string{ + "image": "skaffold-helm", + }, + Overrides: schemautil.HelmOverrides{Values: map[string]interface{}{"foo": "bar"}}, + SetFiles: map[string]string{ + "value": "/some/file.yaml", + }, + }}, +} + var testDeployRecreatePodsConfig = latest.HelmDeploy{ Releases: []latest.HelmRelease{{ Name: "skaffold-helm", @@ -755,6 +769,17 @@ func TestHelmDeploy(t *testing.T) { runContext: makeRunContext(testDeployConfigValuesFilesTemplated, false), builds: testBuilds, }, + { + description: "deploy with setFiles", + commands: testutil. + CmdRunWithOutput("helm version --client", version20rc). + AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"). + AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). + AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set-file value=/some/file.yaml --kubeconfig kubeconfig"). + AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"), + runContext: makeRunContext(testDeployConfigSetFiles, false), + builds: testBuilds, + }, { description: "deploy without actual tags", commands: testutil. From 31d035a0e9acd16642c61b85df38ccb6285f25a5 Mon Sep 17 00:00:00 2001 From: David Gageot Date: Mon, 31 Aug 2020 17:11:53 +0200 Subject: [PATCH 120/138] Fix slow tests (#4741) Mock docker client Signed-off-by: David Gageot --- pkg/skaffold/test/test_test.go | 91 +++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/pkg/skaffold/test/test_test.go b/pkg/skaffold/test/test_test.go index d1be269dad5..d37bf86f6ff 100644 --- a/pkg/skaffold/test/test_test.go +++ b/pkg/skaffold/test/test_test.go @@ -35,63 +35,72 @@ import ( ) func TestNoTestDependencies(t *testing.T) { - runCtx := &runcontext.RunContext{} + testutil.Run(t, "", func(t *testutil.T) { + t.Override(&docker.NewAPIClient, func(*runcontext.RunContext) (docker.LocalDaemon, error) { return nil, nil }) - deps, err := NewTester(runCtx, true).TestDependencies() + runCtx := &runcontext.RunContext{} + deps, err := NewTester(runCtx, true).TestDependencies() - testutil.CheckErrorAndDeepEqual(t, false, err, 0, len(deps)) + t.CheckNoError(err) + t.CheckEmpty(deps) + }) } func TestTestDependencies(t *testing.T) { - tmpDir := testutil.NewTempDir(t) - - tmpDir.Touch("tests/test1.yaml", "tests/test2.yaml", "test3.yaml") + testutil.Run(t, "", func(t *testutil.T) { + tmpDir := t.NewTempDir().Touch("tests/test1.yaml", "tests/test2.yaml", "test3.yaml") - runCtx := &runcontext.RunContext{ - WorkingDir: tmpDir.Root(), - Cfg: latest.Pipeline{ - Test: []*latest.TestCase{ - {StructureTests: []string{"./tests/*"}}, - {}, - {StructureTests: []string{"test3.yaml"}}, + runCtx := &runcontext.RunContext{ + WorkingDir: tmpDir.Root(), + Cfg: latest.Pipeline{ + Test: []*latest.TestCase{ + {StructureTests: []string{"./tests/*"}}, + {}, + {StructureTests: []string{"test3.yaml"}}, + }, }, - }, - } - - deps, err := NewTester(runCtx, true).TestDependencies() + } + deps, err := NewTester(runCtx, true).TestDependencies() - expectedDeps := tmpDir.Paths("tests/test1.yaml", "tests/test2.yaml", "test3.yaml") - testutil.CheckErrorAndDeepEqual(t, false, err, expectedDeps, deps) + expectedDeps := tmpDir.Paths("tests/test1.yaml", "tests/test2.yaml", "test3.yaml") + t.CheckNoError(err) + t.CheckDeepEqual(expectedDeps, deps) + }) } func TestWrongPattern(t *testing.T) { - runCtx := &runcontext.RunContext{ - Cfg: latest.Pipeline{ - Test: []*latest.TestCase{{ - ImageName: "image", - StructureTests: []string{"[]"}, - }}, - }, - } - - tester := NewTester(runCtx, true) - - _, err := tester.TestDependencies() - testutil.CheckError(t, true, err) - - err = tester.Test(context.Background(), ioutil.Discard, []build.Artifact{{ - ImageName: "image", - Tag: "image:tag", - }}) - testutil.CheckError(t, true, err) + testutil.Run(t, "", func(t *testutil.T) { + runCtx := &runcontext.RunContext{ + Cfg: latest.Pipeline{ + Test: []*latest.TestCase{{ + ImageName: "image", + StructureTests: []string{"[]"}, + }}, + }, + } + + tester := NewTester(runCtx, true) + + _, err := tester.TestDependencies() + t.CheckError(true, err) + + err = tester.Test(context.Background(), ioutil.Discard, []build.Artifact{{ + ImageName: "image", + Tag: "image:tag", + }}) + t.CheckError(true, err) + }) } func TestNoTest(t *testing.T) { - runCtx := &runcontext.RunContext{} + testutil.Run(t, "", func(t *testutil.T) { + runCtx := &runcontext.RunContext{} - err := NewTester(runCtx, true).Test(context.Background(), ioutil.Discard, nil) + tester := NewTester(runCtx, true) + err := tester.Test(context.Background(), ioutil.Discard, nil) - testutil.CheckError(t, false, err) + t.CheckNoError(err) + }) } func TestIgnoreDockerNotFound(t *testing.T) { From 89306fd390737fcbf005e519cac61f811f06faf8 Mon Sep 17 00:00:00 2001 From: David Gageot Date: Mon, 31 Aug 2020 17:13:55 +0200 Subject: [PATCH 121/138] Fix slow test (#4740) * Fix slow test Signed-off-by: David Gageot * Use k8s client as a last resort Signed-off-by: David Gageot --- pkg/skaffold/config/util.go | 4 ++-- pkg/skaffold/config/util_test.go | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/pkg/skaffold/config/util.go b/pkg/skaffold/config/util.go index b86446bdda2..17d8c7b494c 100644 --- a/pkg/skaffold/config/util.go +++ b/pkg/skaffold/config/util.go @@ -219,9 +219,9 @@ func GetDebugHelpersRegistry(configFile string) (string, error) { func isDefaultLocal(kubeContext string) bool { return kubeContext == constants.DefaultDockerForDesktopContext || kubeContext == constants.DefaultDockerDesktopContext || - cluster.GetClient().IsMinikube(kubeContext) || IsKindCluster(kubeContext) || - IsK3dCluster(kubeContext) + IsK3dCluster(kubeContext) || + cluster.GetClient().IsMinikube(kubeContext) } // IsImageLoadingRequired checks if the cluster requires loading images into it diff --git a/pkg/skaffold/config/util_test.go b/pkg/skaffold/config/util_test.go index 4ff0742acb2..0f3ec3c175f 100644 --- a/pkg/skaffold/config/util_test.go +++ b/pkg/skaffold/config/util_test.go @@ -18,11 +18,13 @@ package config import ( "fmt" + "os/exec" "path/filepath" "strings" "testing" "time" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/cluster" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/yaml" "github.com/GoogleContainerTools/skaffold/testutil" @@ -281,6 +283,8 @@ func TestIsDefaultLocal(t *testing.T) { } for _, test := range tests { testutil.Run(t, "", func(t *testutil.T) { + t.Override(&cluster.GetClient, func() cluster.Client { return fakeClient{} }) + local := isDefaultLocal(test.context) t.CheckDeepEqual(test.expectedLocal, local) @@ -288,6 +292,11 @@ func TestIsDefaultLocal(t *testing.T) { } } +type fakeClient struct{} + +func (fakeClient) IsMinikube(kubeContext string) bool { return kubeContext == "minikube" } +func (fakeClient) MinikubeExec(...string) (*exec.Cmd, error) { return nil, nil } + func TestIsImageLoadingRequired(t *testing.T) { tests := []struct { context string From 13efa7efac022c3dc1f790dd1d712da6ec722007 Mon Sep 17 00:00:00 2001 From: David Gageot Date: Mon, 31 Aug 2020 17:47:58 +0200 Subject: [PATCH 122/138] Bump golangci lint v1.30.0 (#4739) * Update golangci-lint to v1.30.0 Signed-off-by: David Gageot * Activate `gci` linter. Replaces the `pedantic-imports` checks Signed-off-by: David Gageot --- hack/golangci-lint.sh | 2 +- hack/golangci.yml | 1 + hack/linters.sh | 1 - hack/pedantic-imports.sh | 46 ------------------- integration/rpc_test.go | 3 +- .../initializer/analyze/analyze_test.go | 5 +- pkg/skaffold/kubernetes/client/client.go | 4 +- pkg/skaffold/schema/v1beta5/upgrade_test.go | 3 +- 8 files changed, 10 insertions(+), 55 deletions(-) delete mode 100755 hack/pedantic-imports.sh diff --git a/hack/golangci-lint.sh b/hack/golangci-lint.sh index 198ac9fac41..f71639e4ae8 100755 --- a/hack/golangci-lint.sh +++ b/hack/golangci-lint.sh @@ -18,7 +18,7 @@ set -e -o pipefail DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" BIN=${DIR}/bin -VERSION=1.27.0 +VERSION=1.30.0 function install_linter() { echo "Installing GolangCI-Lint" diff --git a/hack/golangci.yml b/hack/golangci.yml index 0836ee90959..7a402291f1d 100644 --- a/hack/golangci.yml +++ b/hack/golangci.yml @@ -8,6 +8,7 @@ linters: - bodyclose - deadcode - dogsled + - gci - goconst - gocritic - goimports diff --git a/hack/linters.sh b/hack/linters.sh index 396f0f196e8..2c963971e64 100755 --- a/hack/linters.sh +++ b/hack/linters.sh @@ -22,7 +22,6 @@ echo "Running linters..." scripts=( "hack/boilerplate.sh" "hack/gofmt.sh" - "hack/pedantic-imports.sh" "hack/golangci-lint.sh" ) fail=0 diff --git a/hack/pedantic-imports.sh b/hack/pedantic-imports.sh deleted file mode 100755 index 8d60ddea63b..00000000000 --- a/hack/pedantic-imports.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2020 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. - -EXIT_CODE=0 - -for gofile in $(find . -name *.go | grep -v '/vendor/'); do - awk '{ - if ($0 == "import (") {inImport=1} - if (inImport && $0 == "") {blankLines++} - if ($0 == ")") {inImport=0; exit} - } END { - if (blankLines > 2) {exit 1} - }' "${gofile}" - if [[ $? -ne 0 ]]; then - echo "${gofile} contains more than 3 groups of imports" - EXIT_CODE=1 - fi - - awk '{ - if ($0 == "import (") {inImport=1} - if (inImport && $0 == "") {blankLines++} - if (inImport && $0 != ")") {last=$0} - if ($0 == ")") {inImport=0; exit} - } END { - if (blankLines == 2 && index(last, "github.com/GoogleContainerTools") == 0) {exit 1} - }' "${gofile}" - if [[ $? -ne 0 ]]; then - echo "${gofile} should have skaffold imports last" - EXIT_CODE=1 - fi -done - -exit ${EXIT_CODE} diff --git a/integration/rpc_test.go b/integration/rpc_test.go index dc9c527ccb5..b3f50e07601 100644 --- a/integration/rpc_test.go +++ b/integration/rpc_test.go @@ -27,7 +27,8 @@ import ( "testing" "time" - "github.com/golang/protobuf/jsonpb" //nolint:golint,staticcheck + //nolint:golint,staticcheck + "github.com/golang/protobuf/jsonpb" "github.com/golang/protobuf/ptypes/empty" "google.golang.org/grpc" diff --git a/pkg/skaffold/initializer/analyze/analyze_test.go b/pkg/skaffold/initializer/analyze/analyze_test.go index 5e922d5c4c9..51eb7e13e7d 100644 --- a/pkg/skaffold/initializer/analyze/analyze_test.go +++ b/pkg/skaffold/initializer/analyze/analyze_test.go @@ -21,11 +21,10 @@ import ( "strings" "testing" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" - initconfig "github.com/GoogleContainerTools/skaffold/pkg/skaffold/initializer/config" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/jib" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" + initconfig "github.com/GoogleContainerTools/skaffold/pkg/skaffold/initializer/config" "github.com/GoogleContainerTools/skaffold/testutil" ) diff --git a/pkg/skaffold/kubernetes/client/client.go b/pkg/skaffold/kubernetes/client/client.go index 5d0c4a17471..62c806e2dcc 100644 --- a/pkg/skaffold/kubernetes/client/client.go +++ b/pkg/skaffold/kubernetes/client/client.go @@ -21,7 +21,9 @@ import ( "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" - _ "k8s.io/client-go/plugin/pkg/client/auth" // Initialize all known client auth plugins + + // Initialize all known client auth plugins + _ "k8s.io/client-go/plugin/pkg/client/auth" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/context" ) diff --git a/pkg/skaffold/schema/v1beta5/upgrade_test.go b/pkg/skaffold/schema/v1beta5/upgrade_test.go index 5a02e1cc061..e8c08c51671 100644 --- a/pkg/skaffold/schema/v1beta5/upgrade_test.go +++ b/pkg/skaffold/schema/v1beta5/upgrade_test.go @@ -19,9 +19,8 @@ package v1beta5 import ( "testing" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/yaml" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1beta6" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/yaml" "github.com/GoogleContainerTools/skaffold/testutil" ) From d64b7dfdd20dee86cda32f1ce37e5264a8d13976 Mon Sep 17 00:00:00 2001 From: David Gageot Date: Mon, 31 Aug 2020 18:44:47 +0200 Subject: [PATCH 123/138] Introduce Config interfaces (#4598) Signed-off-by: David Gageot --- cmd/skaffold/app/cmd/runner_test.go | 3 +- pkg/skaffold/build/cache/cache.go | 22 +++++++---- pkg/skaffold/build/cache/retrieve_test.go | 4 +- pkg/skaffold/build/cluster/types.go | 26 +++++++++---- pkg/skaffold/build/cluster/types_test.go | 2 +- pkg/skaffold/build/gcb/types.go | 21 +++++++--- pkg/skaffold/build/jib/types.go | 6 +++ pkg/skaffold/build/local/docker_test.go | 3 +- pkg/skaffold/build/local/local_test.go | 10 ++--- pkg/skaffold/build/local/types.go | 39 ++++++++++++------- pkg/skaffold/deploy/helm.go | 13 +++---- pkg/skaffold/deploy/helm_test.go | 6 +-- pkg/skaffold/deploy/kpt.go | 9 ++--- pkg/skaffold/deploy/kubectl.go | 29 +++++++++----- pkg/skaffold/deploy/kubectl/cli.go | 15 ++++--- pkg/skaffold/deploy/kustomize.go | 11 +++--- pkg/skaffold/deploy/resource/deployment.go | 5 +-- pkg/skaffold/deploy/status_check.go | 34 ++++++++++------ pkg/skaffold/diagnose/diagnose.go | 21 ++++++---- pkg/skaffold/docker/client.go | 14 +++++-- .../generate_pipeline/generate_pipeline.go | 7 ++-- pkg/skaffold/generate_pipeline/profile.go | 5 +-- pkg/skaffold/kubectl/cli.go | 15 ++++--- pkg/skaffold/runner/deploy_test.go | 5 +-- pkg/skaffold/runner/generate_pipeline.go | 4 +- pkg/skaffold/runner/new.go | 20 +++++----- pkg/skaffold/sync/types.go | 18 ++++++--- pkg/skaffold/test/test.go | 20 +++++++--- pkg/skaffold/test/test_test.go | 8 ++-- pkg/skaffold/trigger/triggers.go | 24 +++++++----- 30 files changed, 258 insertions(+), 161 deletions(-) diff --git a/cmd/skaffold/app/cmd/runner_test.go b/cmd/skaffold/app/cmd/runner_test.go index dcd467520db..a40e04bcdf9 100644 --- a/cmd/skaffold/app/cmd/runner_test.go +++ b/cmd/skaffold/app/cmd/runner_test.go @@ -24,7 +24,6 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/update" "github.com/GoogleContainerTools/skaffold/testutil" @@ -86,7 +85,7 @@ func TestCreateNewRunner(t *testing.T) { } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - t.Override(&docker.NewAPIClient, func(*runcontext.RunContext) (docker.LocalDaemon, error) { + t.Override(&docker.NewAPIClient, func(docker.Config) (docker.LocalDaemon, error) { return nil, nil }) t.Override(&update.GetLatestAndCurrentVersion, func() (semver.Version, semver.Version, error) { diff --git a/pkg/skaffold/build/cache/cache.go b/pkg/skaffold/build/cache/cache.go index a3f938dd9d7..dad459700c8 100644 --- a/pkg/skaffold/build/cache/cache.go +++ b/pkg/skaffold/build/cache/cache.go @@ -25,9 +25,9 @@ import ( homedir "github.com/mitchellh/go-homedir" "github.com/sirupsen/logrus" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/yaml" @@ -55,13 +55,21 @@ type cache struct { // DependencyLister fetches a list of dependencies for an artifact type DependencyLister func(ctx context.Context, artifact *latest.Artifact) ([]string, error) +type Config interface { + docker.Config + + CacheArtifacts() bool + CacheFile() string + Mode() config.RunMode +} + // NewCache returns the current state of the cache -func NewCache(runCtx *runcontext.RunContext, imagesAreLocal bool, dependencies DependencyLister) (Cache, error) { - if !runCtx.CacheArtifacts() { +func NewCache(cfg Config, imagesAreLocal bool, dependencies DependencyLister) (Cache, error) { + if !cfg.CacheArtifacts() { return &noCache{}, nil } - cacheFile, err := resolveCacheFile(runCtx.CacheFile()) + cacheFile, err := resolveCacheFile(cfg.CacheFile()) if err != nil { logrus.Warnf("Error resolving cache file, not using skaffold cache: %v", err) return &noCache{}, nil @@ -73,7 +81,7 @@ func NewCache(runCtx *runcontext.RunContext, imagesAreLocal bool, dependencies D return &noCache{}, nil } - client, err := docker.NewAPIClient(runCtx) + client, err := docker.NewAPIClient(cfg) if imagesAreLocal && err != nil { return nil, fmt.Errorf("getting local Docker client: %w", err) } @@ -81,11 +89,11 @@ func NewCache(runCtx *runcontext.RunContext, imagesAreLocal bool, dependencies D return &cache{ artifactCache: artifactCache, client: client, - insecureRegistries: runCtx.GetInsecureRegistries(), + insecureRegistries: cfg.GetInsecureRegistries(), cacheFile: cacheFile, imagesAreLocal: imagesAreLocal, hashForArtifact: func(ctx context.Context, a *latest.Artifact) (string, error) { - return getHashForArtifact(ctx, dependencies, a, runCtx.Mode()) + return getHashForArtifact(ctx, dependencies, a, cfg.Mode()) }, }, nil } diff --git a/pkg/skaffold/build/cache/retrieve_test.go b/pkg/skaffold/build/cache/retrieve_test.go index 0d7f451fc53..dab43d6fd3b 100644 --- a/pkg/skaffold/build/cache/retrieve_test.go +++ b/pkg/skaffold/build/cache/retrieve_test.go @@ -118,7 +118,7 @@ func TestCacheBuildLocal(t *testing.T) { // Mock Docker t.Override(&docker.DefaultAuthHelper, stubAuth{}) dockerDaemon := docker.NewLocalDaemon(&testutil.FakeAPIClient{}, nil, false, nil) - t.Override(&docker.NewAPIClient, func(*runcontext.RunContext) (docker.LocalDaemon, error) { + t.Override(&docker.NewAPIClient, func(docker.Config) (docker.LocalDaemon, error) { return dockerDaemon, nil }) @@ -205,7 +205,7 @@ func TestCacheBuildRemote(t *testing.T) { // Mock Docker dockerDaemon := docker.NewLocalDaemon(&testutil.FakeAPIClient{}, nil, false, nil) - t.Override(&docker.NewAPIClient, func(*runcontext.RunContext) (docker.LocalDaemon, error) { + t.Override(&docker.NewAPIClient, func(docker.Config) (docker.LocalDaemon, error) { return dockerDaemon, nil }) t.Override(&docker.DefaultAuthHelper, stubAuth{}) diff --git a/pkg/skaffold/build/cluster/types.go b/pkg/skaffold/build/cluster/types.go index 4da8acb809f..5556dc9e99e 100644 --- a/pkg/skaffold/build/cluster/types.go +++ b/pkg/skaffold/build/cluster/types.go @@ -23,8 +23,9 @@ import ( "time" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" ) @@ -39,20 +40,29 @@ type Builder struct { muted build.Muted } +type Config interface { + kubectl.Config + docker.Config + + Pipeline() latest.Pipeline + GetKubeContext() string + Muted() config.Muted +} + // NewBuilder creates a new Builder that builds artifacts on cluster. -func NewBuilder(runCtx *runcontext.RunContext) (*Builder, error) { - timeout, err := time.ParseDuration(runCtx.Pipeline().Build.Cluster.Timeout) +func NewBuilder(cfg Config) (*Builder, error) { + timeout, err := time.ParseDuration(cfg.Pipeline().Build.Cluster.Timeout) if err != nil { return nil, fmt.Errorf("parsing timeout: %w", err) } return &Builder{ - ClusterDetails: runCtx.Pipeline().Build.Cluster, - kubectlcli: kubectl.NewFromRunContext(runCtx), + ClusterDetails: cfg.Pipeline().Build.Cluster, + kubectlcli: kubectl.NewFromRunContext(cfg), timeout: timeout, - kubeContext: runCtx.GetKubeContext(), - insecureRegistries: runCtx.GetInsecureRegistries(), - muted: runCtx.Muted(), + kubeContext: cfg.GetKubeContext(), + insecureRegistries: cfg.GetInsecureRegistries(), + muted: cfg.Muted(), }, nil } diff --git a/pkg/skaffold/build/cluster/types_test.go b/pkg/skaffold/build/cluster/types_test.go index e35f422985e..64d9b74064e 100644 --- a/pkg/skaffold/build/cluster/types_test.go +++ b/pkg/skaffold/build/cluster/types_test.go @@ -40,7 +40,7 @@ func TestNewBuilder(t *testing.T) { tests := []struct { description string shouldErr bool - runCtx *runcontext.RunContext + runCtx Config expectedBuilder *Builder }{ { diff --git a/pkg/skaffold/build/gcb/types.go b/pkg/skaffold/build/gcb/types.go index 96baa99bc18..d32a80789eb 100644 --- a/pkg/skaffold/build/gcb/types.go +++ b/pkg/skaffold/build/gcb/types.go @@ -24,7 +24,8 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" ) @@ -83,13 +84,21 @@ type Builder struct { muted build.Muted } +type Config interface { + docker.Config + + Pipeline() latest.Pipeline + SkipTests() bool + Muted() config.Muted +} + // NewBuilder creates a new Builder that builds artifacts with Google Cloud Build. -func NewBuilder(runCtx *runcontext.RunContext) *Builder { +func NewBuilder(cfg Config) *Builder { return &Builder{ - GoogleCloudBuild: runCtx.Pipeline().Build.GoogleCloudBuild, - skipTests: runCtx.SkipTests(), - insecureRegistries: runCtx.GetInsecureRegistries(), - muted: runCtx.Muted(), + GoogleCloudBuild: cfg.Pipeline().Build.GoogleCloudBuild, + skipTests: cfg.SkipTests(), + insecureRegistries: cfg.GetInsecureRegistries(), + muted: cfg.Muted(), } } diff --git a/pkg/skaffold/build/jib/types.go b/pkg/skaffold/build/jib/types.go index 73e6ac54bb1..72da309df81 100644 --- a/pkg/skaffold/build/jib/types.go +++ b/pkg/skaffold/build/jib/types.go @@ -26,6 +26,12 @@ type Builder struct { skipTests bool } +type Config interface { + docker.Config + + SkipTests() bool +} + // NewArtifactBuilder returns a new customjib artifact builder func NewArtifactBuilder(localDocker docker.LocalDaemon, insecureRegistries map[string]bool, pushImages, skipTests bool) *Builder { return &Builder{ diff --git a/pkg/skaffold/build/local/docker_test.go b/pkg/skaffold/build/local/docker_test.go index bdf8524bbd5..5636796392b 100644 --- a/pkg/skaffold/build/local/docker_test.go +++ b/pkg/skaffold/build/local/docker_test.go @@ -26,7 +26,6 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/cluster" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/testutil" @@ -86,7 +85,7 @@ func TestDockerCLIBuild(t *testing.T) { )) t.Override(&cluster.GetClient, func() cluster.Client { return fakeMinikubeClient{} }) t.Override(&util.OSEnviron, func() []string { return []string{"KEY=VALUE"} }) - t.Override(&docker.NewAPIClient, func(*runcontext.RunContext) (docker.LocalDaemon, error) { + t.Override(&docker.NewAPIClient, func(docker.Config) (docker.LocalDaemon, error) { return docker.NewLocalDaemon(&testutil.FakeAPIClient{}, test.extraEnv, false, nil), nil }) diff --git a/pkg/skaffold/build/local/local_test.go b/pkg/skaffold/build/local/local_test.go index 9a737776a0e..f1f5a5ad016 100644 --- a/pkg/skaffold/build/local/local_test.go +++ b/pkg/skaffold/build/local/local_test.go @@ -232,7 +232,7 @@ func TestLocalRun(t *testing.T) { t.Override(&docker.DefaultAuthHelper, testAuthHelper{}) fakeWarner := &warnings.Collect{} t.Override(&warnings.Printf, fakeWarner.Warnf) - t.Override(&docker.NewAPIClient, func(*runcontext.RunContext) (docker.LocalDaemon, error) { + t.Override(&docker.NewAPIClient, func(docker.Config) (docker.LocalDaemon, error) { return docker.NewLocalDaemon(test.api, nil, false, nil), nil }) t.Override(&docker.EvalBuildArgs, func(mode config.RunMode, workspace string, a *latest.DockerArtifact) (map[string]*string, error) { @@ -274,18 +274,18 @@ func TestNewBuilder(t *testing.T) { localBuild latest.LocalBuild expectedBuilder *Builder localClusterFn func(string, string) (bool, error) - localDockerFn func(*runcontext.RunContext) (docker.LocalDaemon, error) + localDockerFn func(docker.Config) (docker.LocalDaemon, error) }{ { description: "failed to get docker client", - localDockerFn: func(*runcontext.RunContext) (docker.LocalDaemon, error) { + localDockerFn: func(docker.Config) (docker.LocalDaemon, error) { return nil, errors.New("dummy docker error") }, shouldErr: true, }, { description: "pushImages becomes !localCluster when local:push is not defined", - localDockerFn: func(*runcontext.RunContext) (docker.LocalDaemon, error) { + localDockerFn: func(docker.Config) (docker.LocalDaemon, error) { return dummyDaemon, nil }, localClusterFn: func(string, string) (b bool, e error) { @@ -308,7 +308,7 @@ func TestNewBuilder(t *testing.T) { }, { description: "pushImages defined in config (local:push)", - localDockerFn: func(*runcontext.RunContext) (docker.LocalDaemon, error) { + localDockerFn: func(docker.Config) (docker.LocalDaemon, error) { return dummyDaemon, nil }, localClusterFn: func(string, string) (b bool, e error) { diff --git a/pkg/skaffold/build/local/types.go b/pkg/skaffold/build/local/types.go index 6dfc5b6bef7..c07426c1e4f 100644 --- a/pkg/skaffold/build/local/types.go +++ b/pkg/skaffold/build/local/types.go @@ -26,7 +26,6 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" ) @@ -52,9 +51,21 @@ type Builder struct { var getLocalCluster = config.GetLocalCluster +type Config interface { + docker.Config + + Pipeline() latest.Pipeline + GlobalConfig() string + GetKubeContext() string + SkipTests() bool + Mode() config.RunMode + NoPruneChildren() bool + Muted() config.Muted +} + // NewBuilder returns an new instance of a local Builder. -func NewBuilder(runCtx *runcontext.RunContext) (*Builder, error) { - localDocker, err := docker.NewAPIClient(runCtx) +func NewBuilder(cfg Config) (*Builder, error) { + localDocker, err := docker.NewAPIClient(cfg) if err != nil { return nil, fmt.Errorf("getting docker client: %w", err) } @@ -63,31 +74,31 @@ func NewBuilder(runCtx *runcontext.RunContext) (*Builder, error) { // remove minikubeProfile from here and instead detect it by matching the // kubecontext API Server to minikube profiles - localCluster, err := getLocalCluster(runCtx.GlobalConfig(), runCtx.MinikubeProfile()) + localCluster, err := getLocalCluster(cfg.GlobalConfig(), cfg.MinikubeProfile()) if err != nil { return nil, fmt.Errorf("getting localCluster: %w", err) } var pushImages bool - if runCtx.Pipeline().Build.LocalBuild.Push == nil { + if cfg.Pipeline().Build.LocalBuild.Push == nil { pushImages = !localCluster logrus.Debugf("push value not present, defaulting to %t because localCluster is %t", pushImages, localCluster) } else { - pushImages = *runCtx.Pipeline().Build.LocalBuild.Push + pushImages = *cfg.Pipeline().Build.LocalBuild.Push } return &Builder{ - cfg: *runCtx.Pipeline().Build.LocalBuild, - kubeContext: runCtx.GetKubeContext(), + cfg: *cfg.Pipeline().Build.LocalBuild, + kubeContext: cfg.GetKubeContext(), localDocker: localDocker, localCluster: localCluster, pushImages: pushImages, - skipTests: runCtx.Opts.SkipTests, - mode: runCtx.Mode(), - prune: runCtx.Opts.Prune(), - pruneChildren: !runCtx.Opts.NoPruneChildren, - insecureRegistries: runCtx.InsecureRegistries, - muted: runCtx.Muted(), + skipTests: cfg.SkipTests(), + mode: cfg.Mode(), + prune: cfg.Prune(), + pruneChildren: !cfg.NoPruneChildren(), + insecureRegistries: cfg.GetInsecureRegistries(), + muted: cfg.Muted(), }, nil } diff --git a/pkg/skaffold/deploy/helm.go b/pkg/skaffold/deploy/helm.go index 2bef7f09961..fd8eb5d25b5 100644 --- a/pkg/skaffold/deploy/helm.go +++ b/pkg/skaffold/deploy/helm.go @@ -41,7 +41,6 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/color" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/walk" @@ -80,13 +79,13 @@ type HelmDeployer struct { } // NewHelmDeployer returns a configured HelmDeployer -func NewHelmDeployer(runCtx *runcontext.RunContext, labels map[string]string) *HelmDeployer { +func NewHelmDeployer(cfg Config, labels map[string]string) *HelmDeployer { return &HelmDeployer{ - HelmDeploy: runCtx.Pipeline().Deploy.HelmDeploy, - kubeContext: runCtx.GetKubeContext(), - kubeConfig: runCtx.GetKubeConfig(), - namespace: runCtx.GetKubeNamespace(), - forceDeploy: runCtx.ForceDeploy(), + HelmDeploy: cfg.Pipeline().Deploy.HelmDeploy, + kubeContext: cfg.GetKubeContext(), + kubeConfig: cfg.GetKubeConfig(), + namespace: cfg.GetKubeNamespace(), + forceDeploy: cfg.ForceDeploy(), labels: labels, } } diff --git a/pkg/skaffold/deploy/helm_test.go b/pkg/skaffold/deploy/helm_test.go index 86e2bf09b90..5ff29e6b382 100644 --- a/pkg/skaffold/deploy/helm_test.go +++ b/pkg/skaffold/deploy/helm_test.go @@ -403,7 +403,7 @@ func TestHelmDeploy(t *testing.T) { tests := []struct { description string commands util.Command - runContext *runcontext.RunContext + runContext Config builds []build.Artifact shouldErr bool expectedWarnings []string @@ -833,7 +833,7 @@ func TestHelmCleanup(t *testing.T) { tests := []struct { description string commands util.Command - runContext *runcontext.RunContext + runContext Config builds []build.Artifact shouldErr bool expectedWarnings []string @@ -1081,7 +1081,7 @@ func TestHelmRender(t *testing.T) { description string shouldErr bool commands util.Command - runContext *runcontext.RunContext + runContext Config outputFile string expected string builds []build.Artifact diff --git a/pkg/skaffold/deploy/kpt.go b/pkg/skaffold/deploy/kpt.go index b77f9390928..6beb10a34d7 100644 --- a/pkg/skaffold/deploy/kpt.go +++ b/pkg/skaffold/deploy/kpt.go @@ -32,7 +32,6 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" deploy "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" ) @@ -52,12 +51,12 @@ type KptDeployer struct { globalConfig string } -func NewKptDeployer(runCtx *runcontext.RunContext, labels map[string]string) *KptDeployer { +func NewKptDeployer(ctx Config, labels map[string]string) *KptDeployer { return &KptDeployer{ - KptDeploy: runCtx.Pipeline().Deploy.KptDeploy, - insecureRegistries: runCtx.GetInsecureRegistries(), + KptDeploy: ctx.Pipeline().Deploy.KptDeploy, + insecureRegistries: ctx.GetInsecureRegistries(), labels: labels, - globalConfig: runCtx.GlobalConfig(), + globalConfig: ctx.GlobalConfig(), } } diff --git a/pkg/skaffold/deploy/kubectl.go b/pkg/skaffold/deploy/kubectl.go index ae619ac24f1..255a7ea47fb 100644 --- a/pkg/skaffold/deploy/kubectl.go +++ b/pkg/skaffold/deploy/kubectl.go @@ -33,9 +33,9 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/color" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" deploy "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" ) @@ -54,17 +54,28 @@ type KubectlDeployer struct { skipRender bool } +type Config interface { + deploy.Config + docker.Config + + Pipeline() latest.Pipeline + GetWorkingDir() string + GlobalConfig() string + DefaultRepo() *string + SkipRender() bool +} + // NewKubectlDeployer returns a new KubectlDeployer for a DeployConfig filled // with the needed configuration for `kubectl apply` -func NewKubectlDeployer(runCtx *runcontext.RunContext, labels map[string]string) *KubectlDeployer { +func NewKubectlDeployer(cfg Config, labels map[string]string) *KubectlDeployer { return &KubectlDeployer{ - KubectlDeploy: runCtx.Pipeline().Deploy.KubectlDeploy, - workingDir: runCtx.GetWorkingDir(), - globalConfig: runCtx.GlobalConfig(), - defaultRepo: runCtx.DefaultRepo(), - kubectl: deploy.NewCLI(runCtx, runCtx.Pipeline().Deploy.KubectlDeploy.Flags), - insecureRegistries: runCtx.GetInsecureRegistries(), - skipRender: runCtx.SkipRender(), + KubectlDeploy: cfg.Pipeline().Deploy.KubectlDeploy, + workingDir: cfg.GetWorkingDir(), + globalConfig: cfg.GlobalConfig(), + defaultRepo: cfg.DefaultRepo(), + kubectl: deploy.NewCLI(cfg, cfg.Pipeline().Deploy.KubectlDeploy.Flags), + insecureRegistries: cfg.GetInsecureRegistries(), + skipRender: cfg.SkipRender(), labels: labels, } } diff --git a/pkg/skaffold/deploy/kubectl/cli.go b/pkg/skaffold/deploy/kubectl/cli.go index 87d235771d6..ff588c6cba7 100644 --- a/pkg/skaffold/deploy/kubectl/cli.go +++ b/pkg/skaffold/deploy/kubectl/cli.go @@ -28,7 +28,6 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" pkgkubectl "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" ) @@ -42,12 +41,18 @@ type CLI struct { previousApply ManifestList } -func NewCLI(runCtx *runcontext.RunContext, flags latest.KubectlFlags) CLI { +type Config interface { + pkgkubectl.Config + ForceDeploy() bool + WaitForDeletions() config.WaitForDeletions +} + +func NewCLI(cfg Config, flags latest.KubectlFlags) CLI { return CLI{ - CLI: pkgkubectl.NewFromRunContext(runCtx), + CLI: pkgkubectl.NewFromRunContext(cfg), Flags: flags, - forceDeploy: runCtx.ForceDeploy(), - waitForDeletions: runCtx.WaitForDeletions(), + forceDeploy: cfg.ForceDeploy(), + waitForDeletions: cfg.WaitForDeletions(), } } diff --git a/pkg/skaffold/deploy/kustomize.go b/pkg/skaffold/deploy/kustomize.go index 0fcdcd43b03..4ffe8f233e3 100644 --- a/pkg/skaffold/deploy/kustomize.go +++ b/pkg/skaffold/deploy/kustomize.go @@ -34,7 +34,6 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" deploy "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/warnings" @@ -100,12 +99,12 @@ type KustomizeDeployer struct { globalConfig string } -func NewKustomizeDeployer(runCtx *runcontext.RunContext, labels map[string]string) *KustomizeDeployer { +func NewKustomizeDeployer(cfg Config, labels map[string]string) *KustomizeDeployer { return &KustomizeDeployer{ - KustomizeDeploy: runCtx.Pipeline().Deploy.KustomizeDeploy, - kubectl: deploy.NewCLI(runCtx, runCtx.Pipeline().Deploy.KustomizeDeploy.Flags), - insecureRegistries: runCtx.GetInsecureRegistries(), - globalConfig: runCtx.GlobalConfig(), + KustomizeDeploy: cfg.Pipeline().Deploy.KustomizeDeploy, + kubectl: deploy.NewCLI(cfg, cfg.Pipeline().Deploy.KustomizeDeploy.Flags), + insecureRegistries: cfg.GetInsecureRegistries(), + globalConfig: cfg.GlobalConfig(), labels: labels, } } diff --git a/pkg/skaffold/deploy/resource/deployment.go b/pkg/skaffold/deploy/resource/deployment.go index 1ba4d5dab25..7bacebf5378 100644 --- a/pkg/skaffold/deploy/resource/deployment.go +++ b/pkg/skaffold/deploy/resource/deployment.go @@ -28,7 +28,6 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/diag/validator" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/proto" ) @@ -101,8 +100,8 @@ func (d *Deployment) WithValidator(pd diag.Diagnose) *Deployment { return d } -func (d *Deployment) CheckStatus(ctx context.Context, runCtx *runcontext.RunContext) { - kubeCtl := kubectl.NewFromRunContext(runCtx) +func (d *Deployment) CheckStatus(ctx context.Context, cfg kubectl.Config) { + kubeCtl := kubectl.NewFromRunContext(cfg) b, err := kubeCtl.RunOut(ctx, "rollout", "status", "deployment", d.name, "--namespace", d.namespace, "--watch=false") if ctx.Err() != nil { diff --git a/pkg/skaffold/deploy/status_check.go b/pkg/skaffold/deploy/status_check.go index bf6c069128f..93913774bdb 100644 --- a/pkg/skaffold/deploy/status_check.go +++ b/pkg/skaffold/deploy/status_check.go @@ -31,10 +31,13 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/diag" "github.com/GoogleContainerTools/skaffold/pkg/diag/validator" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/resource" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" + pkgkubectl "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl" kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/proto" ) @@ -59,6 +62,14 @@ type counter struct { failed int32 } +type StatusCheckConfig interface { + kubectl.Config + + GetNamespaces() []string + Pipeline() latest.Pipeline + Muted() config.Muted +} + // StatusChecker wait for the application to be totally deployed. type StatusChecker interface { Check(context.Context, io.Writer) error @@ -66,20 +77,19 @@ type StatusChecker interface { // StatusChecker runs status checks for pods and deployments type statusChecker struct { - // TODO use an interface #4605 - runCtx *runcontext.RunContext + cfg StatusCheckConfig labeller *DefaultLabeller deadlineSeconds int muteLogs bool } // NewStatusChecker returns a status checker which runs checks on deployments and pods. -func NewStatusChecker(runCtx *runcontext.RunContext, labeller *DefaultLabeller) StatusChecker { +func NewStatusChecker(cfg StatusCheckConfig, labeller *DefaultLabeller) StatusChecker { return statusChecker{ - muteLogs: runCtx.Muted().MuteStatusCheck(), - runCtx: runCtx, + muteLogs: cfg.Muted().MuteStatusCheck(), + cfg: cfg, labeller: labeller, - deadlineSeconds: runCtx.Pipeline().Deploy.StatusCheckDeadlineSeconds, + deadlineSeconds: cfg.Pipeline().Deploy.StatusCheckDeadlineSeconds, } } @@ -98,9 +108,9 @@ func (s statusChecker) statusCheck(ctx context.Context, out io.Writer) (proto.St } deployments := make([]*resource.Deployment, 0) - for _, n := range s.runCtx.GetNamespaces() { + for _, n := range s.cfg.GetNamespaces() { newDeployments, err := getDeployments(client, n, s.labeller, - getDeadline(s.runCtx.Pipeline().Deploy.StatusCheckDeadlineSeconds)) + getDeadline(s.cfg.Pipeline().Deploy.StatusCheckDeadlineSeconds)) if err != nil { return proto.StatusCode_STATUSCHECK_DEPLOYMENT_FETCH_ERR, fmt.Errorf("could not fetch deployments: %w", err) } @@ -119,7 +129,7 @@ func (s statusChecker) statusCheck(ctx context.Context, out io.Writer) (proto.St go func(r *resource.Deployment) { defer wg.Done() // keep updating the resource status until it fails/succeeds/times out - pollDeploymentStatus(ctx, s.runCtx, r) + pollDeploymentStatus(ctx, s.cfg, r) rcCopy := c.markProcessed(r.Status().Error()) s.printStatusCheckSummary(out, r, rcCopy) // if one deployment fails, cancel status checks for all deployments. @@ -169,7 +179,7 @@ func getDeployments(client kubernetes.Interface, ns string, l *DefaultLabeller, return deployments, nil } -func pollDeploymentStatus(ctx context.Context, runCtx *runcontext.RunContext, r *resource.Deployment) { +func pollDeploymentStatus(ctx context.Context, cfg pkgkubectl.Config, r *resource.Deployment) { pollDuration := time.Duration(defaultPollPeriodInMilliseconds) * time.Millisecond // Add poll duration to account for one last attempt after progressDeadlineSeconds. timeoutContext, cancel := context.WithTimeout(ctx, r.Deadline()+pollDuration) @@ -192,7 +202,7 @@ func pollDeploymentStatus(ctx context.Context, runCtx *runcontext.RunContext, r } return case <-time.After(pollDuration): - r.CheckStatus(timeoutContext, runCtx) + r.CheckStatus(timeoutContext, cfg) if r.IsStatusCheckCompleteOrCancelled() { return } diff --git a/pkg/skaffold/diagnose/diagnose.go b/pkg/skaffold/diagnose/diagnose.go index 761dbc49c34..7d2348550bc 100644 --- a/pkg/skaffold/diagnose/diagnose.go +++ b/pkg/skaffold/diagnose/diagnose.go @@ -27,17 +27,22 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/color" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/filemon" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/sync" ) -func CheckArtifacts(ctx context.Context, runCtx *runcontext.RunContext, out io.Writer) error { - for _, artifact := range runCtx.Pipeline().Build.Artifacts { +type Config interface { + docker.Config + + Pipeline() latest.Pipeline +} + +func CheckArtifacts(ctx context.Context, cfg Config, out io.Writer) error { + for _, artifact := range cfg.Pipeline().Build.Artifacts { color.Default.Fprintf(out, "\n%s: %s\n", typeOfArtifact(artifact), artifact.ImageName) if artifact.DockerArtifact != nil { - size, err := sizeOfDockerContext(ctx, artifact, runCtx.GetInsecureRegistries()) + size, err := sizeOfDockerContext(ctx, artifact, cfg.GetInsecureRegistries()) if err != nil { return fmt.Errorf("computing the size of the Docker context: %w", err) } @@ -45,11 +50,11 @@ func CheckArtifacts(ctx context.Context, runCtx *runcontext.RunContext, out io.W fmt.Fprintf(out, " - Size of the context: %vbytes\n", size) } - timeDeps1, deps, err := timeToListDependencies(ctx, artifact, runCtx.GetInsecureRegistries()) + timeDeps1, deps, err := timeToListDependencies(ctx, artifact, cfg.GetInsecureRegistries()) if err != nil { return fmt.Errorf("listing artifact dependencies: %w", err) } - timeDeps2, _, err := timeToListDependencies(ctx, artifact, runCtx.GetInsecureRegistries()) + timeDeps2, _, err := timeToListDependencies(ctx, artifact, cfg.GetInsecureRegistries()) if err != nil { return fmt.Errorf("listing artifact dependencies: %w", err) } @@ -57,13 +62,13 @@ func CheckArtifacts(ctx context.Context, runCtx *runcontext.RunContext, out io.W fmt.Fprintln(out, " - Dependencies:", len(deps), "files") fmt.Fprintf(out, " - Time to list dependencies: %v (2nd time: %v)\n", timeDeps1, timeDeps2) - timeSyncMap1, err := timeToConstructSyncMap(artifact, runCtx.GetInsecureRegistries()) + timeSyncMap1, err := timeToConstructSyncMap(artifact, cfg.GetInsecureRegistries()) if err != nil { if _, isNotSupported := err.(build.ErrSyncMapNotSupported); !isNotSupported { return fmt.Errorf("construct artifact dependencies: %w", err) } } - timeSyncMap2, err := timeToConstructSyncMap(artifact, runCtx.GetInsecureRegistries()) + timeSyncMap2, err := timeToConstructSyncMap(artifact, cfg.GetInsecureRegistries()) if err != nil { if _, isNotSupported := err.(build.ErrSyncMapNotSupported); !isNotSupported { return fmt.Errorf("construct artifact dependencies: %w", err) diff --git a/pkg/skaffold/docker/client.go b/pkg/skaffold/docker/client.go index f07a1c5e744..c67ca47f2f5 100644 --- a/pkg/skaffold/docker/client.go +++ b/pkg/skaffold/docker/client.go @@ -32,7 +32,6 @@ import ( "github.com/sirupsen/logrus" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/cluster" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/version" ) @@ -50,11 +49,18 @@ var ( dockerAPIClientErr error ) +type Config interface { + Prune() bool + GetKubeContext() string + MinikubeProfile() string + GetInsecureRegistries() map[string]bool +} + // NewAPIClientImpl guesses the docker client to use based on current Kubernetes context. -func NewAPIClientImpl(runCtx *runcontext.RunContext) (LocalDaemon, error) { +func NewAPIClientImpl(cfg Config) (LocalDaemon, error) { dockerAPIClientOnce.Do(func() { - env, apiClient, err := newAPIClient(runCtx.GetKubeContext(), runCtx.MinikubeProfile()) - dockerAPIClient = NewLocalDaemon(apiClient, env, runCtx.Prune(), runCtx.GetInsecureRegistries()) + env, apiClient, err := newAPIClient(cfg.GetKubeContext(), cfg.MinikubeProfile()) + dockerAPIClient = NewLocalDaemon(apiClient, env, cfg.Prune(), cfg.GetInsecureRegistries()) dockerAPIClientErr = err }) diff --git a/pkg/skaffold/generate_pipeline/generate_pipeline.go b/pkg/skaffold/generate_pipeline/generate_pipeline.go index 94d93142d71..1704b57b89d 100644 --- a/pkg/skaffold/generate_pipeline/generate_pipeline.go +++ b/pkg/skaffold/generate_pipeline/generate_pipeline.go @@ -30,7 +30,6 @@ import ( tekton "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/pipeline" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" ) @@ -41,7 +40,7 @@ type ConfigFile struct { Profile *latest.Profile } -func Yaml(out io.Writer, runCtx *runcontext.RunContext, configFiles []*ConfigFile) (*bytes.Buffer, error) { +func Yaml(out io.Writer, namespace string, configFiles []*ConfigFile) (*bytes.Buffer, error) { // Generate git resource for pipeline gitResource, err := generateGitResource() if err != nil { @@ -50,14 +49,14 @@ func Yaml(out io.Writer, runCtx *runcontext.RunContext, configFiles []*ConfigFil // Generate build task for pipeline var tasks []*tekton.Task - buildTasks, err := generateBuildTasks(runCtx.GetKubeNamespace(), configFiles) + buildTasks, err := generateBuildTasks(namespace, configFiles) if err != nil { return nil, fmt.Errorf("generating build task: %w", err) } tasks = append(tasks, buildTasks...) // Generate deploy task for pipeline - deployTasks, err := generateDeployTasks(runCtx.GetKubeNamespace(), configFiles) + deployTasks, err := generateDeployTasks(namespace, configFiles) if err != nil { return nil, fmt.Errorf("generating deploy task: %w", err) } diff --git a/pkg/skaffold/generate_pipeline/profile.go b/pkg/skaffold/generate_pipeline/profile.go index 7fb11c4f50c..bbbeb227d1b 100644 --- a/pkg/skaffold/generate_pipeline/profile.go +++ b/pkg/skaffold/generate_pipeline/profile.go @@ -28,11 +28,10 @@ import ( yamlv2 "gopkg.in/yaml.v2" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/color" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" ) -func CreateSkaffoldProfile(out io.Writer, runCtx *runcontext.RunContext, configFile *ConfigFile) error { +func CreateSkaffoldProfile(out io.Writer, namespace string, configFile *ConfigFile) error { reader := bufio.NewReader(os.Stdin) // Check for existing oncluster profile, if none exists then prompt to create one @@ -63,7 +62,7 @@ confirmLoop: } color.Default.Fprintln(out, "Creating skaffold profile \"oncluster\"...") - profile, err := generateProfile(out, runCtx.GetKubeNamespace(), configFile.Config) + profile, err := generateProfile(out, namespace, configFile.Config) if err != nil { return fmt.Errorf("generating profile \"oncluster\": %w", err) } diff --git a/pkg/skaffold/kubectl/cli.go b/pkg/skaffold/kubectl/cli.go index 3eed7424fa5..bfeb090cbef 100644 --- a/pkg/skaffold/kubectl/cli.go +++ b/pkg/skaffold/kubectl/cli.go @@ -22,7 +22,6 @@ import ( "os/exec" "sync" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" ) @@ -36,11 +35,17 @@ type CLI struct { versionOnce sync.Once } -func NewFromRunContext(runCtx *runcontext.RunContext) *CLI { +type Config interface { + GetKubeContext() string + GetKubeConfig() string + GetKubeNamespace() string +} + +func NewFromRunContext(cfg Config) *CLI { return &CLI{ - KubeContext: runCtx.GetKubeContext(), - KubeConfig: runCtx.GetKubeConfig(), - Namespace: runCtx.GetKubeNamespace(), + KubeContext: cfg.GetKubeContext(), + KubeConfig: cfg.GetKubeConfig(), + Namespace: cfg.GetKubeNamespace(), } } diff --git a/pkg/skaffold/runner/deploy_test.go b/pkg/skaffold/runner/deploy_test.go index 7c19b8a49dd..8da3a7be1e1 100644 --- a/pkg/skaffold/runner/deploy_test.go +++ b/pkg/skaffold/runner/deploy_test.go @@ -66,9 +66,8 @@ func TestDeploy(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { t.SetupFakeKubernetesContext(api.Config{CurrentContext: "cluster1"}) - t.Override(&client.Client, mockK8sClient) - t.Override(&newStatusCheck, func(*runcontext.RunContext, *deploy.DefaultLabeller) deploy.StatusChecker { + t.Override(&newStatusCheck, func(deploy.StatusCheckConfig, *deploy.DefaultLabeller) deploy.StatusChecker { return dummyStatusChecker{} }) @@ -118,7 +117,7 @@ func TestDeployNamespace(t *testing.T) { testutil.Run(t, test.description, func(t *testutil.T) { t.SetupFakeKubernetesContext(api.Config{CurrentContext: "cluster1"}) t.Override(&client.Client, mockK8sClient) - t.Override(&newStatusCheck, func(*runcontext.RunContext, *deploy.DefaultLabeller) deploy.StatusChecker { + t.Override(&newStatusCheck, func(deploy.StatusCheckConfig, *deploy.DefaultLabeller) deploy.StatusChecker { return dummyStatusChecker{} }) diff --git a/pkg/skaffold/runner/generate_pipeline.go b/pkg/skaffold/runner/generate_pipeline.go index 97dbd4682a4..38597701757 100644 --- a/pkg/skaffold/runner/generate_pipeline.go +++ b/pkg/skaffold/runner/generate_pipeline.go @@ -48,13 +48,13 @@ func (r *SkaffoldRunner) GeneratePipeline(ctx context.Context, out io.Writer, co // Will run the profile setup multiple times and require user input for each specified config color.Default.Fprintln(out, "Running profile setup...") for _, configFile := range configFiles { - if err := pipeline.CreateSkaffoldProfile(out, r.runCtx, configFile); err != nil { + if err := pipeline.CreateSkaffoldProfile(out, r.runCtx.GetKubeNamespace(), configFile); err != nil { return fmt.Errorf("setting up profile: %w", err) } } color.Default.Fprintln(out, "Generating Pipeline...") - pipelineYaml, err := pipeline.Yaml(out, r.runCtx, configFiles) + pipelineYaml, err := pipeline.Yaml(out, r.runCtx.GetKubeNamespace(), configFiles) if err != nil { return fmt.Errorf("generating pipeline yaml contents: %w", err) } diff --git a/pkg/skaffold/runner/new.go b/pkg/skaffold/runner/new.go index 640b8910d1b..93738d9f5c9 100644 --- a/pkg/skaffold/runner/new.go +++ b/pkg/skaffold/runner/new.go @@ -179,33 +179,33 @@ func getBuilder(runCtx *runcontext.RunContext) (build.Builder, bool, error) { } } -func getTester(runCtx *runcontext.RunContext, imagesAreLocal bool) test.Tester { - return test.NewTester(runCtx, imagesAreLocal) +func getTester(cfg test.Config, imagesAreLocal bool) test.Tester { + return test.NewTester(cfg, imagesAreLocal) } -func getSyncer(runCtx *runcontext.RunContext) sync.Syncer { - return sync.NewSyncer(runCtx) +func getSyncer(cfg sync.Config) sync.Syncer { + return sync.NewSyncer(cfg) } -func getDeployer(runCtx *runcontext.RunContext, labels map[string]string) deploy.Deployer { - d := runCtx.Pipeline().Deploy +func getDeployer(cfg deploy.Config, labels map[string]string) deploy.Deployer { + d := cfg.Pipeline().Deploy var deployers deploy.DeployerMux if d.HelmDeploy != nil { - deployers = append(deployers, deploy.NewHelmDeployer(runCtx, labels)) + deployers = append(deployers, deploy.NewHelmDeployer(cfg, labels)) } if d.KptDeploy != nil { - deployers = append(deployers, deploy.NewKptDeployer(runCtx, labels)) + deployers = append(deployers, deploy.NewKptDeployer(cfg, labels)) } if d.KubectlDeploy != nil { - deployers = append(deployers, deploy.NewKubectlDeployer(runCtx, labels)) + deployers = append(deployers, deploy.NewKubectlDeployer(cfg, labels)) } if d.KustomizeDeploy != nil { - deployers = append(deployers, deploy.NewKustomizeDeployer(runCtx, labels)) + deployers = append(deployers, deploy.NewKustomizeDeployer(cfg, labels)) } // avoid muxing overhead when only a single deployer is configured diff --git a/pkg/skaffold/sync/types.go b/pkg/skaffold/sync/types.go index 219399dbd7f..874b2847843 100644 --- a/pkg/skaffold/sync/types.go +++ b/pkg/skaffold/sync/types.go @@ -19,8 +19,8 @@ package sync import ( "context" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" + pkgkubectl "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl" ) type syncMap map[string][]string @@ -36,13 +36,19 @@ type Syncer interface { } type podSyncer struct { - kubectl *kubectl.CLI + kubectl *pkgkubectl.CLI namespaces []string } -func NewSyncer(runCtx *runcontext.RunContext) Syncer { +type Config interface { + kubectl.Config + + GetNamespaces() []string +} + +func NewSyncer(cfg Config) Syncer { return &podSyncer{ - kubectl: kubectl.NewFromRunContext(runCtx), - namespaces: runCtx.GetNamespaces(), + kubectl: pkgkubectl.NewFromRunContext(cfg), + namespaces: cfg.GetNamespaces(), } } diff --git a/pkg/skaffold/test/test.go b/pkg/skaffold/test/test.go index 511404a48dc..f2308bb9a31 100644 --- a/pkg/skaffold/test/test.go +++ b/pkg/skaffold/test/test.go @@ -26,27 +26,35 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/color" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/logfile" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/test/structure" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" ) +type Config interface { + docker.Config + + Pipeline() latest.Pipeline + GetWorkingDir() string + Muted() config.Muted +} + // NewTester parses the provided test cases from the Skaffold config, // and returns a Tester instance with all the necessary test runners // to run all specified tests. -func NewTester(runCtx *runcontext.RunContext, imagesAreLocal bool) Tester { - localDaemon, err := docker.NewAPIClient(runCtx) +func NewTester(cfg Config, imagesAreLocal bool) Tester { + localDaemon, err := docker.NewAPIClient(cfg) if err != nil { return nil } return FullTester{ - testCases: runCtx.Pipeline().Test, - workingDir: runCtx.GetWorkingDir(), - muted: runCtx.Muted(), + testCases: cfg.Pipeline().Test, + workingDir: cfg.GetWorkingDir(), + muted: cfg.Muted(), localDaemon: localDaemon, imagesAreLocal: imagesAreLocal, } diff --git a/pkg/skaffold/test/test_test.go b/pkg/skaffold/test/test_test.go index d37bf86f6ff..c0be7f37f87 100644 --- a/pkg/skaffold/test/test_test.go +++ b/pkg/skaffold/test/test_test.go @@ -36,7 +36,7 @@ import ( func TestNoTestDependencies(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { - t.Override(&docker.NewAPIClient, func(*runcontext.RunContext) (docker.LocalDaemon, error) { return nil, nil }) + t.Override(&docker.NewAPIClient, func(docker.Config) (docker.LocalDaemon, error) { return nil, nil }) runCtx := &runcontext.RunContext{} deps, err := NewTester(runCtx, true).TestDependencies() @@ -105,7 +105,7 @@ func TestNoTest(t *testing.T) { func TestIgnoreDockerNotFound(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { - t.Override(&docker.NewAPIClient, func(*runcontext.RunContext) (docker.LocalDaemon, error) { + t.Override(&docker.NewAPIClient, func(docker.Config) (docker.LocalDaemon, error) { return nil, errors.New("not found") }) @@ -159,7 +159,7 @@ func TestTestSuccessRemoteImage(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { t.NewTempDir().Touch("test.yaml").Chdir() t.Override(&util.DefaultExecCommand, testutil.CmdRun("container-structure-test test -v warn --image image:tag --config test.yaml")) - t.Override(&docker.NewAPIClient, func(*runcontext.RunContext) (docker.LocalDaemon, error) { + t.Override(&docker.NewAPIClient, func(docker.Config) (docker.LocalDaemon, error) { return docker.NewLocalDaemon(&testutil.FakeAPIClient{}, nil, false, nil), nil }) @@ -186,7 +186,7 @@ func TestTestFailureRemoteImage(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { t.NewTempDir().Touch("test.yaml").Chdir() t.Override(&util.DefaultExecCommand, testutil.CmdRun("container-structure-test test -v warn --image image:tag --config test.yaml")) - t.Override(&docker.NewAPIClient, func(*runcontext.RunContext) (docker.LocalDaemon, error) { + t.Override(&docker.NewAPIClient, func(docker.Config) (docker.LocalDaemon, error) { return docker.NewLocalDaemon(&testutil.FakeAPIClient{ErrImagePull: true}, nil, false, nil), nil }) diff --git a/pkg/skaffold/trigger/triggers.go b/pkg/skaffold/trigger/triggers.go index 7af9870dfb6..707120a9d01 100644 --- a/pkg/skaffold/trigger/triggers.go +++ b/pkg/skaffold/trigger/triggers.go @@ -31,7 +31,7 @@ import ( "github.com/sirupsen/logrus" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/color" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" ) // Trigger describes a mechanism that triggers the watch. @@ -41,32 +41,38 @@ type Trigger interface { Debounce() bool } +type Config interface { + Pipeline() latest.Pipeline + Trigger() string + WatchPollInterval() int +} + // NewTrigger creates a new trigger. -func NewTrigger(runCtx *runcontext.RunContext, isActive func() bool) (Trigger, error) { - switch strings.ToLower(runCtx.Trigger()) { +func NewTrigger(cfg Config, isActive func() bool) (Trigger, error) { + switch strings.ToLower(cfg.Trigger()) { case "polling": return &pollTrigger{ - Interval: time.Duration(runCtx.WatchPollInterval()) * time.Millisecond, + Interval: time.Duration(cfg.WatchPollInterval()) * time.Millisecond, isActive: isActive, }, nil case "notify": - return newFSNotifyTrigger(runCtx, isActive), nil + return newFSNotifyTrigger(cfg, isActive), nil case "manual": return &manualTrigger{ isActive: isActive, }, nil default: - return nil, fmt.Errorf("unsupported trigger: %s", runCtx.Trigger()) + return nil, fmt.Errorf("unsupported trigger: %s", cfg.Trigger()) } } -func newFSNotifyTrigger(runCtx *runcontext.RunContext, isActive func() bool) *fsNotifyTrigger { +func newFSNotifyTrigger(cfg Config, isActive func() bool) *fsNotifyTrigger { workspaces := map[string]struct{}{} - for _, a := range runCtx.Pipeline().Build.Artifacts { + for _, a := range cfg.Pipeline().Build.Artifacts { workspaces[a.Workspace] = struct{}{} } return &fsNotifyTrigger{ - Interval: time.Duration(runCtx.WatchPollInterval()) * time.Millisecond, + Interval: time.Duration(cfg.WatchPollInterval()) * time.Millisecond, workspaces: workspaces, isActive: isActive, } From 4accb21e8bc24895b036acaafbc16b2235d05004 Mon Sep 17 00:00:00 2001 From: David Gageot Date: Mon, 31 Aug 2020 19:09:57 +0200 Subject: [PATCH 124/138] Handle ctrl-c in the middle of GetAllAuthConfigs() (#4603) Signed-off-by: David Gageot --- pkg/skaffold/docker/image.go | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/pkg/skaffold/docker/image.go b/pkg/skaffold/docker/image.go index 90bb5b9e714..33262bc35d6 100644 --- a/pkg/skaffold/docker/image.go +++ b/pkg/skaffold/docker/image.go @@ -156,18 +156,36 @@ func (l *localDaemon) ConfigFile(ctx context.Context, image string) (*v1.ConfigF return cfg, nil } +func authConfig(ctx context.Context) map[string]types.AuthConfig { + auth := make(chan map[string]types.AuthConfig) + + go func() { + // Like `docker build`, we ignore the errors + // See https://github.com/docker/cli/blob/75c1bb1f33d7cedbaf48404597d5bf9818199480/cli/command/image/build.go#L364 + authConfigs, _ := DefaultAuthHelper.GetAllAuthConfigs() + auth <- authConfigs + }() + + // Because this can take a long time, we make sure it can be interrupted by the user. + select { + case <-ctx.Done(): + return nil + case r := <-auth: + return r + } +} + // Build performs a docker build and returns the imageID. func (l *localDaemon) Build(ctx context.Context, out io.Writer, workspace string, a *latest.DockerArtifact, ref string, mode config.RunMode) (string, error) { logrus.Debugf("Running docker build: context: %s, dockerfile: %s", workspace, a.DockerfilePath) - // Like `docker build`, we ignore the errors - // See https://github.com/docker/cli/blob/75c1bb1f33d7cedbaf48404597d5bf9818199480/cli/command/image/build.go#L364 - authConfigs, _ := DefaultAuthHelper.GetAllAuthConfigs() buildArgs, err := EvalBuildArgs(mode, workspace, a) if err != nil { return "", fmt.Errorf("unable to evaluate build args: %w", err) } + authConfigs := authConfig(ctx) + buildCtx, buildCtxWriter := io.Pipe() go func() { err := CreateDockerTarContext(ctx, buildCtxWriter, workspace, a, l.insecureRegistries) From b642a69dd62d67a2482a40dafce514e559c59380 Mon Sep 17 00:00:00 2001 From: David Gageot Date: Mon, 31 Aug 2020 19:43:35 +0200 Subject: [PATCH 125/138] Rename NewFromRunContext() to NewCLI() (#4743) Signed-off-by: David Gageot --- integration/port_forward_test.go | 2 +- pkg/skaffold/build/cluster/types.go | 2 +- pkg/skaffold/deploy/kubectl/cli.go | 2 +- pkg/skaffold/deploy/resource/deployment.go | 2 +- pkg/skaffold/kubectl/cli.go | 2 +- pkg/skaffold/kubectl/cli_test.go | 6 +++--- pkg/skaffold/runner/deploy_test.go | 2 +- pkg/skaffold/runner/load_images_test.go | 2 +- pkg/skaffold/runner/new.go | 2 +- pkg/skaffold/sync/types.go | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/integration/port_forward_test.go b/integration/port_forward_test.go index 02a718bf478..381282e273b 100644 --- a/integration/port_forward_test.go +++ b/integration/port_forward_test.go @@ -41,7 +41,7 @@ func TestPortForward(t *testing.T) { cfg, err := kubectx.CurrentConfig() failNowIfError(t, err) - kubectlCLI := kubectl.NewFromRunContext(&runcontext.RunContext{ + kubectlCLI := kubectl.NewCLI(&runcontext.RunContext{ KubeContext: cfg.CurrentContext, Opts: config.SkaffoldOptions{ Namespace: ns.Name, diff --git a/pkg/skaffold/build/cluster/types.go b/pkg/skaffold/build/cluster/types.go index 5556dc9e99e..149c89c6278 100644 --- a/pkg/skaffold/build/cluster/types.go +++ b/pkg/skaffold/build/cluster/types.go @@ -58,7 +58,7 @@ func NewBuilder(cfg Config) (*Builder, error) { return &Builder{ ClusterDetails: cfg.Pipeline().Build.Cluster, - kubectlcli: kubectl.NewFromRunContext(cfg), + kubectlcli: kubectl.NewCLI(cfg), timeout: timeout, kubeContext: cfg.GetKubeContext(), insecureRegistries: cfg.GetInsecureRegistries(), diff --git a/pkg/skaffold/deploy/kubectl/cli.go b/pkg/skaffold/deploy/kubectl/cli.go index ff588c6cba7..ef394097b79 100644 --- a/pkg/skaffold/deploy/kubectl/cli.go +++ b/pkg/skaffold/deploy/kubectl/cli.go @@ -49,7 +49,7 @@ type Config interface { func NewCLI(cfg Config, flags latest.KubectlFlags) CLI { return CLI{ - CLI: pkgkubectl.NewFromRunContext(cfg), + CLI: pkgkubectl.NewCLI(cfg), Flags: flags, forceDeploy: cfg.ForceDeploy(), waitForDeletions: cfg.WaitForDeletions(), diff --git a/pkg/skaffold/deploy/resource/deployment.go b/pkg/skaffold/deploy/resource/deployment.go index 7bacebf5378..c648091e04a 100644 --- a/pkg/skaffold/deploy/resource/deployment.go +++ b/pkg/skaffold/deploy/resource/deployment.go @@ -101,7 +101,7 @@ func (d *Deployment) WithValidator(pd diag.Diagnose) *Deployment { } func (d *Deployment) CheckStatus(ctx context.Context, cfg kubectl.Config) { - kubeCtl := kubectl.NewFromRunContext(cfg) + kubeCtl := kubectl.NewCLI(cfg) b, err := kubeCtl.RunOut(ctx, "rollout", "status", "deployment", d.name, "--namespace", d.namespace, "--watch=false") if ctx.Err() != nil { diff --git a/pkg/skaffold/kubectl/cli.go b/pkg/skaffold/kubectl/cli.go index bfeb090cbef..8a5bd621735 100644 --- a/pkg/skaffold/kubectl/cli.go +++ b/pkg/skaffold/kubectl/cli.go @@ -41,7 +41,7 @@ type Config interface { GetKubeNamespace() string } -func NewFromRunContext(cfg Config) *CLI { +func NewCLI(cfg Config) *CLI { return &CLI{ KubeContext: cfg.GetKubeContext(), KubeConfig: cfg.GetKubeConfig(), diff --git a/pkg/skaffold/kubectl/cli_test.go b/pkg/skaffold/kubectl/cli_test.go index db07adf0309..c9611157fdf 100644 --- a/pkg/skaffold/kubectl/cli_test.go +++ b/pkg/skaffold/kubectl/cli_test.go @@ -67,7 +67,7 @@ func TestCLI(t *testing.T) { test.expectedCommand, )) - cli := NewFromRunContext(&runcontext.RunContext{ + cli := NewCLI(&runcontext.RunContext{ Opts: config.SkaffoldOptions{ Namespace: test.namespace, KubeConfig: test.kubeconfig, @@ -88,7 +88,7 @@ func TestCLI(t *testing.T) { output, )) - cli := NewFromRunContext(&runcontext.RunContext{ + cli := NewCLI(&runcontext.RunContext{ Opts: config.SkaffoldOptions{ Namespace: test.namespace, KubeConfig: test.kubeconfig, @@ -110,7 +110,7 @@ func TestCLI(t *testing.T) { output, )) - cli := NewFromRunContext(&runcontext.RunContext{ + cli := NewCLI(&runcontext.RunContext{ Opts: config.SkaffoldOptions{ Namespace: test.namespace, KubeConfig: test.kubeconfig, diff --git a/pkg/skaffold/runner/deploy_test.go b/pkg/skaffold/runner/deploy_test.go index 8da3a7be1e1..7307230de50 100644 --- a/pkg/skaffold/runner/deploy_test.go +++ b/pkg/skaffold/runner/deploy_test.go @@ -146,7 +146,7 @@ func TestSkaffoldDeployRenderOnly(t *testing.T) { r := SkaffoldRunner{ runCtx: runCtx, - kubectlCLI: kubectl.NewFromRunContext(runCtx), + kubectlCLI: kubectl.NewCLI(runCtx), deployer: getDeployer(runCtx, nil), } var builds []build.Artifact diff --git a/pkg/skaffold/runner/load_images_test.go b/pkg/skaffold/runner/load_images_test.go index 6f4398ce86d..fe10cac6eba 100644 --- a/pkg/skaffold/runner/load_images_test.go +++ b/pkg/skaffold/runner/load_images_test.go @@ -184,7 +184,7 @@ func runImageLoadingTests(t *testing.T, tests []ImageLoadingTest, loadingFunc fu r := &SkaffoldRunner{ runCtx: runCtx, - kubectlCLI: kubectl.NewFromRunContext(runCtx), + kubectlCLI: kubectl.NewCLI(runCtx), builds: test.built, } err := loadingFunc(r, test) diff --git a/pkg/skaffold/runner/new.go b/pkg/skaffold/runner/new.go index 93738d9f5c9..4bf23c13cca 100644 --- a/pkg/skaffold/runner/new.go +++ b/pkg/skaffold/runner/new.go @@ -43,7 +43,7 @@ import ( // NewForConfig returns a new SkaffoldRunner for a SkaffoldConfig func NewForConfig(runCtx *runcontext.RunContext) (*SkaffoldRunner, error) { - kubectlCLI := kubectl.NewFromRunContext(runCtx) + kubectlCLI := kubectl.NewCLI(runCtx) tagger, err := getTagger(runCtx) if err != nil { diff --git a/pkg/skaffold/sync/types.go b/pkg/skaffold/sync/types.go index 874b2847843..c0eeb65b6a0 100644 --- a/pkg/skaffold/sync/types.go +++ b/pkg/skaffold/sync/types.go @@ -48,7 +48,7 @@ type Config interface { func NewSyncer(cfg Config) Syncer { return &podSyncer{ - kubectl: pkgkubectl.NewFromRunContext(cfg), + kubectl: pkgkubectl.NewCLI(cfg), namespaces: cfg.GetNamespaces(), } } From b91bfbe794d5cf01a8c4e8a3e6f8b445ce7702ba Mon Sep 17 00:00:00 2001 From: Gaurav <39389231+gsquared94@users.noreply.github.com> Date: Tue, 1 Sep 2020 01:30:00 +0530 Subject: [PATCH 126/138] Hide minikube detection behind flag (#4745) * Hide minikube detection behind flag * Hide minikube detection behind flag --- cmd/skaffold/app/cmd/flags.go | 8 ++++++++ docs/content/en/docs/references/cli/_index.md | 12 ++++++++++++ pkg/skaffold/build/local/local_test.go | 6 +++--- pkg/skaffold/build/local/types.go | 3 ++- pkg/skaffold/config/options.go | 1 + pkg/skaffold/config/util.go | 18 ++++++++++++------ pkg/skaffold/config/util_test.go | 5 +++-- pkg/skaffold/runner/runcontext/context.go | 1 + 8 files changed, 42 insertions(+), 12 deletions(-) diff --git a/cmd/skaffold/app/cmd/flags.go b/cmd/skaffold/app/cmd/flags.go index 575b910c880..0c90174381f 100644 --- a/cmd/skaffold/app/cmd/flags.go +++ b/cmd/skaffold/app/cmd/flags.go @@ -398,6 +398,14 @@ var flagRegistry = []Flag{ FlagAddMethod: "DurationVar", DefinedOn: []string{"deploy", "dev", "run", "debug"}, }, + { + Name: "detect-minikube", + Usage: "Use heuristics to detect a minikube cluster", + Value: &opts.DetectMinikube, + DefValue: false, + FlagAddMethod: "BoolVar", + DefinedOn: []string{"build", "debug", "delete", "deploy", "dev", "run"}, + }, } func (fl *Flag) flag() *pflag.Flag { diff --git a/docs/content/en/docs/references/cli/_index.md b/docs/content/en/docs/references/cli/_index.md index 676cd37b4ab..b53c778a87f 100644 --- a/docs/content/en/docs/references/cli/_index.md +++ b/docs/content/en/docs/references/cli/_index.md @@ -134,6 +134,7 @@ Options: --cache-file='': Specify the location of the cache file (default $HOME/.skaffold/cache) -c, --config='': File for global configurations (defaults to $HOME/.skaffold/config) -d, --default-repo='': Default repository value (overrides global config) + --detect-minikube=false: Use heuristics to detect a minikube cluster --dry-run=false: Don't build images, just compute the tag for each artifact. --enable-rpc=false: Enable gRPC for exposing Skaffold events (true by default for `skaffold dev`) --file-output='': Filename to write build images to @@ -167,6 +168,7 @@ Env vars: * `SKAFFOLD_CACHE_FILE` (same as `--cache-file`) * `SKAFFOLD_CONFIG` (same as `--config`) * `SKAFFOLD_DEFAULT_REPO` (same as `--default-repo`) +* `SKAFFOLD_DETECT_MINIKUBE` (same as `--detect-minikube`) * `SKAFFOLD_DRY_RUN` (same as `--dry-run`) * `SKAFFOLD_ENABLE_RPC` (same as `--enable-rpc`) * `SKAFFOLD_FILE_OUTPUT` (same as `--file-output`) @@ -341,6 +343,7 @@ Options: --cleanup=true: Delete deployments after dev or debug mode is interrupted -c, --config='': File for global configurations (defaults to $HOME/.skaffold/config) -d, --default-repo='': Default repository value (overrides global config) + --detect-minikube=false: Use heuristics to detect a minikube cluster --enable-rpc=false: Enable gRPC for exposing Skaffold events (true by default for `skaffold dev`) -f, --filename='skaffold.yaml': Path or URL to the Skaffold config file --force=false: Recreate Kubernetes resources if necessary for deployment, warning: might cause downtime! @@ -383,6 +386,7 @@ Env vars: * `SKAFFOLD_CLEANUP` (same as `--cleanup`) * `SKAFFOLD_CONFIG` (same as `--config`) * `SKAFFOLD_DEFAULT_REPO` (same as `--default-repo`) +* `SKAFFOLD_DETECT_MINIKUBE` (same as `--detect-minikube`) * `SKAFFOLD_ENABLE_RPC` (same as `--enable-rpc`) * `SKAFFOLD_FILENAME` (same as `--filename`) * `SKAFFOLD_FORCE` (same as `--force`) @@ -421,6 +425,7 @@ Delete the deployed application Options: -c, --config='': File for global configurations (defaults to $HOME/.skaffold/config) -d, --default-repo='': Default repository value (overrides global config) + --detect-minikube=false: Use heuristics to detect a minikube cluster -f, --filename='skaffold.yaml': Path or URL to the Skaffold config file --kube-context='': Deploy to this Kubernetes context --kubeconfig='': Path to the kubeconfig file to use for CLI requests. @@ -439,6 +444,7 @@ Env vars: * `SKAFFOLD_CONFIG` (same as `--config`) * `SKAFFOLD_DEFAULT_REPO` (same as `--default-repo`) +* `SKAFFOLD_DETECT_MINIKUBE` (same as `--detect-minikube`) * `SKAFFOLD_FILENAME` (same as `--filename`) * `SKAFFOLD_KUBE_CONTEXT` (same as `--kube-context`) * `SKAFFOLD_KUBECONFIG` (same as `--kubeconfig`) @@ -470,6 +476,7 @@ Options: -a, --build-artifacts=: File containing build result from a previous 'skaffold build --file-output' -c, --config='': File for global configurations (defaults to $HOME/.skaffold/config) -d, --default-repo='': Default repository value (overrides global config) + --detect-minikube=false: Use heuristics to detect a minikube cluster --enable-rpc=false: Enable gRPC for exposing Skaffold events (true by default for `skaffold dev`) -f, --filename='skaffold.yaml': Path or URL to the Skaffold config file --force=false: Recreate Kubernetes resources if necessary for deployment, warning: might cause downtime! @@ -504,6 +511,7 @@ Env vars: * `SKAFFOLD_BUILD_ARTIFACTS` (same as `--build-artifacts`) * `SKAFFOLD_CONFIG` (same as `--config`) * `SKAFFOLD_DEFAULT_REPO` (same as `--default-repo`) +* `SKAFFOLD_DETECT_MINIKUBE` (same as `--detect-minikube`) * `SKAFFOLD_ENABLE_RPC` (same as `--enable-rpc`) * `SKAFFOLD_FILENAME` (same as `--filename`) * `SKAFFOLD_FORCE` (same as `--force`) @@ -539,6 +547,7 @@ Options: --cleanup=true: Delete deployments after dev or debug mode is interrupted -c, --config='': File for global configurations (defaults to $HOME/.skaffold/config) -d, --default-repo='': Default repository value (overrides global config) + --detect-minikube=false: Use heuristics to detect a minikube cluster --enable-rpc=false: Enable gRPC for exposing Skaffold events (true by default for `skaffold dev`) -f, --filename='skaffold.yaml': Path or URL to the Skaffold config file --force=false: Recreate Kubernetes resources if necessary for deployment, warning: might cause downtime! @@ -582,6 +591,7 @@ Env vars: * `SKAFFOLD_CLEANUP` (same as `--cleanup`) * `SKAFFOLD_CONFIG` (same as `--config`) * `SKAFFOLD_DEFAULT_REPO` (same as `--default-repo`) +* `SKAFFOLD_DETECT_MINIKUBE` (same as `--detect-minikube`) * `SKAFFOLD_ENABLE_RPC` (same as `--enable-rpc`) * `SKAFFOLD_FILENAME` (same as `--filename`) * `SKAFFOLD_FORCE` (same as `--force`) @@ -797,6 +807,7 @@ Options: --cleanup=true: Delete deployments after dev or debug mode is interrupted -c, --config='': File for global configurations (defaults to $HOME/.skaffold/config) -d, --default-repo='': Default repository value (overrides global config) + --detect-minikube=false: Use heuristics to detect a minikube cluster --enable-rpc=false: Enable gRPC for exposing Skaffold events (true by default for `skaffold dev`) -f, --filename='skaffold.yaml': Path or URL to the Skaffold config file --force=false: Recreate Kubernetes resources if necessary for deployment, warning: might cause downtime! @@ -838,6 +849,7 @@ Env vars: * `SKAFFOLD_CLEANUP` (same as `--cleanup`) * `SKAFFOLD_CONFIG` (same as `--config`) * `SKAFFOLD_DEFAULT_REPO` (same as `--default-repo`) +* `SKAFFOLD_DETECT_MINIKUBE` (same as `--detect-minikube`) * `SKAFFOLD_ENABLE_RPC` (same as `--enable-rpc`) * `SKAFFOLD_FILENAME` (same as `--filename`) * `SKAFFOLD_FORCE` (same as `--force`) diff --git a/pkg/skaffold/build/local/local_test.go b/pkg/skaffold/build/local/local_test.go index f1f5a5ad016..91e65bb5e06 100644 --- a/pkg/skaffold/build/local/local_test.go +++ b/pkg/skaffold/build/local/local_test.go @@ -273,7 +273,7 @@ func TestNewBuilder(t *testing.T) { shouldErr bool localBuild latest.LocalBuild expectedBuilder *Builder - localClusterFn func(string, string) (bool, error) + localClusterFn func(string, string, bool) (bool, error) localDockerFn func(docker.Config) (docker.LocalDaemon, error) }{ { @@ -288,7 +288,7 @@ func TestNewBuilder(t *testing.T) { localDockerFn: func(docker.Config) (docker.LocalDaemon, error) { return dummyDaemon, nil }, - localClusterFn: func(string, string) (b bool, e error) { + localClusterFn: func(string, string, bool) (b bool, e error) { b = false //because this is false and localBuild.push is nil return }, @@ -311,7 +311,7 @@ func TestNewBuilder(t *testing.T) { localDockerFn: func(docker.Config) (docker.LocalDaemon, error) { return dummyDaemon, nil }, - localClusterFn: func(string, string) (b bool, e error) { + localClusterFn: func(string, string, bool) (b bool, e error) { b = false return }, diff --git a/pkg/skaffold/build/local/types.go b/pkg/skaffold/build/local/types.go index c07426c1e4f..153c8f87907 100644 --- a/pkg/skaffold/build/local/types.go +++ b/pkg/skaffold/build/local/types.go @@ -57,6 +57,7 @@ type Config interface { Pipeline() latest.Pipeline GlobalConfig() string GetKubeContext() string + DetectMinikube() bool SkipTests() bool Mode() config.RunMode NoPruneChildren() bool @@ -74,7 +75,7 @@ func NewBuilder(cfg Config) (*Builder, error) { // remove minikubeProfile from here and instead detect it by matching the // kubecontext API Server to minikube profiles - localCluster, err := getLocalCluster(cfg.GlobalConfig(), cfg.MinikubeProfile()) + localCluster, err := getLocalCluster(cfg.GlobalConfig(), cfg.MinikubeProfile(), cfg.DetectMinikube()) if err != nil { return nil, fmt.Errorf("getting localCluster: %w", err) } diff --git a/pkg/skaffold/config/options.go b/pkg/skaffold/config/options.go index 8dca2e97d40..a4cc504ad26 100644 --- a/pkg/skaffold/config/options.go +++ b/pkg/skaffold/config/options.go @@ -66,6 +66,7 @@ type SkaffoldOptions struct { // commands which don't deploy (e.g. `skaffold render`) since the runID // label isn't available. AddSkaffoldLabels bool + DetectMinikube bool PortForward PortForwardOptions CustomTag string diff --git a/pkg/skaffold/config/util.go b/pkg/skaffold/config/util.go index 17d8c7b494c..6803727dde3 100644 --- a/pkg/skaffold/config/util.go +++ b/pkg/skaffold/config/util.go @@ -171,7 +171,7 @@ func GetDefaultRepo(configFile string, cliValue *string) (string, error) { return cfg.DefaultRepo, nil } -func GetLocalCluster(configFile string, minikubeProfile string) (bool, error) { +func GetLocalCluster(configFile string, minikubeProfile string, detectMinikubeCluster bool) (bool, error) { if minikubeProfile != "" { return true, nil } @@ -189,7 +189,7 @@ func GetLocalCluster(configFile string, minikubeProfile string) (bool, error) { if err != nil { return true, err } - return isDefaultLocal(config.CurrentContext), nil + return isDefaultLocal(config.CurrentContext, detectMinikubeCluster), nil } func GetInsecureRegistries(configFile string) ([]string, error) { @@ -216,12 +216,18 @@ func GetDebugHelpersRegistry(configFile string) (string, error) { return constants.DefaultDebugHelpersRegistry, nil } -func isDefaultLocal(kubeContext string) bool { - return kubeContext == constants.DefaultDockerForDesktopContext || +func isDefaultLocal(kubeContext string, detectMinikubeCluster bool) bool { + if kubeContext == constants.DefaultMinikubeContext || + kubeContext == constants.DefaultDockerForDesktopContext || kubeContext == constants.DefaultDockerDesktopContext || IsKindCluster(kubeContext) || - IsK3dCluster(kubeContext) || - cluster.GetClient().IsMinikube(kubeContext) + IsK3dCluster(kubeContext) { + return true + } + if detectMinikubeCluster { + return cluster.GetClient().IsMinikube(kubeContext) + } + return false } // IsImageLoadingRequired checks if the cluster requires loading images into it diff --git a/pkg/skaffold/config/util_test.go b/pkg/skaffold/config/util_test.go index 0f3ec3c175f..4cd86c45eb4 100644 --- a/pkg/skaffold/config/util_test.go +++ b/pkg/skaffold/config/util_test.go @@ -285,8 +285,9 @@ func TestIsDefaultLocal(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { t.Override(&cluster.GetClient, func() cluster.Client { return fakeClient{} }) - local := isDefaultLocal(test.context) - + local := isDefaultLocal(test.context, true) + t.CheckDeepEqual(test.expectedLocal, local) + local = isDefaultLocal(test.context, false) t.CheckDeepEqual(test.expectedLocal, local) }) } diff --git a/pkg/skaffold/runner/runcontext/context.go b/pkg/skaffold/runner/runcontext/context.go index dc2243ef0b6..4214728f566 100644 --- a/pkg/skaffold/runner/runcontext/context.go +++ b/pkg/skaffold/runner/runcontext/context.go @@ -67,6 +67,7 @@ func (rc *RunContext) GetKubeConfig() string { return rc.Opt func (rc *RunContext) GetKubeNamespace() string { return rc.Opts.Namespace } func (rc *RunContext) GlobalConfig() string { return rc.Opts.GlobalConfig } func (rc *RunContext) MinikubeProfile() string { return rc.Opts.MinikubeProfile } +func (rc *RunContext) DetectMinikube() bool { return rc.Opts.DetectMinikube } func (rc *RunContext) Muted() config.Muted { return rc.Opts.Muted } func (rc *RunContext) NoPruneChildren() bool { return rc.Opts.NoPruneChildren } func (rc *RunContext) Notification() bool { return rc.Opts.Notification } From 374326bbd21ab08501b84178acd9c20feae10f9c Mon Sep 17 00:00:00 2001 From: David Gageot Date: Tue, 1 Sep 2020 05:21:53 +0200 Subject: [PATCH 127/138] Use the newer notation for integration tests (#4744) Signed-off-by: David Gageot --- integration/dev_test.go | 4 +--- integration/render_test.go | 12 +++--------- integration/run_test.go | 4 +--- integration/sync_test.go | 4 +--- integration/util.go | 10 ++++------ 5 files changed, 10 insertions(+), 24 deletions(-) diff --git a/integration/dev_test.go b/integration/dev_test.go index 2679c129cbf..9a22d02cb6d 100644 --- a/integration/dev_test.go +++ b/integration/dev_test.go @@ -133,9 +133,7 @@ func TestDevAPITriggers(t *testing.T) { } func TestDevAPIAutoTriggers(t *testing.T) { - if testing.Short() || RunOnGCP() { - t.Skip("skipping kind integration test") - } + MarkIntegrationTest(t, CanRunWithoutGcp) Run(t, "testdata/dev", "sh", "-c", "echo foo > foo") defer Run(t, "testdata/dev", "rm", "foo") diff --git a/integration/render_test.go b/integration/render_test.go index 30288a3de5a..29a2767d29f 100644 --- a/integration/render_test.go +++ b/integration/render_test.go @@ -37,9 +37,7 @@ import ( ) func TestKubectlRenderOutput(t *testing.T) { - if testing.Short() || RunOnGCP() { - t.Skip("skipping kind integration test") - } + MarkIntegrationTest(t, CanRunWithoutGcp) test := struct { description string @@ -253,9 +251,7 @@ spec: } func TestHelmRender(t *testing.T) { - if testing.Short() || RunOnGCP() { - t.Skip("skipping kind integration test") - } + MarkIntegrationTest(t, CanRunWithoutGcp) tests := []struct { description string @@ -438,9 +434,7 @@ spec: } func TestRenderFromBuildOutput(t *testing.T) { - if testing.Short() || RunOnGCP() { - t.Skip("skipping kind integration test") - } + MarkIntegrationTest(t, CanRunWithoutGcp) tests := []struct { description string diff --git a/integration/run_test.go b/integration/run_test.go index 42693dfab08..5965ca53aa0 100644 --- a/integration/run_test.go +++ b/integration/run_test.go @@ -136,9 +136,7 @@ func TestRun(t *testing.T) { } func TestRunRenderOnly(t *testing.T) { - if testing.Short() || RunOnGCP() { - t.Skip("skipping kind integration test") - } + MarkIntegrationTest(t, CanRunWithoutGcp) testutil.Run(t, "write rendered manifest to provided filepath", func(tu *testutil.T) { tmpDir := tu.NewTempDir() diff --git a/integration/sync_test.go b/integration/sync_test.go index 98431346a85..707465641ed 100644 --- a/integration/sync_test.go +++ b/integration/sync_test.go @@ -197,9 +197,7 @@ func TestDevSyncAPITrigger(t *testing.T) { } func TestDevAutoSyncAPITrigger(t *testing.T) { - if testing.Short() || RunOnGCP() { - t.Skip("skipping kind integration test") - } + MarkIntegrationTest(t, CanRunWithoutGcp) ns, client := SetupNamespace(t) diff --git a/integration/util.go b/integration/util.go index 4e97bd0dafa..f8861d8ced8 100644 --- a/integration/util.go +++ b/integration/util.go @@ -54,11 +54,13 @@ func MarkIntegrationTest(t *testing.T, testType TestType) { t.Skip("skipping integration test") } - if testType == NeedsGcp && !RunOnGCP() { + runOnGCP := os.Getenv("GCP_ONLY") == "true" + + if testType == NeedsGcp && !runOnGCP { t.Skip("skipping GCP integration test") } - if testType == CanRunWithoutGcp && RunOnGCP() { + if testType == CanRunWithoutGcp && runOnGCP { t.Skip("skipping non-GCP integration test") } @@ -86,10 +88,6 @@ func matchesPartition(testName string) bool { return strconv.Itoa(partition) == getPartition() } -func RunOnGCP() bool { - return os.Getenv("GCP_ONLY") == "true" -} - func Run(t *testing.T, dir, command string, args ...string) { cmd := exec.Command(command, args...) cmd.Dir = dir From fcfe4d64874fdb432766ed9cacdd6f4f19d427cc Mon Sep 17 00:00:00 2001 From: Gaurav <39389231+gsquared94@users.noreply.github.com> Date: Tue, 1 Sep 2020 21:00:42 +0530 Subject: [PATCH 128/138] Minikube cluster detection followup (#4742) * Minikube cluster detection followup * remove profile name matching * fix trace messages * Update minikube.go --- pkg/skaffold/cluster/minikube.go | 92 ++++++++------------------- pkg/skaffold/cluster/minikube_test.go | 64 ++++++------------- 2 files changed, 49 insertions(+), 107 deletions(-) diff --git a/pkg/skaffold/cluster/minikube.go b/pkg/skaffold/cluster/minikube.go index 398b4a2eff8..c325dc77825 100644 --- a/pkg/skaffold/cluster/minikube.go +++ b/pkg/skaffold/cluster/minikube.go @@ -26,8 +26,6 @@ import ( "path/filepath" "github.com/sirupsen/logrus" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/rest" "k8s.io/client-go/util/homedir" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" @@ -39,14 +37,8 @@ var GetClient = getClient // To override during tests var ( - minikubeBinaryFunc = minikubeBinary - getRestClientConfigFunc = context.GetRestClientConfig - getClusterInfo = context.GetClusterInfo -) - -const ( - VirtualBox = "virtualbox" - HyperKit = "hyperkit" + minikubeBinaryFunc = minikubeBinary + getClusterInfo = context.GetClusterInfo ) type Client interface { @@ -63,26 +55,29 @@ func getClient() Client { } func (clientImpl) IsMinikube(kubeContext string) bool { + if _, err := minikubeBinaryFunc(); err != nil { + logrus.Tracef("Minikube cluster not detected: %v", err) + return false + } // short circuit if context is 'minikube' if kubeContext == constants.DefaultMinikubeContext { return true } - if _, err := minikubeBinaryFunc(); err != nil { - logrus.Tracef("Minikube cluster not detected: %v", err) + + cluster, err := getClusterInfo(kubeContext) + if err != nil { + logrus.Tracef("failed to get cluster info: %v", err) return false } - - if ok, err := matchClusterCertPath(kubeContext); err != nil { - logrus.Tracef("failed to match cluster certificate path: %v", err) - } else if ok { + if matchClusterCertPath(cluster.CertificateAuthority) { logrus.Debugf("Minikube cluster detected: cluster certificate for context %q found inside the minikube directory", kubeContext) return true } - if ok, err := matchProfileAndServerURL(kubeContext); err != nil { - logrus.Tracef("failed to match minikube profile: %v", err) + if ok, err := matchServerURL(cluster.Server); err != nil { + logrus.Tracef("failed to match server url: %v", err) } else if ok { - logrus.Debugf("Minikube cluster detected: context %q has matching profile name or server url", kubeContext) + logrus.Debugf("Minikube cluster detected: server url for context %q matches minikube node ip", kubeContext) return true } logrus.Tracef("Minikube cluster not detected for context %q", kubeContext) @@ -117,63 +112,32 @@ func minikubeBinary() (string, error) { } // matchClusterCertPath checks if the cluster certificate for this context is from inside the minikube directory -func matchClusterCertPath(kubeContext string) (bool, error) { - c, err := getClusterInfo(kubeContext) - if err != nil { - return false, fmt.Errorf("getting kubernetes config: %w", err) - } - if c.CertificateAuthority == "" { - return false, nil - } - return util.IsSubPath(minikubePath(), c.CertificateAuthority), nil -} - -// matchProfileAndServerURL checks if kubecontext matches any valid minikube profile -// and for selected drivers if the k8s server url is same as any of the minikube nodes IPs -func matchProfileAndServerURL(kubeContext string) (bool, error) { - config, err := getRestClientConfigFunc() - if err != nil { - return false, fmt.Errorf("getting kubernetes config: %w", err) - } - apiServerURL, _, err := rest.DefaultServerURL(config.Host, config.APIPath, schema.GroupVersion{}, false) - - if err != nil { - return false, fmt.Errorf("getting kubernetes server url: %w", err) - } - - logrus.Tracef("kubernetes server url: %s", apiServerURL) - - ok, err := matchServerURLFor(kubeContext, apiServerURL) - if err != nil { - return false, fmt.Errorf("checking minikube node url: %w", err) - } - return ok, nil +func matchClusterCertPath(certPath string) bool { + return certPath != "" && util.IsSubPath(minikubePath(), certPath) } -func matchServerURLFor(kubeContext string, serverURL *url.URL) (bool, error) { +// matchServerURL checks if the k8s server url is same as any of the minikube nodes IPs +func matchServerURL(server string) (bool, error) { cmd, _ := minikubeExec("profile", "list", "-o", "json") out, err := util.RunCmdOut(cmd) if err != nil { return false, fmt.Errorf("getting minikube profiles: %w", err) } - var data data + var data profileList if err = json.Unmarshal(out, &data); err != nil { - return false, fmt.Errorf("failed to unmarshal data: %w", err) + return false, fmt.Errorf("failed to unmarshal minikube profile list: %w", err) } - for _, v := range data.Valid { - if v.Config.Name != kubeContext { - continue - } + serverURL, err := url.Parse(server) + if err != nil { + logrus.Tracef("invalid server url: %v", err) + } - if v.Config.Driver != HyperKit && v.Config.Driver != VirtualBox { - // Since node IPs don't match server API for other drivers we assume profile name match is enough. - // TODO: Revisit once https://github.com/kubernetes/minikube/issues/6642 is fixed - return true, nil - } + for _, v := range data.Valid { for _, n := range v.Config.Nodes { - if serverURL.Host == fmt.Sprintf("%s:%d", n.IP, n.Port) { + if err == nil && serverURL.Host == fmt.Sprintf("%s:%d", n.IP, n.Port) { + // TODO: Revisit once https://github.com/kubernetes/minikube/issues/6642 is fixed return true, nil } } @@ -193,7 +157,7 @@ func minikubePath() string { return filepath.Join(minikubeHomeEnv, ".minikube") } -type data struct { +type profileList struct { Valid []profile `json:"valid,omitempty"` Invalid []profile `json:"invalid,omitempty"` } diff --git a/pkg/skaffold/cluster/minikube_test.go b/pkg/skaffold/cluster/minikube_test.go index effa2a918d1..b77008d674f 100644 --- a/pkg/skaffold/cluster/minikube_test.go +++ b/pkg/skaffold/cluster/minikube_test.go @@ -21,7 +21,6 @@ import ( "path/filepath" "testing" - "k8s.io/client-go/rest" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "k8s.io/client-go/util/homedir" @@ -34,8 +33,8 @@ func TestClientImpl_IsMinikube(t *testing.T) { tests := []struct { description string kubeContext string - clusterInfo clientcmdapi.Cluster - config rest.Config + certPath string + serverURL string minikubeProfileCmd util.Command minikubeNotInPath bool expected bool @@ -54,58 +53,33 @@ func TestClientImpl_IsMinikube(t *testing.T) { { description: "cluster cert inside minikube dir", kubeContext: "test-cluster", - clusterInfo: clientcmdapi.Cluster{ - CertificateAuthority: filepath.Join(home, ".minikube", "ca.crt"), - }, - expected: true, - }, - { - description: "cluster cert outside minikube dir", - kubeContext: "test-cluster", - clusterInfo: clientcmdapi.Cluster{ - CertificateAuthority: filepath.Join(home, "foo", "ca.crt"), - }, - expected: false, + certPath: filepath.Join(home, ".minikube", "ca.crt"), + expected: true, }, { - description: "minikube profile name with docker driver matches kubeContext", - kubeContext: "test-cluster", - config: rest.Config{ - Host: "127.0.0.1:32768", - }, + description: "cluster cert outside minikube dir", + kubeContext: "test-cluster", minikubeProfileCmd: testutil.CmdRunOut("minikube profile list -o json", fmt.Sprintf(profileStr, "test-cluster", "docker", "172.17.0.3", 8443)), - expected: true, + certPath: filepath.Join(home, "foo", "ca.crt"), + expected: false, }, { - description: "minikube profile name with hyperkit driver node ip matches api server url", - kubeContext: "test-cluster", - config: rest.Config{ - Host: "192.168.64.10:8443", - }, + description: "minikube node ip matches api server url", + kubeContext: "test-cluster", + serverURL: "https://192.168.64.10:8443", minikubeProfileCmd: testutil.CmdRunOut("minikube profile list -o json", fmt.Sprintf(profileStr, "test-cluster", "hyperkit", "192.168.64.10", 8443)), expected: true, }, - { - description: "minikube profile name different from kubeContext", - kubeContext: "test-cluster", - config: rest.Config{ - Host: "127.0.0.1:32768", - }, - minikubeProfileCmd: testutil.CmdRunOut("minikube profile list -o json", fmt.Sprintf(profileStr, "test-cluster2", "docker", "172.17.0.3", 8443)), - expected: false, - }, { description: "cannot parse minikube profile list", kubeContext: "test-cluster", - minikubeProfileCmd: testutil.CmdRunOut("minikube profile list -o json", `{"foo":"bar"}`), + minikubeProfileCmd: testutil.CmdRunOut("minikube profile list -o json", `Random error`), expected: false, }, { - description: "minikube with hyperkit driver node ip different from api server url", - kubeContext: "test-cluster", - config: rest.Config{ - Host: "192.168.64.10:8443", - }, + description: "minikube node ip different from api server url", + kubeContext: "test-cluster", + serverURL: "https://192.168.64.10:8443", minikubeProfileCmd: testutil.CmdRunOut("minikube profile list -o json", fmt.Sprintf(profileStr, "test-cluster", "hyperkit", "192.168.64.11", 8443)), expected: false, }, @@ -119,8 +93,12 @@ func TestClientImpl_IsMinikube(t *testing.T) { t.Override(&minikubeBinaryFunc, func() (string, error) { return "minikube", nil }) } t.Override(&util.DefaultExecCommand, test.minikubeProfileCmd) - t.Override(&getRestClientConfigFunc, func() (*rest.Config, error) { return &test.config, nil }) - t.Override(&getClusterInfo, func(string) (*clientcmdapi.Cluster, error) { return &test.clusterInfo, nil }) + t.Override(&getClusterInfo, func(string) (*clientcmdapi.Cluster, error) { + return &clientcmdapi.Cluster{ + Server: test.serverURL, + CertificateAuthority: test.certPath, + }, nil + }) ok := GetClient().IsMinikube(test.kubeContext) t.CheckDeepEqual(test.expected, ok) From 76c154752e29ec56e61ac7e43ccd8f36ff0a8965 Mon Sep 17 00:00:00 2001 From: Gaurav <39389231+gsquared94@users.noreply.github.com> Date: Wed, 2 Sep 2020 00:48:37 +0530 Subject: [PATCH 129/138] Fix 4748: Panic with skaffold dev (#4750) * Fix uninitialized trigger. Add testcase. * Fix uninitialized trigger. Add testcase. --- pkg/skaffold/trigger/triggers.go | 7 +++- pkg/skaffold/trigger/triggers_test.go | 54 ++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/pkg/skaffold/trigger/triggers.go b/pkg/skaffold/trigger/triggers.go index 707120a9d01..f937e71205c 100644 --- a/pkg/skaffold/trigger/triggers.go +++ b/pkg/skaffold/trigger/triggers.go @@ -75,6 +75,7 @@ func newFSNotifyTrigger(cfg Config, isActive func() bool) *fsNotifyTrigger { Interval: time.Duration(cfg.WatchPollInterval()) * time.Millisecond, workspaces: workspaces, isActive: isActive, + watchFunc: notify.Watch, } } @@ -179,6 +180,7 @@ type fsNotifyTrigger struct { Interval time.Duration workspaces map[string]struct{} isActive func() bool + watchFunc func(path string, c chan<- notify.EventInfo, events ...notify.Event) error } // Debounce tells the watcher to not debounce rapid sequence of changes. @@ -206,7 +208,7 @@ func (t *fsNotifyTrigger) Start(ctx context.Context) (<-chan bool, error) { } // Watch current directory recursively - if err := notify.Watch(filepath.Join(wd, "..."), c, notify.All); err != nil { + if err := t.watchFunc(filepath.Join(wd, "..."), c, notify.All); err != nil { return nil, err } @@ -216,7 +218,7 @@ func (t *fsNotifyTrigger) Start(ctx context.Context) (<-chan bool, error) { continue } - if err := notify.Watch(filepath.Join(wd, w, "..."), c, notify.All); err != nil { + if err := t.watchFunc(filepath.Join(wd, w, "..."), c, notify.All); err != nil { return nil, err } } @@ -267,6 +269,7 @@ func StartTrigger(ctx context.Context, t Trigger) (<-chan bool, error) { t = &pollTrigger{ Interval: notifyTrigger.Interval, + isActive: notifyTrigger.isActive, } ret, err = t.Start(ctx) } diff --git a/pkg/skaffold/trigger/triggers_test.go b/pkg/skaffold/trigger/triggers_test.go index 8fc6ff275e1..7cb3e8751ff 100644 --- a/pkg/skaffold/trigger/triggers_test.go +++ b/pkg/skaffold/trigger/triggers_test.go @@ -18,10 +18,13 @@ package trigger import ( "bytes" + "context" + "fmt" "testing" "time" "github.com/google/go-cmp/cmp" + "github.com/rjeczalik/notify" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" @@ -52,6 +55,7 @@ func TestNewTrigger(t *testing.T) { "../workspace": {}, "../some/other/workspace": {}, }, + watchFunc: notify.Watch, }, }, { @@ -87,12 +91,22 @@ func TestNewTrigger(t *testing.T) { got, err := NewTrigger(runCtx, nil) t.CheckError(test.shouldErr, err) if !test.shouldErr { - t.CheckDeepEqual(test.expected, got, cmp.AllowUnexported(fsNotifyTrigger{}), cmp.AllowUnexported(manualTrigger{}), cmp.AllowUnexported(pollTrigger{})) + t.CheckDeepEqual(test.expected, got, cmp.AllowUnexported(fsNotifyTrigger{}), cmp.Comparer(ignoreFuncComparer), cmp.AllowUnexported(manualTrigger{}), cmp.AllowUnexported(pollTrigger{})) } }) } } +func ignoreFuncComparer(x, y func(path string, c chan<- notify.EventInfo, events ...notify.Event) error) bool { + if x == nil && y == nil { + return true + } + if x == nil || y == nil { + return false + } + return true // cannot assert function equality, so skip +} + func TestPollTrigger_Debounce(t *testing.T) { trigger := &pollTrigger{} got, want := trigger.Debounce(), true @@ -208,3 +222,41 @@ func TestManualTrigger_LogWatchToUser(t *testing.T) { testutil.CheckDeepEqual(t, want, got) } } + +func TestStartTrigger(t *testing.T) { + tests := []struct { + description string + trigger Trigger + }{ + { + description: "fsNotify trigger works", + trigger: &fsNotifyTrigger{ + Interval: 200 * time.Millisecond, + workspaces: nil, + isActive: func() bool { return false }, + watchFunc: func(string, chan<- notify.EventInfo, ...notify.Event) error { + return nil + }, + }, + }, + { + description: "fallback on polling trigger", + trigger: &fsNotifyTrigger{ + Interval: 200 * time.Millisecond, + workspaces: nil, + isActive: func() bool { return false }, + watchFunc: func(string, chan<- notify.EventInfo, ...notify.Event) error { + return fmt.Errorf("failed to start watch trigger") + }, + }, + }, + } + + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + _, err := StartTrigger(context.Background(), test.trigger) + time.Sleep(1 * time.Second) + t.CheckNoError(err) + }) + } +} From 3b51e31ebd25e17b9b343abfece9a31366a3fbba Mon Sep 17 00:00:00 2001 From: David Gageot Date: Wed, 2 Sep 2020 17:50:13 +0200 Subject: [PATCH 130/138] Leverage Config interfaces to simplify tests (#4754) Signed-off-by: David Gageot --- pkg/skaffold/build/cache/retrieve_test.go | 31 +- pkg/skaffold/build/cluster/cluster_test.go | 40 +-- pkg/skaffold/build/cluster/secret_test.go | 59 ++-- pkg/skaffold/build/cluster/types_test.go | 62 ++-- pkg/skaffold/build/gcb/buildpacks_test.go | 6 +- pkg/skaffold/build/gcb/docker_test.go | 18 +- pkg/skaffold/build/gcb/jib_test.go | 12 +- pkg/skaffold/build/gcb/kaniko_test.go | 12 +- pkg/skaffold/build/gcb/spec_test.go | 25 +- pkg/skaffold/build/local/docker_test.go | 4 +- pkg/skaffold/build/local/local_test.go | 28 +- pkg/skaffold/deploy/helm_test.go | 281 ++++++++++-------- pkg/skaffold/deploy/kpt_test.go | 158 +++++----- pkg/skaffold/deploy/kubectl_test.go | 257 ++++++---------- pkg/skaffold/deploy/kustomize_test.go | 114 +++---- .../deploy/resource/deployment_test.go | 12 +- pkg/skaffold/deploy/status_check_test.go | 13 +- pkg/skaffold/diagnose/diagnose_test.go | 34 ++- pkg/skaffold/kubectl/cli_test.go | 43 +-- .../runner/runcontext/context_test.go | 81 ++--- pkg/skaffold/test/test_test.go | 155 +++++----- pkg/skaffold/trigger/triggers_test.go | 63 ++-- 22 files changed, 722 insertions(+), 786 deletions(-) diff --git a/pkg/skaffold/build/cache/retrieve_test.go b/pkg/skaffold/build/cache/retrieve_test.go index dab43d6fd3b..173713f8c46 100644 --- a/pkg/skaffold/build/cache/retrieve_test.go +++ b/pkg/skaffold/build/cache/retrieve_test.go @@ -96,12 +96,6 @@ func TestCacheBuildLocal(t *testing.T) { Write("dep3", "content3"). Chdir() - runCtx := &runcontext.RunContext{ - Opts: config.SkaffoldOptions{ - CacheArtifacts: true, - CacheFile: tmpDir.Path("cache"), - }, - } tags := map[string]string{ "artifact1": "artifact1:tag1", "artifact2": "artifact2:tag2", @@ -128,7 +122,10 @@ func TestCacheBuildLocal(t *testing.T) { }) // Create cache - artifactCache, err := NewCache(runCtx, true, deps) + cfg := &mockConfig{ + cacheFile: tmpDir.Path("cache"), + } + artifactCache, err := NewCache(cfg, true, deps) t.CheckNoError(err) // First build: Need to build both artifacts @@ -184,12 +181,6 @@ func TestCacheBuildRemote(t *testing.T) { Write("dep3", "content3"). Chdir() - runCtx := &runcontext.RunContext{ - Opts: config.SkaffoldOptions{ - CacheArtifacts: true, - CacheFile: tmpDir.Path("cache"), - }, - } tags := map[string]string{ "artifact1": "artifact1:tag1", "artifact2": "artifact2:tag2", @@ -224,8 +215,12 @@ func TestCacheBuildRemote(t *testing.T) { t.Override(&docker.EvalBuildArgs, func(mode config.RunMode, workspace string, a *latest.DockerArtifact) (map[string]*string, error) { return a.BuildArgs, nil }) + // Create cache - artifactCache, err := NewCache(runCtx, false, deps) + cfg := &mockConfig{ + cacheFile: tmpDir.Path("cache"), + } + artifactCache, err := NewCache(cfg, false, deps) t.CheckNoError(err) // First build: Need to build both artifacts @@ -261,3 +256,11 @@ func TestCacheBuildRemote(t *testing.T) { t.CheckDeepEqual("artifact2", bRes[1].ImageName) }) } + +type mockConfig struct { + runcontext.RunContext // Embedded to provide the default values. + cacheFile string +} + +func (c *mockConfig) CacheArtifacts() bool { return true } +func (c *mockConfig) CacheFile() string { return c.cacheFile } diff --git a/pkg/skaffold/build/cluster/cluster_test.go b/pkg/skaffold/build/cluster/cluster_test.go index c34e0c76ff9..998ea209ba8 100644 --- a/pkg/skaffold/build/cluster/cluster_test.go +++ b/pkg/skaffold/build/cluster/cluster_test.go @@ -19,31 +19,21 @@ package cluster import ( "testing" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/testutil" ) func TestRetrieveEnv(t *testing.T) { - builder, err := NewBuilder(&runcontext.RunContext{ - KubeContext: "kubecontext", - Opts: config.SkaffoldOptions{ - Namespace: "test-namespace", - }, - Cfg: latest.Pipeline{ - Build: latest.BuildConfig{ - BuildType: latest.BuildType{ - Cluster: &latest.ClusterDetails{ - Namespace: "namespace", - PullSecretName: "pullSecret", - DockerConfig: &latest.DockerConfig{ - SecretName: "dockerconfig", - }, - Timeout: "2m", - }, - }, + builder, err := NewBuilder(&mockConfig{ + kubeContext: "kubecontext", + namespace: "test-namespace", + cluster: latest.ClusterDetails{ + Namespace: "namespace", + PullSecretName: "pullSecret", + DockerConfig: &latest.DockerConfig{ + SecretName: "dockerconfig", }, + Timeout: "2m", }, }) testutil.CheckError(t, false, err) @@ -54,15 +44,9 @@ func TestRetrieveEnv(t *testing.T) { } func TestRetrieveEnvMinimal(t *testing.T) { - builder, err := NewBuilder(&runcontext.RunContext{ - Cfg: latest.Pipeline{ - Build: latest.BuildConfig{ - BuildType: latest.BuildType{ - Cluster: &latest.ClusterDetails{ - Timeout: "20m", - }, - }, - }, + builder, err := NewBuilder(&mockConfig{ + cluster: latest.ClusterDetails{ + Timeout: "20m", }, }) testutil.CheckError(t, false, err) diff --git a/pkg/skaffold/build/cluster/secret_test.go b/pkg/skaffold/build/cluster/secret_test.go index 8e5baac13a0..887375dee63 100644 --- a/pkg/skaffold/build/cluster/secret_test.go +++ b/pkg/skaffold/build/cluster/secret_test.go @@ -26,7 +26,6 @@ import ( "k8s.io/client-go/kubernetes/fake" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/testutil" ) @@ -39,18 +38,12 @@ func TestCreateSecret(t *testing.T) { return fakeKubernetesclient, nil }) - builder, err := NewBuilder(&runcontext.RunContext{ - Cfg: latest.Pipeline{ - Build: latest.BuildConfig{ - BuildType: latest.BuildType{ - Cluster: &latest.ClusterDetails{ - Timeout: "20m", - PullSecretName: "kaniko-secret", - PullSecretPath: tmpDir.Path("secret.json"), - Namespace: "ns", - }, - }, - }, + builder, err := NewBuilder(&mockConfig{ + cluster: latest.ClusterDetails{ + Timeout: "20m", + PullSecretName: "kaniko-secret", + PullSecretPath: tmpDir.Path("secret.json"), + Namespace: "ns", }, }) t.CheckNoError(err) @@ -78,16 +71,10 @@ func TestExistingSecretNotFound(t *testing.T) { return fake.NewSimpleClientset(), nil }) - builder, err := NewBuilder(&runcontext.RunContext{ - Cfg: latest.Pipeline{ - Build: latest.BuildConfig{ - BuildType: latest.BuildType{ - Cluster: &latest.ClusterDetails{ - Timeout: "20m", - PullSecretName: "kaniko-secret", - }, - }, - }, + builder, err := NewBuilder(&mockConfig{ + cluster: latest.ClusterDetails{ + Timeout: "20m", + PullSecretName: "kaniko-secret", }, }) t.CheckNoError(err) @@ -109,16 +96,10 @@ func TestExistingSecret(t *testing.T) { }), nil }) - builder, err := NewBuilder(&runcontext.RunContext{ - Cfg: latest.Pipeline{ - Build: latest.BuildConfig{ - BuildType: latest.BuildType{ - Cluster: &latest.ClusterDetails{ - Timeout: "20m", - PullSecretName: "kaniko-secret", - }, - }, - }, + builder, err := NewBuilder(&mockConfig{ + cluster: latest.ClusterDetails{ + Timeout: "20m", + PullSecretName: "kaniko-secret", }, }) t.CheckNoError(err) @@ -137,15 +118,9 @@ func TestSkipSecretCreation(t *testing.T) { return nil, nil }) - builder, err := NewBuilder(&runcontext.RunContext{ - Cfg: latest.Pipeline{ - Build: latest.BuildConfig{ - BuildType: latest.BuildType{ - Cluster: &latest.ClusterDetails{ - Timeout: "20m", - }, - }, - }, + builder, err := NewBuilder(&mockConfig{ + cluster: latest.ClusterDetails{ + Timeout: "20m", }, }) t.CheckNoError(err) diff --git a/pkg/skaffold/build/cluster/types_test.go b/pkg/skaffold/build/cluster/types_test.go index 64d9b74064e..a4cf7cadf8d 100644 --- a/pkg/skaffold/build/cluster/types_test.go +++ b/pkg/skaffold/build/cluster/types_test.go @@ -40,22 +40,28 @@ func TestNewBuilder(t *testing.T) { tests := []struct { description string shouldErr bool - runCtx Config + cfg Config expectedBuilder *Builder }{ { description: "failed to parse cluster build timeout", - runCtx: stubRunContext(&latest.ClusterDetails{ - Timeout: "illegal", - }, nil), + cfg: &mockConfig{ + cluster: latest.ClusterDetails{ + Timeout: "illegal", + }, + }, shouldErr: true, }, { description: "cluster builder inherits the config", - runCtx: stubRunContext(&latest.ClusterDetails{ - Timeout: "100s", - Namespace: "test-ns", - }, nil), + cfg: &mockConfig{ + kubeContext: kubeContext, + namespace: namespace, + cluster: latest.ClusterDetails{ + Timeout: "100s", + Namespace: "test-ns", + }, + }, shouldErr: false, expectedBuilder: &Builder{ ClusterDetails: &latest.ClusterDetails{ @@ -74,10 +80,15 @@ func TestNewBuilder(t *testing.T) { }, { description: "insecure registries are taken from the run context", - runCtx: stubRunContext(&latest.ClusterDetails{ - Timeout: "100s", - Namespace: "test-ns", - }, map[string]bool{"insecure-reg1": true}), + cfg: &mockConfig{ + kubeContext: kubeContext, + namespace: namespace, + insecureRegistries: map[string]bool{"insecure-reg1": true}, + cluster: latest.ClusterDetails{ + Timeout: "100s", + Namespace: "test-ns", + }, + }, shouldErr: false, expectedBuilder: &Builder{ ClusterDetails: &latest.ClusterDetails{ @@ -97,7 +108,7 @@ func TestNewBuilder(t *testing.T) { } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - builder, err := NewBuilder(test.runCtx) + builder, err := NewBuilder(test.cfg) t.CheckError(test.shouldErr, err) if !test.shouldErr { @@ -112,16 +123,19 @@ func TestPruneIsNoop(t *testing.T) { testutil.CheckDeepEqual(t, nil, pruneError) } -func stubRunContext(clusterDetails *latest.ClusterDetails, insecureRegistries map[string]bool) *runcontext.RunContext { - pipeline := latest.Pipeline{} - pipeline.Build.BuildType.Cluster = clusterDetails +type mockConfig struct { + runcontext.RunContext // Embedded to provide the default values. + kubeContext string + namespace string + insecureRegistries map[string]bool + cluster latest.ClusterDetails +} - return &runcontext.RunContext{ - Cfg: pipeline, - InsecureRegistries: insecureRegistries, - KubeContext: kubeContext, - Opts: config.SkaffoldOptions{ - Namespace: namespace, - }, - } +func (c *mockConfig) GetKubeContext() string { return c.kubeContext } +func (c *mockConfig) GetKubeNamespace() string { return c.namespace } +func (c *mockConfig) GetInsecureRegistries() map[string]bool { return c.insecureRegistries } +func (c *mockConfig) Pipeline() latest.Pipeline { + var pipeline latest.Pipeline + pipeline.Build.BuildType.Cluster = &c.cluster + return pipeline } diff --git a/pkg/skaffold/build/gcb/buildpacks_test.go b/pkg/skaffold/build/gcb/buildpacks_test.go index 3c7958ee80f..9dace274814 100644 --- a/pkg/skaffold/build/gcb/buildpacks_test.go +++ b/pkg/skaffold/build/gcb/buildpacks_test.go @@ -140,8 +140,10 @@ func TestBuildpackBuildSpec(t *testing.T) { }, } - builder := newBuilder(latest.GoogleCloudBuild{ - PackImage: "pack/image", + builder := NewBuilder(&mockConfig{ + gcb: latest.GoogleCloudBuild{ + PackImage: "pack/image", + }, }) buildSpec, err := builder.buildSpec(artifact, "img", "bucket", "object") t.CheckError(test.shouldErr, err) diff --git a/pkg/skaffold/build/gcb/docker_test.go b/pkg/skaffold/build/gcb/docker_test.go index 639c7bb0fba..81819bc0e3c 100644 --- a/pkg/skaffold/build/gcb/docker_test.go +++ b/pkg/skaffold/build/gcb/docker_test.go @@ -39,11 +39,13 @@ func TestDockerBuildSpec(t *testing.T) { }, } - builder := newBuilder(latest.GoogleCloudBuild{ - DockerImage: "docker/docker", - DiskSizeGb: 100, - MachineType: "n1-standard-1", - Timeout: "10m", + builder := NewBuilder(&mockConfig{ + gcb: latest.GoogleCloudBuild{ + DockerImage: "docker/docker", + DiskSizeGb: 100, + MachineType: "n1-standard-1", + Timeout: "10m", + }, }) desc, err := builder.buildSpec(artifact, "nginx", "bucket", "object") @@ -76,8 +78,10 @@ func TestPullCacheFrom(t *testing.T) { CacheFrom: []string{"from/image1", "from/image2"}, } - builder := newBuilder(latest.GoogleCloudBuild{ - DockerImage: "docker/docker", + builder := NewBuilder(&mockConfig{ + gcb: latest.GoogleCloudBuild{ + DockerImage: "docker/docker", + }, }) desc, err := builder.dockerBuildSpec(artifact, "nginx2") diff --git a/pkg/skaffold/build/gcb/jib_test.go b/pkg/skaffold/build/gcb/jib_test.go index 029fadcbc5e..9ce8d631b5f 100644 --- a/pkg/skaffold/build/gcb/jib_test.go +++ b/pkg/skaffold/build/gcb/jib_test.go @@ -52,8 +52,10 @@ func TestJibMavenBuildSpec(t *testing.T) { }, } - builder := newBuilder(latest.GoogleCloudBuild{ - MavenImage: "maven:3.6.0", + builder := NewBuilder(&mockConfig{ + gcb: latest.GoogleCloudBuild{ + MavenImage: "maven:3.6.0", + }, }) builder.skipTests = test.skipTests @@ -97,8 +99,10 @@ func TestJibGradleBuildSpec(t *testing.T) { }, } - builder := newBuilder(latest.GoogleCloudBuild{ - GradleImage: "gradle:5.1.1", + builder := NewBuilder(&mockConfig{ + gcb: latest.GoogleCloudBuild{ + GradleImage: "gradle:5.1.1", + }, }) builder.skipTests = test.skipTests diff --git a/pkg/skaffold/build/gcb/kaniko_test.go b/pkg/skaffold/build/gcb/kaniko_test.go index 7768d782adb..c844e88e19f 100644 --- a/pkg/skaffold/build/gcb/kaniko_test.go +++ b/pkg/skaffold/build/gcb/kaniko_test.go @@ -85,11 +85,13 @@ func TestKanikoBuildSpec(t *testing.T) { }, } - builder := newBuilder(latest.GoogleCloudBuild{ - KanikoImage: "gcr.io/kaniko-project/executor", - DiskSizeGb: 100, - MachineType: "n1-standard-1", - Timeout: "10m", + builder := NewBuilder(&mockConfig{ + gcb: latest.GoogleCloudBuild{ + KanikoImage: "gcr.io/kaniko-project/executor", + DiskSizeGb: 100, + MachineType: "n1-standard-1", + Timeout: "10m", + }, }) defaultExpectedArgs := []string{ diff --git a/pkg/skaffold/build/gcb/spec_test.go b/pkg/skaffold/build/gcb/spec_test.go index 0dcff702aa0..845269b1d04 100644 --- a/pkg/skaffold/build/gcb/spec_test.go +++ b/pkg/skaffold/build/gcb/spec_test.go @@ -19,7 +19,6 @@ package gcb import ( "testing" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/testutil" @@ -45,7 +44,9 @@ func TestBuildSpecFail(t *testing.T) { } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - builder := newBuilder(latest.GoogleCloudBuild{}) + builder := NewBuilder(&mockConfig{ + gcb: latest.GoogleCloudBuild{}, + }) _, err := builder.buildSpec(test.artifact, "tag", "bucket", "object") @@ -54,15 +55,13 @@ func TestBuildSpecFail(t *testing.T) { } } -func newBuilder(gcb latest.GoogleCloudBuild) *Builder { - return NewBuilder(&runcontext.RunContext{ - Opts: config.SkaffoldOptions{}, - Cfg: latest.Pipeline{ - Build: latest.BuildConfig{ - BuildType: latest.BuildType{ - GoogleCloudBuild: &gcb, - }, - }, - }, - }) +type mockConfig struct { + runcontext.RunContext // Embedded to provide the default values. + gcb latest.GoogleCloudBuild +} + +func (c *mockConfig) Pipeline() latest.Pipeline { + var pipeline latest.Pipeline + pipeline.Build.BuildType.GoogleCloudBuild = &c.gcb + return pipeline } diff --git a/pkg/skaffold/build/local/docker_test.go b/pkg/skaffold/build/local/docker_test.go index 5636796392b..9fb1389d94d 100644 --- a/pkg/skaffold/build/local/docker_test.go +++ b/pkg/skaffold/build/local/docker_test.go @@ -89,7 +89,9 @@ func TestDockerCLIBuild(t *testing.T) { return docker.NewLocalDaemon(&testutil.FakeAPIClient{}, test.extraEnv, false, nil), nil }) - builder, err := NewBuilder(stubRunContext(test.localBuild)) + builder, err := NewBuilder(&mockConfig{ + local: test.localBuild, + }) t.CheckNoError(err) artifact := &latest.Artifact{ diff --git a/pkg/skaffold/build/local/local_test.go b/pkg/skaffold/build/local/local_test.go index 91e65bb5e06..ffaa2f3c1b8 100644 --- a/pkg/skaffold/build/local/local_test.go +++ b/pkg/skaffold/build/local/local_test.go @@ -246,10 +246,12 @@ func TestLocalRun(t *testing.T) { }, }}, "", true, true, true) - builder, err := NewBuilder(stubRunContext(latest.LocalBuild{ - Push: util.BoolPtr(test.pushImages), - Concurrency: &constants.DefaultLocalConcurrency, - })) + builder, err := NewBuilder(&mockConfig{ + local: latest.LocalBuild{ + Push: util.BoolPtr(test.pushImages), + Concurrency: &constants.DefaultLocalConcurrency, + }, + }) t.CheckNoError(err) res, err := builder.Build(context.Background(), ioutil.Discard, test.tags, test.artifacts) @@ -345,7 +347,9 @@ func TestNewBuilder(t *testing.T) { t.Override(&getLocalCluster, test.localClusterFn) } - builder, err := NewBuilder(stubRunContext(test.localBuild)) + builder, err := NewBuilder(&mockConfig{ + local: test.localBuild, + }) t.CheckError(test.shouldErr, err) if !test.shouldErr { @@ -355,11 +359,13 @@ func TestNewBuilder(t *testing.T) { } } -func stubRunContext(localBuild latest.LocalBuild) *runcontext.RunContext { - pipeline := latest.Pipeline{} - pipeline.Build.BuildType.LocalBuild = &localBuild +type mockConfig struct { + runcontext.RunContext // Embedded to provide the default values. + local latest.LocalBuild +} - return &runcontext.RunContext{ - Cfg: pipeline, - } +func (c *mockConfig) Pipeline() latest.Pipeline { + var pipeline latest.Pipeline + pipeline.Build.BuildType.LocalBuild = &c.local + return pipeline } diff --git a/pkg/skaffold/deploy/helm_test.go b/pkg/skaffold/deploy/helm_test.go index 5ff29e6b382..b98130a9e92 100644 --- a/pkg/skaffold/deploy/helm_test.go +++ b/pkg/skaffold/deploy/helm_test.go @@ -24,7 +24,6 @@ import ( "testing" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" schemautil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util" @@ -386,7 +385,9 @@ func TestBinVer(t *testing.T) { testutil.Run(t, test.description, func(t *testutil.T) { t.Override(&util.DefaultExecCommand, testutil.CmdRunWithOutput("helm version --client", test.helmVersion)) - deployer := NewHelmDeployer(makeRunContext(testDeployConfig, false), nil) + deployer := NewHelmDeployer(&helmConfig{ + helm: testDeployConfig, + }, nil) ver, err := deployer.binVer(context.TODO()) t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expected, ver.String()) @@ -403,8 +404,10 @@ func TestHelmDeploy(t *testing.T) { tests := []struct { description string commands util.Command - runContext Config + helm latest.HelmDeploy + namespace string builds []build.Artifact + force bool shouldErr bool expectedWarnings []string }{ @@ -416,8 +419,8 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployConfig, false), - builds: testBuilds, + helm: testDeployConfig, + builds: testBuilds, }, { description: "helm3.0beta deploy success", @@ -427,8 +430,8 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get all skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployConfig, false), - builds: testBuilds, + helm: testDeployConfig, + builds: testBuilds, }, { description: "helm3.0beta namespaced context deploy success", @@ -438,8 +441,9 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test --namespace testNamespace -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get all --namespace testNamespace skaffold-helm --kubeconfig kubeconfig"), - runContext: makeNamespacedRunContext(testDeployConfig), - builds: testBuilds, + helm: testDeployConfig, + namespace: testNamespace, + builds: testBuilds, }, { description: "helm3.0 deploy success", @@ -449,8 +453,8 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get all skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployConfig, false), - builds: testBuilds, + helm: testDeployConfig, + builds: testBuilds, }, { description: "helm3.0 namespaced deploy success", @@ -460,8 +464,8 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test --namespace testReleaseNamespace -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get all --namespace testReleaseNamespace skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployNamespacedConfig, false), - builds: testBuilds, + helm: testDeployNamespacedConfig, + builds: testBuilds, }, { description: "helm3.0 namespaced context deploy success", @@ -471,8 +475,9 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test --namespace testNamespace -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get all --namespace testNamespace skaffold-helm --kubeconfig kubeconfig"), - runContext: makeNamespacedRunContext(testDeployConfig), - builds: testBuilds, + helm: testDeployConfig, + namespace: testNamespace, + builds: testBuilds, }, { description: "helm3.0 namespaced context deploy success overrides release namespaces", @@ -482,8 +487,9 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test --namespace testNamespace -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get all --namespace testNamespace skaffold-helm --kubeconfig kubeconfig"), - runContext: makeNamespacedRunContext(testDeployNamespacedConfig), - builds: testBuilds, + helm: testDeployNamespacedConfig, + namespace: testNamespace, + builds: testBuilds, }, { description: "helm3.1 deploy success", @@ -493,8 +499,8 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get all skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployConfig, false), - builds: testBuilds, + helm: testDeployConfig, + builds: testBuilds, }, { description: "helm3.1 namespaced deploy success", @@ -504,8 +510,8 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test --namespace testReleaseNamespace -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get all --namespace testReleaseNamespace skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployNamespacedConfig, false), - builds: testBuilds, + helm: testDeployNamespacedConfig, + builds: testBuilds, }, { description: "helm3.1 namespaced context deploy success", @@ -515,8 +521,9 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test --namespace testNamespace -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get all --namespace testNamespace skaffold-helm --kubeconfig kubeconfig"), - runContext: makeNamespacedRunContext(testDeployConfig), - builds: testBuilds, + helm: testDeployConfig, + namespace: testNamespace, + builds: testBuilds, }, { description: "helm3.1 namespaced context deploy success overrides release namespaces", @@ -526,13 +533,14 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test --namespace testNamespace -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get all --namespace testNamespace skaffold-helm --kubeconfig kubeconfig"), - runContext: makeNamespacedRunContext(testDeployNamespacedConfig), - builds: testBuilds, + helm: testDeployNamespacedConfig, + namespace: testNamespace, + builds: testBuilds, }, { description: "helm3 unparseable version", commands: testutil.CmdRunWithOutput("helm version --client", "gobbledygook"), - runContext: makeRunContext(testDeployConfig, false), + helm: testDeployConfig, builds: testBuilds, shouldErr: true, }, @@ -544,8 +552,8 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm --recreate-pods examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployRecreatePodsConfig, false), - builds: testBuilds, + helm: testDeployRecreatePodsConfig, + builds: testBuilds, }, { description: "deploy success with skipBuildDependencies", @@ -554,8 +562,8 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeploySkipBuildDependenciesConfig, false), - builds: testBuilds, + helm: testDeploySkipBuildDependenciesConfig, + builds: testBuilds, }, { description: "deploy should error for unmatched parameter", @@ -565,9 +573,9 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployConfigParameterUnmatched, false), - builds: testBuilds, - shouldErr: true, + helm: testDeployConfigParameterUnmatched, + builds: testBuilds, + shouldErr: true, }, { description: "deploy success remote chart with skipBuildDependencies", @@ -576,15 +584,15 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm stable/chartmuseum --set-string image.tag=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeploySkipBuildDependencies, false), - builds: testBuilds, + helm: testDeploySkipBuildDependencies, + builds: testBuilds, }, { description: "deploy success when `upgradeOnChange: false` and does not upgrade", commands: testutil. CmdRunWithOutput("helm version --client", version21). AndRun("helm --kube-context kubecontext get skaffold-helm-upgradeOnChange --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployUpgradeOnChange, false), + helm: testDeployUpgradeOnChange, }, { description: "deploy error remote chart without skipBuildDependencies", @@ -592,9 +600,9 @@ func TestHelmDeploy(t *testing.T) { CmdRunWithOutput("helm version --client", version21). AndRun("helm --kube-context kubecontext get skaffold-helm-remote --kubeconfig kubeconfig"). AndRunErr("helm --kube-context kubecontext dep build stable/chartmuseum --kubeconfig kubeconfig", fmt.Errorf("building helm dependencies")), - runContext: makeRunContext(testDeployRemoteChart, false), - builds: testBuilds, - shouldErr: true, + helm: testDeployRemoteChart, + builds: testBuilds, + shouldErr: true, }, { description: "get failure should install not upgrade", @@ -604,8 +612,8 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext install --name skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployConfig, false), - builds: testBuilds, + helm: testDeployConfig, + builds: testBuilds, }, { description: "helm3 get failure should install not upgrade", @@ -615,8 +623,8 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext install skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get all skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployConfig, false), - builds: testBuilds, + helm: testDeployConfig, + builds: testBuilds, }, { description: "get failure should install not upgrade with helm image strategy", @@ -626,8 +634,8 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext install --name skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image.repository=docker.io:5000/skaffold-helm,image.tag=3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployHelmStyleConfig, false), - builds: testBuilds, + helm: testDeployHelmStyleConfig, + builds: testBuilds, }, { description: "helm image strategy with explicit registry should set the Helm registry value", @@ -637,8 +645,8 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext install --name skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image.registry=docker.io:5000,image.repository=skaffold-helm,image.tag=3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployHelmExplicitRegistryStyleConfig, false), - builds: testBuilds, + helm: testDeployHelmExplicitRegistryStyleConfig, + builds: testBuilds, }, { description: "get success should upgrade by force, not install", @@ -648,8 +656,9 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm --force examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployConfig, true), - builds: testBuilds, + helm: testDeployConfig, + force: true, + builds: testBuilds, }, { description: "get success should upgrade without force, not install", @@ -659,8 +668,8 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployConfig, false), - builds: testBuilds, + helm: testDeployConfig, + builds: testBuilds, }, { description: "deploy error", @@ -670,9 +679,9 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRunErr("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig", fmt.Errorf("unexpected error")). AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"), - shouldErr: true, - runContext: makeRunContext(testDeployConfig, false), - builds: testBuilds, + shouldErr: true, + helm: testDeployConfig, + builds: testBuilds, }, { description: "dep build error", @@ -682,9 +691,9 @@ func TestHelmDeploy(t *testing.T) { AndRunErr("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig", fmt.Errorf("unexpected error")). AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"), - shouldErr: true, - runContext: makeRunContext(testDeployConfig, false), - builds: testBuilds, + shouldErr: true, + helm: testDeployConfig, + builds: testBuilds, }, { description: "helm 2.0 should package chart and deploy", @@ -695,9 +704,9 @@ func TestHelmDeploy(t *testing.T) { AndRunWithOutput("helm --kube-context kubecontext package testdata/foo --destination "+tmpDir+" --version 0.1.2 --app-version 1.2.3 --kubeconfig kubeconfig", fmt.Sprintf("Packaged to %s", filepath.Join(tmpDir, "foo-0.1.2.tgz"))). AndRun("helm --kube-context kubecontext upgrade foo " + filepath.Join(tmpDir, "foo-0.1.2.tgz") + " --set-string image=foo:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get foo --kubeconfig kubeconfig"), - shouldErr: false, - runContext: makeRunContext(testDeployFooWithPackaged, false), - builds: testBuildsFoo, + shouldErr: false, + helm: testDeployFooWithPackaged, + builds: testBuildsFoo, }, { description: "helm 3.0 beta should package chart and deploy", @@ -708,9 +717,9 @@ func TestHelmDeploy(t *testing.T) { AndRunWithOutput("helm --kube-context kubecontext package testdata/foo --destination "+tmpDir+" --version 0.1.2 --app-version 1.2.3 --kubeconfig kubeconfig", fmt.Sprintf("Packaged to %s", filepath.Join(tmpDir, "foo-0.1.2.tgz"))). AndRun("helm --kube-context kubecontext upgrade foo " + filepath.Join(tmpDir, "foo-0.1.2.tgz") + " --set-string image=foo:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get all foo --kubeconfig kubeconfig"), - shouldErr: false, - runContext: makeRunContext(testDeployFooWithPackaged, false), - builds: testBuildsFoo, + shouldErr: false, + helm: testDeployFooWithPackaged, + builds: testBuildsFoo, }, { description: "helm 3.1 should package chart and deploy", @@ -721,9 +730,9 @@ func TestHelmDeploy(t *testing.T) { AndRunWithOutput("helm --kube-context kubecontext package testdata/foo --destination "+tmpDir+" --version 0.1.2 --app-version 1.2.3 --kubeconfig kubeconfig", fmt.Sprintf("Packaged to %s", filepath.Join(tmpDir, "foo-0.1.2.tgz"))). AndRun("helm --kube-context kubecontext upgrade foo " + filepath.Join(tmpDir, "foo-0.1.2.tgz") + " --set-string image=foo:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get all foo --kubeconfig kubeconfig"), - shouldErr: false, - runContext: makeRunContext(testDeployFooWithPackaged, false), - builds: testBuildsFoo, + shouldErr: false, + helm: testDeployFooWithPackaged, + builds: testBuildsFoo, }, { description: "should fail to deploy when packaging fails", @@ -732,9 +741,9 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext get foo --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext dep build testdata/foo --kubeconfig kubeconfig"). AndRunErr("helm --kube-context kubecontext package testdata/foo --destination "+tmpDir+" --version 0.1.2 --app-version 1.2.3 --kubeconfig kubeconfig", fmt.Errorf("packaging failed")), - shouldErr: true, - runContext: makeRunContext(testDeployFooWithPackaged, false), - builds: testBuildsFoo, + shouldErr: true, + helm: testDeployFooWithPackaged, + builds: testBuildsFoo, }, { description: "deploy and get templated release name", @@ -744,8 +753,8 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade -skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image.tag=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get -skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployWithTemplatedName, false), - builds: testBuilds, + helm: testDeployWithTemplatedName, + builds: testBuilds, }, { description: "deploy with templated values", @@ -755,8 +764,8 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set image.name=skaffold-helm --set image.tag=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set missing.key= --set other.key=FOOBAR --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployConfigTemplated, false), - builds: testBuilds, + helm: testDeployConfigTemplated, + builds: testBuilds, }, { description: "deploy with valuesFiles templated", @@ -766,8 +775,8 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 -f /some/file-FOOBAR.yaml --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployConfigValuesFilesTemplated, false), - builds: testBuilds, + helm: testDeployConfigValuesFilesTemplated, + builds: testBuilds, }, { description: "deploy with setFiles", @@ -777,8 +786,8 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set-file value=/some/file.yaml --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployConfigSetFiles, false), - builds: testBuilds, + helm: testDeployConfigSetFiles, + builds: testBuilds, }, { description: "deploy without actual tags", @@ -788,8 +797,8 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployWithoutTags, false), - builds: testBuilds, + helm: testDeployWithoutTags, + builds: testBuilds, expectedWarnings: []string{ "See helm sample for how to replace image names with their actual tags: https://github.com/GoogleContainerTools/skaffold/blob/master/examples/helm-deployment/skaffold.yaml", "image [docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184] is not used.", @@ -808,8 +817,8 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext dep build --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext upgrade skaffold-helm --set-string image.tag=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testTwoReleases, false), - builds: testBuilds, + helm: testTwoReleases, + builds: testBuilds, }, } for _, test := range tests { @@ -819,7 +828,11 @@ func TestHelmDeploy(t *testing.T) { t.Override(&util.OSEnviron, func() []string { return []string{"FOO=FOOBAR"} }) t.Override(&util.DefaultExecCommand, test.commands) - deployer := NewHelmDeployer(test.runContext, nil) + deployer := NewHelmDeployer(&helmConfig{ + helm: test.helm, + namespace: test.namespace, + force: test.force, + }, nil) deployer.pkgTmpDir = tmpDir _, err := deployer.Deploy(context.Background(), ioutil.Discard, test.builds) @@ -833,7 +846,8 @@ func TestHelmCleanup(t *testing.T) { tests := []struct { description string commands util.Command - runContext Config + helm latest.HelmDeploy + namespace string builds []build.Artifact shouldErr bool expectedWarnings []string @@ -843,40 +857,42 @@ func TestHelmCleanup(t *testing.T) { commands: testutil. CmdRunWithOutput("helm version --client", version20rc). AndRun("helm --kube-context kubecontext delete skaffold-helm --purge --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployConfig, false), - builds: testBuilds, + helm: testDeployConfig, + builds: testBuilds, }, { description: "helm3 cleanup success", commands: testutil. CmdRunWithOutput("helm version --client", version31). AndRun("helm --kube-context kubecontext delete skaffold-helm --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployConfig, false), - builds: testBuilds, + helm: testDeployConfig, + builds: testBuilds, }, { description: "helm3 namespace cleanup success", commands: testutil. CmdRunWithOutput("helm version --client", version31). AndRun("helm --kube-context kubecontext delete skaffold-helm --namespace testReleaseNamespace --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployNamespacedConfig, false), - builds: testBuilds, + helm: testDeployNamespacedConfig, + builds: testBuilds, }, { description: "helm3 namespaced context cleanup success", commands: testutil. CmdRunWithOutput("helm version --client", version31). AndRun("helm --kube-context kubecontext delete skaffold-helm --namespace testNamespace --kubeconfig kubeconfig"), - runContext: makeNamespacedRunContext(testDeployConfig), - builds: testBuilds, + helm: testDeployConfig, + namespace: testNamespace, + builds: testBuilds, }, { description: "helm3 namespaced context cleanup success overriding release namespace", commands: testutil. CmdRunWithOutput("helm version --client", version31). AndRun("helm --kube-context kubecontext delete skaffold-helm --namespace testNamespace --kubeconfig kubeconfig"), - runContext: makeNamespacedRunContext(testDeployNamespacedConfig), - builds: testBuilds, + helm: testDeployNamespacedConfig, + namespace: testNamespace, + builds: testBuilds, }, } for _, test := range tests { @@ -886,8 +902,12 @@ func TestHelmCleanup(t *testing.T) { t.Override(&util.OSEnviron, func() []string { return []string{"FOO=FOOBAR"} }) t.Override(&util.DefaultExecCommand, test.commands) - deployer := NewHelmDeployer(test.runContext, nil) + deployer := NewHelmDeployer(&helmConfig{ + helm: test.helm, + namespace: test.namespace, + }, nil) err := deployer.Cleanup(context.Background(), ioutil.Discard) + t.CheckError(test.shouldErr, err) t.CheckDeepEqual(test.expectedWarnings, fakeWarner.Warnings) }) @@ -984,18 +1004,19 @@ func TestHelmDependencies(t *testing.T) { tmpDir := t.NewTempDir(). Touch(test.files...) - deployer := NewHelmDeployer(makeRunContext(latest.HelmDeploy{ - Releases: []latest.HelmRelease{{ - Name: "skaffold-helm", - ChartPath: tmpDir.Root(), - ValuesFiles: test.valuesFiles, - ArtifactOverrides: map[string]string{"image": "skaffold-helm"}, - Overrides: schemautil.HelmOverrides{Values: map[string]interface{}{"foo": "bar"}}, - SetValues: map[string]string{"some.key": "somevalue"}, - SkipBuildDependencies: test.skipBuildDependencies, - Remote: test.remote, - }}, - }, false), nil) + deployer := NewHelmDeployer(&helmConfig{ + helm: latest.HelmDeploy{ + Releases: []latest.HelmRelease{{ + Name: "skaffold-helm", + ChartPath: tmpDir.Root(), + ValuesFiles: test.valuesFiles, + ArtifactOverrides: map[string]string{"image": "skaffold-helm"}, + Overrides: schemautil.HelmOverrides{Values: map[string]interface{}{"foo": "bar"}}, + SetValues: map[string]string{"some.key": "somevalue"}, + SkipBuildDependencies: test.skipBuildDependencies, + Remote: test.remote, + }}, + }}, nil) deps, err := deployer.Dependencies() @@ -1081,7 +1102,7 @@ func TestHelmRender(t *testing.T) { description string shouldErr bool commands util.Command - runContext Config + helm latest.HelmDeploy outputFile string expected string builds []build.Artifact @@ -1090,7 +1111,7 @@ func TestHelmRender(t *testing.T) { description: "error if version can't be retrieved", shouldErr: true, commands: testutil.CmdRunErr("helm version --client", fmt.Errorf("yep not working")), - runContext: makeRunContext(testDeployConfig, false), + helm: testDeployConfig, }, { description: "normal render v2", @@ -1098,7 +1119,7 @@ func TestHelmRender(t *testing.T) { commands: testutil. CmdRunWithOutput("helm version --client", version21). AndRun("helm --kube-context kubecontext template examples/test --name skaffold-helm --set-string image=skaffold-helm:tag1 --set some.key=somevalue --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployConfig, false), + helm: testDeployConfig, builds: []build.Artifact{ { ImageName: "skaffold-helm", @@ -1111,7 +1132,7 @@ func TestHelmRender(t *testing.T) { commands: testutil. CmdRunWithOutput("helm version --client", version31). AndRun("helm --kube-context kubecontext template skaffold-helm examples/test --set-string image=skaffold-helm:tag1 --set some.key=somevalue --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployConfig, false), + helm: testDeployConfig, builds: []build.Artifact{ { ImageName: "skaffold-helm", @@ -1125,7 +1146,7 @@ func TestHelmRender(t *testing.T) { CmdRunWithOutput("helm version --client", version31). AndRunWithOutput("helm --kube-context kubecontext template skaffold-helm examples/test --set-string image=skaffold-helm:tag1 --set some.key=somevalue --kubeconfig kubeconfig", "Dummy Output"), - runContext: makeRunContext(testDeployConfig, false), + helm: testDeployConfig, outputFile: "dummy.yaml", expected: "Dummy Output\n", builds: []build.Artifact{ @@ -1140,7 +1161,7 @@ func TestHelmRender(t *testing.T) { commands: testutil. CmdRunWithOutput("helm version --client", version31). AndRun("helm --kube-context kubecontext template skaffold-helm examples/test --set-string image=skaffold-helm:tag1 --set image.name=skaffold-helm --set image.tag=skaffold-helm:tag1 --set missing.key= --set other.key= --set some.key=somevalue --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployConfigTemplated, false), + helm: testDeployConfigTemplated, builds: []build.Artifact{ { ImageName: "skaffold-helm", @@ -1152,7 +1173,7 @@ func TestHelmRender(t *testing.T) { shouldErr: false, commands: testutil.CmdRunWithOutput("helm version --client", version31). AndRun("helm --kube-context kubecontext template skaffold-helm examples/test --set-string image=skaffold-helm:tag1 --set some.key=somevalue --namespace testReleaseNamespace --kubeconfig kubeconfig"), - runContext: makeRunContext(testDeployNamespacedConfig, false), + helm: testDeployNamespacedConfig, builds: []build.Artifact{ { ImageName: "skaffold-helm", @@ -1167,7 +1188,9 @@ func TestHelmRender(t *testing.T) { file = t.NewTempDir().Path(test.outputFile) } - deployer := NewHelmDeployer(test.runContext, nil) + deployer := NewHelmDeployer(&helmConfig{ + helm: test.helm, + }, nil) t.Override(&util.DefaultExecCommand, test.commands) err := deployer.Render(context.Background(), ioutil.Discard, test.builds, true, file) @@ -1181,23 +1204,19 @@ func TestHelmRender(t *testing.T) { } } -func makeRunContext(deploy latest.HelmDeploy, force bool) *runcontext.RunContext { - pipeline := latest.Pipeline{} - pipeline.Deploy.DeployType.HelmDeploy = &deploy - - return &runcontext.RunContext{ - Cfg: pipeline, - KubeContext: testKubeContext, - Opts: config.SkaffoldOptions{ - KubeConfig: testKubeConfig, - Force: force, - }, - } +type helmConfig struct { + runcontext.RunContext // Embedded to provide the default values. + namespace string + force bool + helm latest.HelmDeploy } -func makeNamespacedRunContext(deploy latest.HelmDeploy) *runcontext.RunContext { - runContext := makeRunContext(deploy, false) - runContext.Opts.Namespace = testNamespace - - return runContext +func (c *helmConfig) ForceDeploy() bool { return c.force } +func (c *helmConfig) GetKubeConfig() string { return testKubeConfig } +func (c *helmConfig) GetKubeContext() string { return testKubeContext } +func (c *helmConfig) GetKubeNamespace() string { return c.namespace } +func (c *helmConfig) Pipeline() latest.Pipeline { + var pipeline latest.Pipeline + pipeline.Deploy.DeployType.HelmDeploy = &c.helm + return pipeline } diff --git a/pkg/skaffold/deploy/kpt_test.go b/pkg/skaffold/deploy/kpt_test.go index c35bae8b5b0..6aa3cfb0c0b 100644 --- a/pkg/skaffold/deploy/kpt_test.go +++ b/pkg/skaffold/deploy/kpt_test.go @@ -28,7 +28,6 @@ import ( "testing" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" @@ -48,7 +47,7 @@ spec: tests := []struct { description string builds []build.Artifact - cfg *latest.KptDeploy + kpt latest.KptDeploy kustomizations map[string]string commands util.Command expected []string @@ -56,7 +55,7 @@ spec: }{ { description: "no manifest", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", }, commands: testutil. @@ -66,7 +65,7 @@ spec: }, { description: "invalid manifest", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", }, commands: testutil. @@ -77,7 +76,7 @@ spec: }, { description: "invalid user specified applyDir", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", ApplyDir: "invalid_path", }, @@ -89,7 +88,7 @@ spec: }, { description: "kustomization and specified kpt fn", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", Fn: latest.KptFn{FnPath: "kpt-func.yaml"}, ApplyDir: "valid_path", @@ -106,7 +105,7 @@ spec: }, { description: "kpt live apply fails", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", }, commands: testutil. @@ -119,7 +118,7 @@ spec: }, { description: "user specifies reconcile timeout and poll period", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", ApplyDir: "valid_path", Live: latest.KptLive{ @@ -137,7 +136,7 @@ spec: }, { description: "user specifies invalid reconcile timeout and poll period", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", ApplyDir: "valid_path", Live: latest.KptLive{ @@ -155,7 +154,7 @@ spec: }, { description: "user specifies prune propagation policy and prune timeout", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", ApplyDir: "valid_path", Live: latest.KptLive{ @@ -173,7 +172,7 @@ spec: }, { description: "user specifies invalid prune propagation policy and prune timeout", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", ApplyDir: "valid_path", Live: latest.KptLive{ @@ -197,14 +196,8 @@ spec: tmpDir.WriteFiles(test.kustomizations) - k := NewKptDeployer(&runcontext.RunContext{ - Cfg: latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - KptDeploy: test.cfg, - }, - }, - }, + k := NewKptDeployer(&kptConfig{ + kpt: test.kpt, }, nil) if k.ApplyDir == "valid_path" { @@ -223,7 +216,7 @@ spec: func TestKpt_Dependencies(t *testing.T) { tests := []struct { description string - cfg *latest.KptDeploy + kpt latest.KptDeploy createFiles map[string]string kustomizations map[string]string expected []string @@ -231,20 +224,20 @@ func TestKpt_Dependencies(t *testing.T) { }{ { description: "bad dir", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: "invalid_path", }, shouldErr: true, }, { description: "empty dir and unspecified fnPath", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", }, }, { description: "dir", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", }, createFiles: map[string]string{ @@ -255,7 +248,7 @@ func TestKpt_Dependencies(t *testing.T) { }, { description: "dir with subdirs and file path variants", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", }, createFiles: map[string]string{ @@ -268,7 +261,7 @@ func TestKpt_Dependencies(t *testing.T) { }, { description: "fnpath", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", Fn: latest.KptFn{FnPath: "kpt-func.yaml"}, }, @@ -276,7 +269,7 @@ func TestKpt_Dependencies(t *testing.T) { }, { description: "fnpath and dir and kustomization", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", Fn: latest.KptFn{FnPath: "kpt-func.yaml"}, }, @@ -287,7 +280,7 @@ func TestKpt_Dependencies(t *testing.T) { }, { description: "dependencies that can only be detected as a kustomization", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", }, kustomizations: map[string]string{"kustomization.yaml": `configMapGenerator: @@ -296,7 +289,7 @@ func TestKpt_Dependencies(t *testing.T) { }, { description: "kustomization.yml variant", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", }, kustomizations: map[string]string{"kustomization.yml": `configMapGenerator: @@ -305,7 +298,7 @@ func TestKpt_Dependencies(t *testing.T) { }, { description: "Kustomization variant", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", }, kustomizations: map[string]string{"Kustomization": `configMapGenerator: @@ -314,7 +307,7 @@ func TestKpt_Dependencies(t *testing.T) { }, { description: "incorrectly named kustomization", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", }, kustomizations: map[string]string{"customization": `configMapGenerator: @@ -328,14 +321,8 @@ func TestKpt_Dependencies(t *testing.T) { tmpDir.WriteFiles(test.createFiles) tmpDir.WriteFiles(test.kustomizations) - k := NewKptDeployer(&runcontext.RunContext{ - Cfg: latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - KptDeploy: test.cfg, - }, - }, - }, + k := NewKptDeployer(&kptConfig{ + kpt: test.kpt, }, nil) res, err := k.Dependencies() @@ -387,20 +374,10 @@ func TestKpt_Cleanup(t *testing.T) { os.Mkdir(test.applyDir, 0755) } - k := NewKptDeployer(&runcontext.RunContext{ - WorkingDir: ".", - Cfg: latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - KptDeploy: &latest.KptDeploy{ - ApplyDir: test.applyDir, - }, - }, - }, - }, - KubeContext: testKubeContext, - Opts: config.SkaffoldOptions{ - Namespace: testNamespace, + k := NewKptDeployer(&kptConfig{ + workingDir: ".", + kpt: latest.KptDeploy{ + ApplyDir: test.applyDir, }, }, nil) @@ -458,7 +435,7 @@ spec: description string builds []build.Artifact labels map[string]string - cfg *latest.KptDeploy + kpt latest.KptDeploy commands util.Command kustomizations map[string]string expected string @@ -472,7 +449,7 @@ spec: Tag: "gcr.io/project/image1:tag1", }, }, - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", }, commands: testutil. @@ -502,7 +479,7 @@ spec: }, }, labels: map[string]string{"user/label": "test"}, - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: "test", Fn: latest.KptFn{FnPath: "kpt-func.yaml"}, }, @@ -545,7 +522,7 @@ spec: Tag: "gcr.io/project/image2:tag2", }, }, - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", Fn: latest.KptFn{Image: "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar"}, }, @@ -574,7 +551,7 @@ spec: }, }, labels: map[string]string{"user/label": "test"}, - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", }, commands: testutil. @@ -585,7 +562,7 @@ spec: }, { description: "both fnPath and image specified", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", Fn: latest.KptFn{ FnPath: "kpt-func.yaml", @@ -604,7 +581,7 @@ spec: Tag: "gcr.io/project/image1:tag1", }, }, - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", }, commands: testutil. @@ -626,7 +603,7 @@ spec: }, { description: "reading configs from sourceDir fails", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", }, commands: testutil. @@ -637,7 +614,7 @@ spec: }, { description: "outputting configs to sinkDir fails", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", }, commands: testutil. @@ -654,7 +631,7 @@ spec: Tag: "gcr.io/project/image1:tag1", }, }, - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", }, commands: testutil. @@ -668,7 +645,7 @@ spec: }, { description: "kpt fn run fails", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", }, commands: testutil. @@ -679,7 +656,7 @@ spec: }, { description: "kpt fn run with --global-scope", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", Fn: latest.KptFn{ Image: "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", @@ -694,7 +671,7 @@ spec: }, { description: "kpt fn run with --mount arguments", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", Fn: latest.KptFn{ Image: "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", @@ -709,7 +686,7 @@ spec: }, { description: "kpt fn run with invalid --mount arguments", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", Fn: latest.KptFn{ Image: "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", @@ -724,7 +701,7 @@ spec: }, { description: "kpt fn run flag with --network and --network-name arguments", - cfg: &latest.KptDeploy{ + kpt: latest.KptDeploy{ Dir: ".", Fn: latest.KptFn{ Image: "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", @@ -746,19 +723,9 @@ spec: tmpDir.WriteFiles(test.kustomizations) - k := NewKptDeployer(&runcontext.RunContext{ - WorkingDir: ".", - Cfg: latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - KptDeploy: test.cfg, - }, - }, - }, - KubeContext: testKubeContext, - Opts: config.SkaffoldOptions{ - Namespace: testNamespace, - }, + k := NewKptDeployer(&kptConfig{ + workingDir: ".", + kpt: test.kpt, }, test.labels) var b bytes.Buffer @@ -822,17 +789,11 @@ func TestKpt_GetApplyDir(t *testing.T) { tmpDir.Touch(".kpt-hydrated/inventory-template.yaml") } - k := NewKptDeployer(&runcontext.RunContext{ - WorkingDir: ".", - Cfg: latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - KptDeploy: &latest.KptDeploy{ - ApplyDir: test.applyDir, - Live: test.live, - }, - }, - }, + k := NewKptDeployer(&kptConfig{ + workingDir: ".", + kpt: latest.KptDeploy{ + ApplyDir: test.applyDir, + Live: test.live, }, }, nil) @@ -899,3 +860,18 @@ func TestKpt_KptCommandArgs(t *testing.T) { }) } } + +type kptConfig struct { + runcontext.RunContext // Embedded to provide the default values. + workingDir string + kpt latest.KptDeploy +} + +func (c *kptConfig) WorkingDir() string { return c.workingDir } +func (c *kptConfig) GetKubeContext() string { return testKubeContext } +func (c *kptConfig) GetKubeNamespace() string { return testNamespace } +func (c *kptConfig) Pipeline() latest.Pipeline { + var pipeline latest.Pipeline + pipeline.Deploy.DeployType.KptDeploy = &c.kpt + return pipeline +} diff --git a/pkg/skaffold/deploy/kubectl_test.go b/pkg/skaffold/deploy/kubectl_test.go index 2f4dd5396f5..393c57f55f7 100644 --- a/pkg/skaffold/deploy/kubectl_test.go +++ b/pkg/skaffold/deploy/kubectl_test.go @@ -90,7 +90,7 @@ spec: func TestKubectlDeploy(t *testing.T) { tests := []struct { description string - cfg *latest.KubectlDeploy + kubectl latest.KubectlDeploy builds []build.Artifact commands util.Command shouldErr bool @@ -99,13 +99,13 @@ func TestKubectlDeploy(t *testing.T) { }{ { description: "no manifest", - cfg: &latest.KubectlDeploy{}, + kubectl: latest.KubectlDeploy{}, commands: testutil.CmdRunOut("kubectl version --client -ojson", kubectlVersion112), waitForDeletions: true, }, { description: "deploy success (disable validation)", - cfg: &latest.KubectlDeploy{ + kubectl: latest.KubectlDeploy{ Manifests: []string{"deployment.yaml"}, Flags: latest.KubectlFlags{ DisableValidation: true, @@ -124,7 +124,7 @@ func TestKubectlDeploy(t *testing.T) { }, { description: "deploy success (forced)", - cfg: &latest.KubectlDeploy{ + kubectl: latest.KubectlDeploy{ Manifests: []string{"deployment.yaml"}, }, commands: testutil. @@ -141,7 +141,7 @@ func TestKubectlDeploy(t *testing.T) { }, { description: "deploy success", - cfg: &latest.KubectlDeploy{ + kubectl: latest.KubectlDeploy{ Manifests: []string{"deployment.yaml"}, }, commands: testutil. @@ -157,7 +157,7 @@ func TestKubectlDeploy(t *testing.T) { }, { description: "deploy success (kubectl v1.18)", - cfg: &latest.KubectlDeploy{ + kubectl: latest.KubectlDeploy{ Manifests: []string{"deployment.yaml"}, }, commands: testutil. @@ -173,7 +173,7 @@ func TestKubectlDeploy(t *testing.T) { }, { description: "http manifest", - cfg: &latest.KubectlDeploy{ + kubectl: latest.KubectlDeploy{ Manifests: []string{"deployment.yaml", "http://remote.yaml"}, }, commands: testutil. @@ -187,7 +187,7 @@ func TestKubectlDeploy(t *testing.T) { }, { description: "deploy command error", - cfg: &latest.KubectlDeploy{ + kubectl: latest.KubectlDeploy{ Manifests: []string{"deployment.yaml"}, }, commands: testutil. @@ -202,7 +202,7 @@ func TestKubectlDeploy(t *testing.T) { }, { description: "additional flags", - cfg: &latest.KubectlDeploy{ + kubectl: latest.KubectlDeploy{ Manifests: []string{"deployment.yaml"}, Flags: latest.KubectlFlags{ Global: []string{"-v=0"}, @@ -231,24 +231,14 @@ func TestKubectlDeploy(t *testing.T) { Touch("empty.ignored"). Chdir() - k := NewKubectlDeployer(&runcontext.RunContext{ - WorkingDir: ".", - Cfg: latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - KubectlDeploy: test.cfg, - }, - }, - }, - KubeContext: testKubeContext, - Opts: config.SkaffoldOptions{ - Namespace: testNamespace, - Force: test.forceDeploy, - WaitForDeletions: config.WaitForDeletions{ - Enabled: test.waitForDeletions, - Delay: 0 * time.Second, - Max: 10 * time.Second, - }, + k := NewKubectlDeployer(&kubectlConfig{ + workingDir: ".", + kubectl: test.kubectl, + force: test.forceDeploy, + waitForDeletions: config.WaitForDeletions{ + Enabled: test.waitForDeletions, + Delay: 0 * time.Second, + Max: 10 * time.Second, }, }, nil) @@ -262,13 +252,13 @@ func TestKubectlDeploy(t *testing.T) { func TestKubectlCleanup(t *testing.T) { tests := []struct { description string - cfg *latest.KubectlDeploy + kubectl latest.KubectlDeploy commands util.Command shouldErr bool }{ { description: "cleanup success", - cfg: &latest.KubectlDeploy{ + kubectl: latest.KubectlDeploy{ Manifests: []string{"deployment.yaml"}, }, commands: testutil. @@ -278,7 +268,7 @@ func TestKubectlCleanup(t *testing.T) { }, { description: "cleanup success (kubectl v1.18)", - cfg: &latest.KubectlDeploy{ + kubectl: latest.KubectlDeploy{ Manifests: []string{"deployment.yaml"}, }, commands: testutil. @@ -288,7 +278,7 @@ func TestKubectlCleanup(t *testing.T) { }, { description: "cleanup error", - cfg: &latest.KubectlDeploy{ + kubectl: latest.KubectlDeploy{ Manifests: []string{"deployment.yaml"}, }, commands: testutil. @@ -299,7 +289,7 @@ func TestKubectlCleanup(t *testing.T) { }, { description: "additional flags", - cfg: &latest.KubectlDeploy{ + kubectl: latest.KubectlDeploy{ Manifests: []string{"deployment.yaml"}, Flags: latest.KubectlFlags{ Global: []string{"-v=0"}, @@ -320,19 +310,9 @@ func TestKubectlCleanup(t *testing.T) { Write("deployment.yaml", deploymentWebYAML). Chdir() - k := NewKubectlDeployer(&runcontext.RunContext{ - WorkingDir: ".", - Cfg: latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - KubectlDeploy: test.cfg, - }, - }, - }, - KubeContext: testKubeContext, - Opts: config.SkaffoldOptions{ - Namespace: testNamespace, - }, + k := NewKubectlDeployer(&kubectlConfig{ + workingDir: ".", + kubectl: test.kubectl, }, nil) err := k.Cleanup(context.Background(), ioutil.Discard) @@ -344,12 +324,12 @@ func TestKubectlCleanup(t *testing.T) { func TestKubectlDeployerRemoteCleanup(t *testing.T) { tests := []struct { description string - cfg *latest.KubectlDeploy + kubectl latest.KubectlDeploy commands util.Command }{ { description: "cleanup success", - cfg: &latest.KubectlDeploy{ + kubectl: latest.KubectlDeploy{ RemoteManifests: []string{"pod/leeroy-web"}, }, commands: testutil. @@ -359,7 +339,7 @@ func TestKubectlDeployerRemoteCleanup(t *testing.T) { }, { description: "cleanup error", - cfg: &latest.KubectlDeploy{ + kubectl: latest.KubectlDeploy{ RemoteManifests: []string{"anotherNamespace:pod/leeroy-web"}, }, commands: testutil. @@ -375,19 +355,9 @@ func TestKubectlDeployerRemoteCleanup(t *testing.T) { Write("deployment.yaml", deploymentWebYAML). Chdir() - k := NewKubectlDeployer(&runcontext.RunContext{ - WorkingDir: ".", - Cfg: latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - KubectlDeploy: test.cfg, - }, - }, - }, - KubeContext: testKubeContext, - Opts: config.SkaffoldOptions{ - Namespace: testNamespace, - }, + k := NewKubectlDeployer(&kubectlConfig{ + workingDir: ".", + kubectl: test.kubectl, }, nil) err := k.Cleanup(context.Background(), ioutil.Discard) @@ -414,26 +384,15 @@ func TestKubectlRedeploy(t *testing.T) { AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentAppYAMLv2+"\n---\n"+deploymentWebYAMLv1, ""), ) - cfg := &latest.KubectlDeploy{ - Manifests: []string{tmpDir.Path("deployment-app.yaml"), "deployment-web.yaml"}, - } - deployer := NewKubectlDeployer(&runcontext.RunContext{ - WorkingDir: tmpDir.Root(), - Cfg: latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - KubectlDeploy: cfg, - }, - }, + deployer := NewKubectlDeployer(&kubectlConfig{ + workingDir: tmpDir.Root(), + kubectl: latest.KubectlDeploy{ + Manifests: []string{tmpDir.Path("deployment-app.yaml"), tmpDir.Path("deployment-web.yaml")}, }, - KubeContext: testKubeContext, - Opts: config.SkaffoldOptions{ - Namespace: testNamespace, - WaitForDeletions: config.WaitForDeletions{ - Enabled: true, - Delay: 0 * time.Millisecond, - Max: 10 * time.Second, - }, + waitForDeletions: config.WaitForDeletions{ + Enabled: true, + Delay: 0 * time.Millisecond, + Max: 10 * time.Second, }, }, nil) @@ -491,26 +450,15 @@ func TestKubectlWaitForDeletions(t *testing.T) { AndRunInput("kubectl --context kubecontext --namespace testNamespace apply -f -", deploymentWebYAMLv1), ) - cfg := &latest.KubectlDeploy{ - Manifests: []string{tmpDir.Path("deployment-web.yaml")}, - } - deployer := NewKubectlDeployer(&runcontext.RunContext{ - WorkingDir: tmpDir.Root(), - Cfg: latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - KubectlDeploy: cfg, - }, - }, + deployer := NewKubectlDeployer(&kubectlConfig{ + workingDir: tmpDir.Root(), + kubectl: latest.KubectlDeploy{ + Manifests: []string{tmpDir.Path("deployment-web.yaml")}, }, - KubeContext: testKubeContext, - Opts: config.SkaffoldOptions{ - Namespace: testNamespace, - WaitForDeletions: config.WaitForDeletions{ - Enabled: true, - Delay: 0 * time.Millisecond, - Max: 10 * time.Second, - }, + waitForDeletions: config.WaitForDeletions{ + Enabled: true, + Delay: 0 * time.Millisecond, + Max: 10 * time.Second, }, }, nil) @@ -541,26 +489,15 @@ func TestKubectlWaitForDeletionsFails(t *testing.T) { }`), ) - cfg := &latest.KubectlDeploy{ - Manifests: []string{tmpDir.Path("deployment-web.yaml")}, - } - deployer := NewKubectlDeployer(&runcontext.RunContext{ - WorkingDir: tmpDir.Root(), - Cfg: latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - KubectlDeploy: cfg, - }, - }, + deployer := NewKubectlDeployer(&kubectlConfig{ + workingDir: tmpDir.Root(), + kubectl: latest.KubectlDeploy{ + Manifests: []string{tmpDir.Path("deployment-web.yaml")}, }, - KubeContext: testKubeContext, - Opts: config.SkaffoldOptions{ - Namespace: testNamespace, - WaitForDeletions: config.WaitForDeletions{ - Enabled: true, - Delay: 10 * time.Second, - Max: 100 * time.Millisecond, - }, + waitForDeletions: config.WaitForDeletions{ + Enabled: true, + Delay: 10 * time.Second, + Max: 100 * time.Millisecond, }, }, nil) @@ -622,15 +559,9 @@ func TestDependencies(t *testing.T) { Touch("00/b.yaml", "00/a.yaml"). Chdir() - k := NewKubectlDeployer(&runcontext.RunContext{ - Cfg: latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - KubectlDeploy: &latest.KubectlDeploy{ - Manifests: test.manifests, - }, - }, - }, + k := NewKubectlDeployer(&kubectlConfig{ + kubectl: latest.KubectlDeploy{ + Manifests: test.manifests, }, }, nil) dependencies, err := k.Dependencies() @@ -739,32 +670,21 @@ spec: } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - tmpDir := t.NewTempDir(). - Write("deployment.yaml", test.input) - + tmpDir := t.NewTempDir().Write("deployment.yaml", test.input) t.Override(&util.DefaultExecCommand, testutil. CmdRunOut("kubectl version --client -ojson", kubectlVersion112). AndRunOut("kubectl --context kubecontext create --dry-run -oyaml -f "+tmpDir.Path("deployment.yaml"), test.input)) - defaultRepo := config.StringOrUndefined{} - defaultRepo.Set("gcr.io/project") - deployer := NewKubectlDeployer(&runcontext.RunContext{ - WorkingDir: ".", - Cfg: latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - KubectlDeploy: &latest.KubectlDeploy{ - Manifests: []string{tmpDir.Path("deployment.yaml")}, - }, - }, - }, - }, - KubeContext: testKubeContext, - Opts: config.SkaffoldOptions{ - DefaultRepo: defaultRepo, + + deployer := NewKubectlDeployer(&kubectlConfig{ + workingDir: ".", + defaultRepo: "gcr.io/project", + kubectl: latest.KubectlDeploy{ + Manifests: []string{tmpDir.Path("deployment.yaml")}, }, }, nil) var b bytes.Buffer err := deployer.Render(context.Background(), &b, test.builds, true, "") + t.CheckNoError(err) t.CheckDeepEqual(test.expected, b.String()) }) @@ -774,14 +694,14 @@ spec: func TestGCSManifests(t *testing.T) { tests := []struct { description string - cfg *latest.KubectlDeploy + kubectl latest.KubectlDeploy commands util.Command shouldErr bool skipRender bool }{ { description: "manifest from GCS", - cfg: &latest.KubectlDeploy{ + kubectl: latest.KubectlDeploy{ Manifests: []string{"gs://dev/deployment.yaml"}, }, commands: testutil. @@ -801,20 +721,10 @@ func TestGCSManifests(t *testing.T) { t.Fatal(err) } - k := NewKubectlDeployer(&runcontext.RunContext{ - WorkingDir: ".", - Cfg: latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - KubectlDeploy: test.cfg, - }, - }, - }, - KubeContext: testKubeContext, - Opts: config.SkaffoldOptions{ - Namespace: testNamespace, - SkipRender: test.skipRender, - }, + k := NewKubectlDeployer(&kubectlConfig{ + workingDir: ".", + kubectl: test.kubectl, + skipRender: test.skipRender, }, nil) _, err := k.Deploy(context.Background(), ioutil.Discard, nil) @@ -823,3 +733,26 @@ func TestGCSManifests(t *testing.T) { }) } } + +type kubectlConfig struct { + runcontext.RunContext // Embedded to provide the default values. + workingDir string + defaultRepo string + skipRender bool + force bool + waitForDeletions config.WaitForDeletions + kubectl latest.KubectlDeploy +} + +func (c *kubectlConfig) GetKubeContext() string { return testKubeContext } +func (c *kubectlConfig) GetKubeNamespace() string { return testNamespace } +func (c *kubectlConfig) WorkingDir() string { return c.workingDir } +func (c *kubectlConfig) SkipRender() bool { return c.skipRender } +func (c *kubectlConfig) ForceDeploy() bool { return c.force } +func (c *kubectlConfig) DefaultRepo() *string { return &c.defaultRepo } +func (c *kubectlConfig) WaitForDeletions() config.WaitForDeletions { return c.waitForDeletions } +func (c *kubectlConfig) Pipeline() latest.Pipeline { + var pipeline latest.Pipeline + pipeline.Deploy.DeployType.KubectlDeploy = &c.kubectl + return pipeline +} diff --git a/pkg/skaffold/deploy/kustomize_test.go b/pkg/skaffold/deploy/kustomize_test.go index 600ad5db743..b40d8aa9f73 100644 --- a/pkg/skaffold/deploy/kustomize_test.go +++ b/pkg/skaffold/deploy/kustomize_test.go @@ -36,7 +36,7 @@ import ( func TestKustomizeDeploy(t *testing.T) { tests := []struct { description string - cfg *latest.KustomizeDeploy + kustomize latest.KustomizeDeploy builds []build.Artifact commands util.Command shouldErr bool @@ -44,7 +44,7 @@ func TestKustomizeDeploy(t *testing.T) { }{ { description: "no manifest", - cfg: &latest.KustomizeDeploy{ + kustomize: latest.KustomizeDeploy{ KustomizePaths: []string{"."}, }, commands: testutil. @@ -53,7 +53,7 @@ func TestKustomizeDeploy(t *testing.T) { }, { description: "deploy success", - cfg: &latest.KustomizeDeploy{ + kustomize: latest.KustomizeDeploy{ KustomizePaths: []string{"."}, }, commands: testutil. @@ -69,7 +69,7 @@ func TestKustomizeDeploy(t *testing.T) { }, { description: "deploy success with multiple kustomizations", - cfg: &latest.KustomizeDeploy{ + kustomize: latest.KustomizeDeploy{ KustomizePaths: []string{"a", "b"}, }, commands: testutil. @@ -97,25 +97,15 @@ func TestKustomizeDeploy(t *testing.T) { t.NewTempDir(). Chdir() - k := NewKustomizeDeployer(&runcontext.RunContext{ - WorkingDir: ".", - Cfg: latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - KustomizeDeploy: test.cfg, - }, - }, - }, - KubeContext: testKubeContext, - Opts: config.SkaffoldOptions{ - Namespace: testNamespace, - Force: test.forceDeploy, - WaitForDeletions: config.WaitForDeletions{ - Enabled: true, - Delay: 0 * time.Second, - Max: 10 * time.Second, - }, + k := NewKustomizeDeployer(&kustomizeConfig{ + workingDir: ".", + force: test.forceDeploy, + waitForDeletions: config.WaitForDeletions{ + Enabled: true, + Delay: 0 * time.Second, + Max: 10 * time.Second, }, + kustomize: test.kustomize, }, nil) _, err := k.Deploy(context.Background(), ioutil.Discard, test.builds) @@ -129,13 +119,13 @@ func TestKustomizeCleanup(t *testing.T) { tests := []struct { description string - cfg *latest.KustomizeDeploy + kustomize latest.KustomizeDeploy commands util.Command shouldErr bool }{ { description: "cleanup success", - cfg: &latest.KustomizeDeploy{ + kustomize: latest.KustomizeDeploy{ KustomizePaths: []string{tmpDir.Root()}, }, commands: testutil. @@ -144,7 +134,7 @@ func TestKustomizeCleanup(t *testing.T) { }, { description: "cleanup success with multiple kustomizations", - cfg: &latest.KustomizeDeploy{ + kustomize: latest.KustomizeDeploy{ KustomizePaths: tmpDir.Paths("a", "b"), }, commands: testutil. @@ -154,7 +144,7 @@ func TestKustomizeCleanup(t *testing.T) { }, { description: "cleanup error", - cfg: &latest.KustomizeDeploy{ + kustomize: latest.KustomizeDeploy{ KustomizePaths: []string{tmpDir.Root()}, }, commands: testutil. @@ -164,7 +154,7 @@ func TestKustomizeCleanup(t *testing.T) { }, { description: "fail to read manifests", - cfg: &latest.KustomizeDeploy{ + kustomize: latest.KustomizeDeploy{ KustomizePaths: []string{tmpDir.Root()}, }, commands: testutil.CmdRunOutErr( @@ -179,19 +169,9 @@ func TestKustomizeCleanup(t *testing.T) { testutil.Run(t, test.description, func(t *testutil.T) { t.Override(&util.DefaultExecCommand, test.commands) - k := NewKustomizeDeployer(&runcontext.RunContext{ - WorkingDir: tmpDir.Root(), - Cfg: latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - KustomizeDeploy: test.cfg, - }, - }, - }, - KubeContext: testKubeContext, - Opts: config.SkaffoldOptions{ - Namespace: testNamespace, - }, + k := NewKustomizeDeployer(&kustomizeConfig{ + workingDir: tmpDir.Root(), + kustomize: test.kustomize, }, nil) err := k.Cleanup(context.Background(), ioutil.Discard) @@ -393,17 +373,10 @@ func TestDependenciesForKustomization(t *testing.T) { tmpDir.Write(path, contents) } - k := NewKustomizeDeployer(&runcontext.RunContext{ - Cfg: latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - KustomizeDeploy: &latest.KustomizeDeploy{ - KustomizePaths: kustomizePaths, - }, - }, - }, + k := NewKustomizeDeployer(&kustomizeConfig{ + kustomize: latest.KustomizeDeploy{ + KustomizePaths: kustomizePaths, }, - KubeContext: testKubeContext, }, nil) deps, err := k.Dependencies() @@ -641,29 +614,38 @@ spec: kustomizationPaths = append(kustomizationPaths, kustomizationCall.folder) } t.Override(&util.DefaultExecCommand, fakeCmd) - t.NewTempDir(). - Chdir() + t.NewTempDir().Chdir() - k := NewKustomizeDeployer(&runcontext.RunContext{ - WorkingDir: ".", - Cfg: latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - KustomizeDeploy: &latest.KustomizeDeploy{ - KustomizePaths: kustomizationPaths, - }, - }, - }, - }, - KubeContext: testKubeContext, - Opts: config.SkaffoldOptions{ - Namespace: testNamespace, + k := NewKustomizeDeployer(&kustomizeConfig{ + workingDir: ".", + kustomize: latest.KustomizeDeploy{ + KustomizePaths: kustomizationPaths, }, }, test.labels) var b bytes.Buffer err := k.Render(context.Background(), &b, test.builds, true, "") + t.CheckError(test.shouldErr, err) t.CheckDeepEqual(test.expected, b.String()) }) } } + +type kustomizeConfig struct { + runcontext.RunContext // Embedded to provide the default values. + force bool + workingDir string + waitForDeletions config.WaitForDeletions + kustomize latest.KustomizeDeploy +} + +func (c *kustomizeConfig) ForceDeploy() bool { return c.force } +func (c *kustomizeConfig) WaitForDeletions() config.WaitForDeletions { return c.waitForDeletions } +func (c *kustomizeConfig) WorkingDir() string { return c.workingDir } +func (c *kustomizeConfig) GetKubeContext() string { return testKubeContext } +func (c *kustomizeConfig) GetKubeNamespace() string { return testNamespace } +func (c *kustomizeConfig) Pipeline() latest.Pipeline { + var pipeline latest.Pipeline + pipeline.Deploy.DeployType.KustomizeDeploy = &c.kustomize + return pipeline +} diff --git a/pkg/skaffold/deploy/resource/deployment_test.go b/pkg/skaffold/deploy/resource/deployment_test.go index df4e3615e18..f96ccf63bbe 100644 --- a/pkg/skaffold/deploy/resource/deployment_test.go +++ b/pkg/skaffold/deploy/resource/deployment_test.go @@ -100,12 +100,10 @@ func TestDeploymentCheckStatus(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { t.Override(&util.DefaultExecCommand, test.commands) + r := NewDeployment("dep", "test", 0) - runCtx := &runcontext.RunContext{ - KubeContext: "kubecontext", - } + r.CheckStatus(context.Background(), &statusConfig{}) - r.CheckStatus(context.Background(), runCtx) if test.cancelled { r.UpdateStatus(proto.ActionableErr{ ErrCode: proto.StatusCode_STATUSCHECK_USER_CANCELLED, @@ -366,3 +364,9 @@ func TestReportSinceLastUpdatedMultipleTimes(t *testing.T) { }) } } + +type statusConfig struct { + runcontext.RunContext // Embedded to provide the default values. +} + +func (c *statusConfig) GetKubeContext() string { return "kubecontext" } diff --git a/pkg/skaffold/deploy/status_check_test.go b/pkg/skaffold/deploy/status_check_test.go index 36e49a134d7..0fd8172253e 100644 --- a/pkg/skaffold/deploy/status_check_test.go +++ b/pkg/skaffold/deploy/status_check_test.go @@ -592,10 +592,9 @@ func TestPollDeployment(t *testing.T) { event.InitializeState(latest.Pipeline{}, "test", true, true, true) mockVal := mockValidator{runs: test.runs} dep := test.dep.WithValidator(mockVal) - runCtx := &runcontext.RunContext{ - KubeContext: "kubecontext", - } - pollDeploymentStatus(context.Background(), runCtx, dep) + + pollDeploymentStatus(context.Background(), &statusConfig{}, dep) + t.CheckDeepEqual(test.expected, test.dep.Status().ActionableError().ErrCode) }) } @@ -621,3 +620,9 @@ func (m mockValidator) WithLabel(string, string) diag.Diagnose { func (m mockValidator) WithValidators([]validator.Validator) diag.Diagnose { return m } + +type statusConfig struct { + runcontext.RunContext // Embedded to provide the default values. +} + +func (c *statusConfig) GetKubeContext() string { return testKubeContext } diff --git a/pkg/skaffold/diagnose/diagnose_test.go b/pkg/skaffold/diagnose/diagnose_test.go index e712146a17e..4d8c1de6ce2 100644 --- a/pkg/skaffold/diagnose/diagnose_test.go +++ b/pkg/skaffold/diagnose/diagnose_test.go @@ -81,22 +81,28 @@ func TestCheckArtifacts(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { tmpDir := t.NewTempDir().Write("Dockerfile", "FROM busybox") - runCtx := &runcontext.RunContext{ - Cfg: latest.Pipeline{ - Build: latest.BuildConfig{ - Artifacts: []*latest.Artifact{{ - Workspace: tmpDir.Root(), - ArtifactType: latest.ArtifactType{ - DockerArtifact: &latest.DockerArtifact{ - DockerfilePath: "Dockerfile", - }, - }, - }}, + err := CheckArtifacts(context.Background(), &mockConfig{ + artifacts: []*latest.Artifact{{ + Workspace: tmpDir.Root(), + ArtifactType: latest.ArtifactType{ + DockerArtifact: &latest.DockerArtifact{ + DockerfilePath: "Dockerfile", + }, }, - }, - } - err := CheckArtifacts(context.Background(), runCtx, ioutil.Discard) + }}, + }, ioutil.Discard) t.CheckNoError(err) }) } + +type mockConfig struct { + runcontext.RunContext // Embedded to provide the default values. + artifacts []*latest.Artifact +} + +func (c *mockConfig) Pipeline() latest.Pipeline { + var pipeline latest.Pipeline + pipeline.Build.Artifacts = c.artifacts + return pipeline +} diff --git a/pkg/skaffold/kubectl/cli_test.go b/pkg/skaffold/kubectl/cli_test.go index c9611157fdf..26096c6844a 100644 --- a/pkg/skaffold/kubectl/cli_test.go +++ b/pkg/skaffold/kubectl/cli_test.go @@ -20,8 +20,6 @@ import ( "context" "testing" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/testutil" ) @@ -67,12 +65,10 @@ func TestCLI(t *testing.T) { test.expectedCommand, )) - cli := NewCLI(&runcontext.RunContext{ - Opts: config.SkaffoldOptions{ - Namespace: test.namespace, - KubeConfig: test.kubeconfig, - }, - KubeContext: kubeContext, + cli := NewCLI(&mockConfig{ + kubeContext: kubeContext, + kubeConfig: test.kubeconfig, + namespace: test.namespace, }) err := cli.Run(context.Background(), nil, nil, "exec", "arg1", "arg2") @@ -88,12 +84,10 @@ func TestCLI(t *testing.T) { output, )) - cli := NewCLI(&runcontext.RunContext{ - Opts: config.SkaffoldOptions{ - Namespace: test.namespace, - KubeConfig: test.kubeconfig, - }, - KubeContext: kubeContext, + cli := NewCLI(&mockConfig{ + kubeContext: kubeContext, + kubeConfig: test.kubeconfig, + namespace: test.namespace, }) out, err := cli.RunOut(context.Background(), "exec", "arg1", "arg2") @@ -110,17 +104,26 @@ func TestCLI(t *testing.T) { output, )) - cli := NewCLI(&runcontext.RunContext{ - Opts: config.SkaffoldOptions{ - Namespace: test.namespace, - KubeConfig: test.kubeconfig, - }, - KubeContext: kubeContext, + cli := NewCLI(&mockConfig{ + kubeContext: kubeContext, + kubeConfig: test.kubeconfig, + namespace: test.namespace, }) cmd := cli.CommandWithStrictCancellation(context.Background(), "exec", "arg1", "arg2") out, err := util.RunCmdOut(cmd.Cmd) + t.CheckNoError(err) t.CheckDeepEqual(string(out), output) }) } } + +type mockConfig struct { + kubeContext string + kubeConfig string + namespace string +} + +func (c *mockConfig) GetKubeContext() string { return c.kubeContext } +func (c *mockConfig) GetKubeConfig() string { return c.kubeConfig } +func (c *mockConfig) GetKubeNamespace() string { return c.namespace } diff --git a/pkg/skaffold/runner/runcontext/context_test.go b/pkg/skaffold/runner/runcontext/context_test.go index b1546202e38..82d8dcfe978 100644 --- a/pkg/skaffold/runner/runcontext/context_test.go +++ b/pkg/skaffold/runner/runcontext/context_test.go @@ -24,64 +24,69 @@ import ( func TestRunContext_UpdateNamespaces(t *testing.T) { tests := []struct { - description string - runContext *RunContext - namespaces []string - expected []string + description string + oldNamespaces []string + newNamespaces []string + expected []string }{ { - description: "update namespace when not present in runContext", - runContext: &RunContext{Namespaces: []string{"test"}}, - namespaces: []string{"another"}, - expected: []string{"another", "test"}, + description: "update namespace when not present in runContext", + oldNamespaces: []string{"test"}, + newNamespaces: []string{"another"}, + expected: []string{"another", "test"}, }, { - description: "update namespace with duplicates should not return duplicate", - runContext: &RunContext{Namespaces: []string{"test", "foo"}}, - namespaces: []string{"another", "foo", "another"}, - expected: []string{"another", "foo", "test"}, + description: "update namespace with duplicates should not return duplicate", + oldNamespaces: []string{"test", "foo"}, + newNamespaces: []string{"another", "foo", "another"}, + expected: []string{"another", "foo", "test"}, }, { - description: "update namespaces when namespaces is empty", - runContext: &RunContext{Namespaces: []string{"test", "foo"}}, - namespaces: []string{}, - expected: []string{"test", "foo"}, + description: "update namespaces when namespaces is empty", + oldNamespaces: []string{"test", "foo"}, + newNamespaces: []string{}, + expected: []string{"test", "foo"}, }, { - description: "update namespaces when runcontext namespaces is empty", - runContext: &RunContext{Namespaces: []string{}}, - namespaces: []string{"test", "another"}, - expected: []string{"another", "test"}, + description: "update namespaces when runcontext namespaces is empty", + oldNamespaces: []string{}, + newNamespaces: []string{"test", "another"}, + expected: []string{"another", "test"}, }, { - description: "update namespaces when both namespaces and runcontext namespaces is empty", - runContext: &RunContext{Namespaces: []string{}}, - namespaces: []string{}, - expected: []string{}, + description: "update namespaces when both namespaces and runcontext namespaces is empty", + oldNamespaces: []string{}, + newNamespaces: []string{}, + expected: []string{}, }, { - description: "update namespace when runcontext namespace has an empty string", - runContext: &RunContext{Namespaces: []string{""}}, - namespaces: []string{"another"}, - expected: []string{"another"}, + description: "update namespace when runcontext namespace has an empty string", + oldNamespaces: []string{""}, + newNamespaces: []string{"another"}, + expected: []string{"another"}, }, { - description: "update namespace when namespace is empty string", - runContext: &RunContext{Namespaces: []string{"test"}}, - namespaces: []string{""}, - expected: []string{"test"}, + description: "update namespace when namespace is empty string", + oldNamespaces: []string{"test"}, + newNamespaces: []string{""}, + expected: []string{"test"}, }, { - description: "update namespace when namespace is empty string and runContext is empty", - runContext: &RunContext{Namespaces: []string{}}, - namespaces: []string{""}, - expected: []string{}, + description: "update namespace when namespace is empty string and runContext is empty", + oldNamespaces: []string{}, + newNamespaces: []string{""}, + expected: []string{}, }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - test.runContext.UpdateNamespaces(test.namespaces) - t.CheckDeepEqual(test.expected, test.runContext.Namespaces) + runCtx := &RunContext{ + Namespaces: test.oldNamespaces, + } + + runCtx.UpdateNamespaces(test.newNamespaces) + + t.CheckDeepEqual(test.expected, runCtx.Namespaces) }) } } diff --git a/pkg/skaffold/test/test_test.go b/pkg/skaffold/test/test_test.go index c0be7f37f87..7460b04ad7f 100644 --- a/pkg/skaffold/test/test_test.go +++ b/pkg/skaffold/test/test_test.go @@ -38,8 +38,8 @@ func TestNoTestDependencies(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { t.Override(&docker.NewAPIClient, func(docker.Config) (docker.LocalDaemon, error) { return nil, nil }) - runCtx := &runcontext.RunContext{} - deps, err := NewTester(runCtx, true).TestDependencies() + cfg := &mockConfig{} + deps, err := NewTester(cfg, true).TestDependencies() t.CheckNoError(err) t.CheckEmpty(deps) @@ -50,17 +50,15 @@ func TestTestDependencies(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { tmpDir := t.NewTempDir().Touch("tests/test1.yaml", "tests/test2.yaml", "test3.yaml") - runCtx := &runcontext.RunContext{ - WorkingDir: tmpDir.Root(), - Cfg: latest.Pipeline{ - Test: []*latest.TestCase{ - {StructureTests: []string{"./tests/*"}}, - {}, - {StructureTests: []string{"test3.yaml"}}, - }, + cfg := &mockConfig{ + workingDir: tmpDir.Root(), + tests: []*latest.TestCase{ + {StructureTests: []string{"./tests/*"}}, + {}, + {StructureTests: []string{"test3.yaml"}}, }, } - deps, err := NewTester(runCtx, true).TestDependencies() + deps, err := NewTester(cfg, true).TestDependencies() expectedDeps := tmpDir.Paths("tests/test1.yaml", "tests/test2.yaml", "test3.yaml") t.CheckNoError(err) @@ -70,16 +68,14 @@ func TestTestDependencies(t *testing.T) { func TestWrongPattern(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { - runCtx := &runcontext.RunContext{ - Cfg: latest.Pipeline{ - Test: []*latest.TestCase{{ - ImageName: "image", - StructureTests: []string{"[]"}, - }}, - }, + cfg := &mockConfig{ + tests: []*latest.TestCase{{ + ImageName: "image", + StructureTests: []string{"[]"}, + }}, } - tester := NewTester(runCtx, true) + tester := NewTester(cfg, true) _, err := tester.TestDependencies() t.CheckError(true, err) @@ -94,9 +90,9 @@ func TestWrongPattern(t *testing.T) { func TestNoTest(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { - runCtx := &runcontext.RunContext{} + cfg := &mockConfig{} - tester := NewTester(runCtx, true) + tester := NewTester(cfg, true) err := tester.Test(context.Background(), ioutil.Discard, nil) t.CheckNoError(err) @@ -109,7 +105,7 @@ func TestIgnoreDockerNotFound(t *testing.T) { return nil, errors.New("not found") }) - tester := NewTester(&runcontext.RunContext{}, true) + tester := NewTester(&mockConfig{}, true) t.CheckNil(tester) }) @@ -123,30 +119,28 @@ func TestTestSuccess(t *testing.T) { CmdRun("container-structure-test test -v warn --image image:tag --config "+tmpDir.Path("tests/test1.yaml")+" --config "+tmpDir.Path("tests/test2.yaml")). AndRun("container-structure-test test -v warn --image image:tag --config "+tmpDir.Path("test3.yaml"))) - runCtx := &runcontext.RunContext{ - WorkingDir: tmpDir.Root(), - Cfg: latest.Pipeline{ - Test: []*latest.TestCase{ - { - ImageName: "image", - StructureTests: []string{"./tests/*"}, - }, - {}, - { - ImageName: "image", - StructureTests: []string{"test3.yaml"}, - }, - { - // This is image is not built so it won't be tested. - ImageName: "not-built", - StructureTests: []string{"./tests/*"}, - }, + cfg := &mockConfig{ + workingDir: tmpDir.Root(), + tests: []*latest.TestCase{ + { + ImageName: "image", + StructureTests: []string{"./tests/*"}, + }, + {}, + { + ImageName: "image", + StructureTests: []string{"test3.yaml"}, + }, + { + // This is image is not built so it won't be tested. + ImageName: "not-built", + StructureTests: []string{"./tests/*"}, }, }, } imagesAreLocal := true - err := NewTester(runCtx, imagesAreLocal).Test(context.Background(), ioutil.Discard, []build.Artifact{{ + err := NewTester(cfg, imagesAreLocal).Test(context.Background(), ioutil.Discard, []build.Artifact{{ ImageName: "image", Tag: "image:tag", }}) @@ -163,17 +157,15 @@ func TestTestSuccessRemoteImage(t *testing.T) { return docker.NewLocalDaemon(&testutil.FakeAPIClient{}, nil, false, nil), nil }) - runCtx := &runcontext.RunContext{ - Cfg: latest.Pipeline{ - Test: []*latest.TestCase{{ - ImageName: "image", - StructureTests: []string{"test.yaml"}, - }}, - }, + cfg := &mockConfig{ + tests: []*latest.TestCase{{ + ImageName: "image", + StructureTests: []string{"test.yaml"}, + }}, } imagesAreLocal := false - err := NewTester(runCtx, imagesAreLocal).Test(context.Background(), ioutil.Discard, []build.Artifact{{ + err := NewTester(cfg, imagesAreLocal).Test(context.Background(), ioutil.Discard, []build.Artifact{{ ImageName: "image", Tag: "image:tag", }}) @@ -190,17 +182,15 @@ func TestTestFailureRemoteImage(t *testing.T) { return docker.NewLocalDaemon(&testutil.FakeAPIClient{ErrImagePull: true}, nil, false, nil), nil }) - runCtx := &runcontext.RunContext{ - Cfg: latest.Pipeline{ - Test: []*latest.TestCase{{ - ImageName: "image", - StructureTests: []string{"test.yaml"}, - }}, - }, + cfg := &mockConfig{ + tests: []*latest.TestCase{{ + ImageName: "image", + StructureTests: []string{"test.yaml"}, + }}, } imagesAreLocal := false - err := NewTester(runCtx, imagesAreLocal).Test(context.Background(), ioutil.Discard, []build.Artifact{{ + err := NewTester(cfg, imagesAreLocal).Test(context.Background(), ioutil.Discard, []build.Artifact{{ ImageName: "image", Tag: "image:tag", }}) @@ -218,18 +208,16 @@ func TestTestFailure(t *testing.T) { errors.New("FAIL"), )) - runCtx := &runcontext.RunContext{ - Cfg: latest.Pipeline{ - Test: []*latest.TestCase{ - { - ImageName: "broken-image", - StructureTests: []string{"test.yaml"}, - }, + cfg := &mockConfig{ + tests: []*latest.TestCase{ + { + ImageName: "broken-image", + StructureTests: []string{"test.yaml"}, }, }, } - err := NewTester(runCtx, true).Test(context.Background(), ioutil.Discard, []build.Artifact{{ + err := NewTester(cfg, true).Test(context.Background(), ioutil.Discard, []build.Artifact{{ ImageName: "broken-image", Tag: "broken-image:tag", }}) @@ -242,23 +230,19 @@ func TestTestMuted(t *testing.T) { tmpDir := t.NewTempDir().Touch("test.yaml") t.Override(&util.DefaultExecCommand, testutil.CmdRun("container-structure-test test -v warn --image image:tag --config "+tmpDir.Path("test.yaml"))) - runCtx := &runcontext.RunContext{ - WorkingDir: tmpDir.Root(), - Cfg: latest.Pipeline{ - Test: []*latest.TestCase{{ - ImageName: "image", - StructureTests: []string{"test.yaml"}, - }}, - }, - Opts: config.SkaffoldOptions{ - Muted: config.Muted{ - Phases: []string{"test"}, - }, + cfg := &mockConfig{ + workingDir: tmpDir.Root(), + tests: []*latest.TestCase{{ + ImageName: "image", + StructureTests: []string{"test.yaml"}, + }}, + muted: config.Muted{ + Phases: []string{"test"}, }, } var buf bytes.Buffer - err := NewTester(runCtx, true).Test(context.Background(), &buf, []build.Artifact{{ + err := NewTester(cfg, true).Test(context.Background(), &buf, []build.Artifact{{ ImageName: "image", Tag: "image:tag", }}) @@ -267,3 +251,18 @@ func TestTestMuted(t *testing.T) { t.CheckContains("- writing logs to "+filepath.Join(os.TempDir(), "skaffold", "test.log"), buf.String()) }) } + +type mockConfig struct { + runcontext.RunContext // Embedded to provide the default values. + workingDir string + tests []*latest.TestCase + muted config.Muted +} + +func (c *mockConfig) Muted() config.Muted { return c.muted } +func (c *mockConfig) GetWorkingDir() string { return c.workingDir } +func (c *mockConfig) Pipeline() latest.Pipeline { + var pipeline latest.Pipeline + pipeline.Test = c.tests + return pipeline +} diff --git a/pkg/skaffold/trigger/triggers_test.go b/pkg/skaffold/trigger/triggers_test.go index 7cb3e8751ff..e00e2f800f9 100644 --- a/pkg/skaffold/trigger/triggers_test.go +++ b/pkg/skaffold/trigger/triggers_test.go @@ -26,29 +26,30 @@ import ( "github.com/google/go-cmp/cmp" "github.com/rjeczalik/notify" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/testutil" ) func TestNewTrigger(t *testing.T) { tests := []struct { - description string - opts config.SkaffoldOptions - expected Trigger - shouldErr bool + description string + trigger string + watchPollInterval int + expected Trigger + shouldErr bool }{ { - description: "polling trigger", - opts: config.SkaffoldOptions{Trigger: "polling", WatchPollInterval: 1}, + description: "polling trigger", + trigger: "polling", + watchPollInterval: 1, expected: &pollTrigger{ Interval: 1 * time.Millisecond, }, }, { - description: "notify trigger", - opts: config.SkaffoldOptions{Trigger: "notify", WatchPollInterval: 1}, + description: "notify trigger", + trigger: "notify", + watchPollInterval: 1, expected: &fsNotifyTrigger{ Interval: 1 * time.Millisecond, workspaces: map[string]struct{}{ @@ -60,35 +61,29 @@ func TestNewTrigger(t *testing.T) { }, { description: "manual trigger", - opts: config.SkaffoldOptions{Trigger: "manual"}, + trigger: "manual", expected: &manualTrigger{}, }, { description: "unknown trigger", - opts: config.SkaffoldOptions{Trigger: "unknown"}, + trigger: "unknown", shouldErr: true, }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - runCtx := &runcontext.RunContext{ - Opts: test.opts, - Cfg: latest.Pipeline{ - Build: latest.BuildConfig{ - Artifacts: []*latest.Artifact{ - { - Workspace: "../workspace", - }, { - Workspace: "../workspace", - }, { - Workspace: "../some/other/workspace", - }, - }, - }, + cfg := &mockConfig{ + trigger: test.trigger, + watchPollInterval: test.watchPollInterval, + artifacts: []*latest.Artifact{ + {Workspace: "../workspace"}, + {Workspace: "../workspace"}, + {Workspace: "../some/other/workspace"}, }, } - got, err := NewTrigger(runCtx, nil) + got, err := NewTrigger(cfg, nil) + t.CheckError(test.shouldErr, err) if !test.shouldErr { t.CheckDeepEqual(test.expected, got, cmp.AllowUnexported(fsNotifyTrigger{}), cmp.Comparer(ignoreFuncComparer), cmp.AllowUnexported(manualTrigger{}), cmp.AllowUnexported(pollTrigger{})) @@ -260,3 +255,17 @@ func TestStartTrigger(t *testing.T) { }) } } + +type mockConfig struct { + trigger string + watchPollInterval int + artifacts []*latest.Artifact +} + +func (c *mockConfig) Trigger() string { return c.trigger } +func (c *mockConfig) WatchPollInterval() int { return c.watchPollInterval } +func (c *mockConfig) Pipeline() latest.Pipeline { + var pipeline latest.Pipeline + pipeline.Build.Artifacts = c.artifacts + return pipeline +} From 4b3ca59af505c4ab5d5b6960a194e1f6887018f8 Mon Sep 17 00:00:00 2001 From: Marlon Gamez Date: Wed, 2 Sep 2020 11:33:11 -0700 Subject: [PATCH 131/138] Release v1.14.0 (#4756) * v1.14.0 changelog and examples * add links and config message to changelog --- CHANGELOG.md | 85 +++++++++++++++++++ examples/bazel/WORKSPACE | 17 +++- examples/bazel/skaffold.yaml | 2 +- examples/buildpacks-java/skaffold.yaml | 4 +- examples/buildpacks-node/skaffold.yaml | 4 +- examples/buildpacks-python/skaffold.yaml | 2 +- examples/buildpacks/skaffold.yaml | 4 +- examples/custom/skaffold.yaml | 2 +- examples/gcb-kaniko/skaffold.yaml | 2 +- examples/generate-pipeline/skaffold.yaml | 2 +- .../getting-started-kustomize/skaffold.yaml | 2 +- examples/getting-started/skaffold.yaml | 2 +- examples/google-cloud-build/skaffold.yaml | 2 +- .../skaffold.yaml | 2 +- examples/helm-deployment/skaffold.yaml | 2 +- examples/hot-reload/skaffold.yaml | 2 +- examples/jib-gradle/skaffold.yaml | 2 +- examples/jib-multimodule/skaffold.yaml | 2 +- examples/jib-sync/goose.yaml | 24 ------ examples/jib-sync/skaffold-gradle.yaml | 2 +- examples/jib-sync/skaffold-maven.yaml | 2 +- examples/jib/skaffold.yaml | 2 +- examples/kaniko/skaffold.yaml | 2 +- .../kustomize/skaffold-kustomize-args.yaml | 2 +- examples/kustomize/skaffold.yaml | 2 +- examples/microservices/skaffold.yaml | 2 +- examples/nodejs/skaffold.yaml | 2 +- examples/profile-patches/skaffold.yaml | 2 +- examples/profiles/skaffold.yaml | 2 +- examples/react-reload/skaffold.yaml | 2 +- examples/ruby/skaffold.yaml | 2 +- examples/structure-tests/skaffold.yaml | 2 +- .../skaffold.yaml | 2 +- examples/templated-fields/skaffold.yaml | 2 +- pkg/skaffold/schema/latest/config.go | 2 +- 35 files changed, 131 insertions(+), 65 deletions(-) delete mode 100644 examples/jib-sync/goose.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 9de5400248d..078c5396131 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,88 @@ +# v1.14.0 Release - 09/02/2020 + +**Linux** +`curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v1.14.0/skaffold-linux-amd64 && chmod +x skaffold && sudo mv skaffold /usr/local/bin` + +**macOS** +`curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v1.14.0/skaffold-darwin-amd64 && chmod +x skaffold && sudo mv skaffold /usr/local/bin` + +**Windows** + https://storage.googleapis.com/skaffold/releases/v1.14.0/skaffold-windows-amd64.exe + +**Docker image** +`gcr.io/k8s-skaffold/skaffold:v1.14.0` + +Note: This release comes with a new config version, `v2beta7`. To upgrade your skaffold.yaml, use `skaffold fix`. If you choose not to upgrade, skaffold will auto-upgrade as best as it can. + + +Highlights: +* Skaffold can now detect minikube clusters regardless of profile name +* Skaffold now has support for debugging .NET Core containers +* Statuscheck phase is seeing some UX improvements + + +New Features: +* Build debuggable containers [#4606](https://github.com/GoogleContainerTools/skaffold/pull/4606) +* Add .NET Core container debugging support [#4699](https://github.com/GoogleContainerTools/skaffold/pull/4699) +* Identify minikube cluster for any profile name [#4701](https://github.com/GoogleContainerTools/skaffold/pull/4701) +* Add SKAFFOLD_CMDLINE environment variable to pass command-line [#4704](https://github.com/GoogleContainerTools/skaffold/pull/4704) +* Hide minikube detection behind flag [#4745](https://github.com/GoogleContainerTools/skaffold/pull/4745) + + +Fixes: +* Handle ctrl-c in the middle of GetAllAuthConfigs() [#4603](https://github.com/GoogleContainerTools/skaffold/pull/4603) +* Fix 4748: Panic with skaffold dev [#4750](https://github.com/GoogleContainerTools/skaffold/pull/4750) + + +Updates: +* Introduce Config interfaces [#4598](https://github.com/GoogleContainerTools/skaffold/pull/4598) +* add pod initialization logic in diag and follow up some minor reporting changes. [#4690](https://github.com/GoogleContainerTools/skaffold/pull/4690) +* Kpt Deployer Render() implementation and tests [#4708](https://github.com/GoogleContainerTools/skaffold/pull/4708) +* Use ParseTolerant to parse Helm version because of missing patch version [#4712](https://github.com/GoogleContainerTools/skaffold/pull/4712) +* Add explicit tests for Helm version parsing [#4715](https://github.com/GoogleContainerTools/skaffold/pull/4715) +* drop codecov patch threshold to 40% [#4716](https://github.com/GoogleContainerTools/skaffold/pull/4716) +* Add Kustomize Hydration to Kpt Deployer's Render method [#4719](https://github.com/GoogleContainerTools/skaffold/pull/4719) +* Move kubernetes client into its own package [#4720](https://github.com/GoogleContainerTools/skaffold/pull/4720) +* Move `DetectWSL` function into util package [#4721](https://github.com/GoogleContainerTools/skaffold/pull/4721) +* Kpt Deployer Deploy() Implementation/Tests [#4723](https://github.com/GoogleContainerTools/skaffold/pull/4723) +* Add test for using helm setFiles [#4735](https://github.com/GoogleContainerTools/skaffold/pull/4735) +* Extending Workflow for Kpt Deployer (accepting additional arguments) [#4736](https://github.com/GoogleContainerTools/skaffold/pull/4736) +* Fix slow test [#4740](https://github.com/GoogleContainerTools/skaffold/pull/4740) +* Fix slow tests [#4741](https://github.com/GoogleContainerTools/skaffold/pull/4741) +* Minikube cluster detection followup [#4742](https://github.com/GoogleContainerTools/skaffold/pull/4742) +* Rename NewFromRunContext() to NewCLI() [#4743](https://github.com/GoogleContainerTools/skaffold/pull/4743) +* Use the newer notation for integration tests [#4744](https://github.com/GoogleContainerTools/skaffold/pull/4744) +* Leverage Config interfaces to simplify tests [#4754](https://github.com/GoogleContainerTools/skaffold/pull/4754) + + +Dependency Updates: +* Bump golangci lint v1.30.0 [#4739](https://github.com/GoogleContainerTools/skaffold/pull/4739) + + +Docs Updates: +* Update log-tailing.md [#4636](https://github.com/GoogleContainerTools/skaffold/pull/4636) +* Change kpt deployer doc from Beta to Alpha [#4728](https://github.com/GoogleContainerTools/skaffold/pull/4728) + + +Huge thanks goes out to all of our contributors for this release: + +- Appu Goundan +- Boris Dudelsack +- Brian C +- Brian de Alwis +- David Gageot +- Felix Tran +- Gaurav +- Hasso Mehide +- Julien Ammous +- Marlon Gamez +- MrLuje +- Mridula +- Nick Kubala +- Tejal Desai +- Tyler Schroeder +- Yuwen Ma + # v1.13.2 Release - 08/20/2020 **Linux** diff --git a/examples/bazel/WORKSPACE b/examples/bazel/WORKSPACE index 1c1f8a3ea15..5637093ed3e 100644 --- a/examples/bazel/WORKSPACE +++ b/examples/bazel/WORKSPACE @@ -4,9 +4,9 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "io_bazel_rules_docker", - sha256 = "3efbd23e195727a67f87b2a04fb4388cc7a11a0c0c2cf33eec225fb8ffbb27ea", - strip_prefix = "rules_docker-0.14.2", - urls = ["https://github.com/bazelbuild/rules_docker/releases/download/v0.14.2/rules_docker-v0.14.2.tar.gz"], + sha256 = "4521794f0fba2e20f3bf15846ab5e01d5332e587e9ce81629c7f96c793bb7036", + strip_prefix = "rules_docker-0.14.4", + urls = ["https://github.com/bazelbuild/rules_docker/releases/download/v0.14.4/rules_docker-v0.14.4.tar.gz"], ) http_archive( @@ -32,6 +32,17 @@ load( container_repositories() +load( + "@io_bazel_rules_docker//repositories:deps.bzl", + container_deps = "deps", +) + +container_deps() + +load("@io_bazel_rules_docker//repositories:pip_repositories.bzl", "pip_deps") + +pip_deps() + load( "@io_bazel_rules_docker//go:image.bzl", _go_image_repos = "repositories", diff --git a/examples/bazel/skaffold.yaml b/examples/bazel/skaffold.yaml index a3a28920935..4bc1c9ce18c 100644 --- a/examples/bazel/skaffold.yaml +++ b/examples/bazel/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/examples/buildpacks-java/skaffold.yaml b/examples/buildpacks-java/skaffold.yaml index ed374ab601d..04237cad528 100644 --- a/examples/buildpacks-java/skaffold.yaml +++ b/examples/buildpacks-java/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: @@ -7,8 +7,6 @@ build: builder: "gcr.io/buildpacks/builder:v1" env: - GOOGLE_RUNTIME_VERSION=8 - sync: - auto: {} profiles: - name: gcb build: diff --git a/examples/buildpacks-node/skaffold.yaml b/examples/buildpacks-node/skaffold.yaml index 2b7f0951102..5f34b414c89 100644 --- a/examples/buildpacks-node/skaffold.yaml +++ b/examples/buildpacks-node/skaffold.yaml @@ -1,9 +1,7 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: - image: skaffold-buildpacks-node buildpacks: builder: "gcr.io/buildpacks/builder:v1" - sync: - auto: {} diff --git a/examples/buildpacks-python/skaffold.yaml b/examples/buildpacks-python/skaffold.yaml index 5fbbafb9f54..3fedbd626cb 100644 --- a/examples/buildpacks-python/skaffold.yaml +++ b/examples/buildpacks-python/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/examples/buildpacks/skaffold.yaml b/examples/buildpacks/skaffold.yaml index d78c0a32950..f5ee9475dee 100644 --- a/examples/buildpacks/skaffold.yaml +++ b/examples/buildpacks/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: @@ -7,8 +7,6 @@ build: builder: "gcr.io/buildpacks/builder:v1" env: - GOPROXY={{.GOPROXY}} - sync: - auto: {} profiles: - name: gcb build: diff --git a/examples/custom/skaffold.yaml b/examples/custom/skaffold.yaml index 9fab8ee3963..498dfb1f189 100644 --- a/examples/custom/skaffold.yaml +++ b/examples/custom/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/examples/gcb-kaniko/skaffold.yaml b/examples/gcb-kaniko/skaffold.yaml index a6fd38405ce..1db45ad5549 100644 --- a/examples/gcb-kaniko/skaffold.yaml +++ b/examples/gcb-kaniko/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: googleCloudBuild: diff --git a/examples/generate-pipeline/skaffold.yaml b/examples/generate-pipeline/skaffold.yaml index 5716074437a..fdfaafcfced 100644 --- a/examples/generate-pipeline/skaffold.yaml +++ b/examples/generate-pipeline/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/examples/getting-started-kustomize/skaffold.yaml b/examples/getting-started-kustomize/skaffold.yaml index 14229de67ab..35a2919b26a 100644 --- a/examples/getting-started-kustomize/skaffold.yaml +++ b/examples/getting-started-kustomize/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config metadata: name: getting-started-kustomize diff --git a/examples/getting-started/skaffold.yaml b/examples/getting-started/skaffold.yaml index 36a019559aa..65a4f4fdfe6 100644 --- a/examples/getting-started/skaffold.yaml +++ b/examples/getting-started/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/examples/google-cloud-build/skaffold.yaml b/examples/google-cloud-build/skaffold.yaml index dc50e83541e..ff2f351855b 100644 --- a/examples/google-cloud-build/skaffold.yaml +++ b/examples/google-cloud-build/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: googleCloudBuild: diff --git a/examples/helm-deployment-dependencies/skaffold.yaml b/examples/helm-deployment-dependencies/skaffold.yaml index ffb340c5aa8..c3fc6cd06c7 100644 --- a/examples/helm-deployment-dependencies/skaffold.yaml +++ b/examples/helm-deployment-dependencies/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: tagPolicy: diff --git a/examples/helm-deployment/skaffold.yaml b/examples/helm-deployment/skaffold.yaml index ee904081d92..09baacb88b5 100644 --- a/examples/helm-deployment/skaffold.yaml +++ b/examples/helm-deployment/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/examples/hot-reload/skaffold.yaml b/examples/hot-reload/skaffold.yaml index e11cc149ac4..9a787fd98a3 100644 --- a/examples/hot-reload/skaffold.yaml +++ b/examples/hot-reload/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/examples/jib-gradle/skaffold.yaml b/examples/jib-gradle/skaffold.yaml index 11973a4ee48..75bd9a1be59 100644 --- a/examples/jib-gradle/skaffold.yaml +++ b/examples/jib-gradle/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/examples/jib-multimodule/skaffold.yaml b/examples/jib-multimodule/skaffold.yaml index 6f4e4284630..a671cc903d1 100644 --- a/examples/jib-multimodule/skaffold.yaml +++ b/examples/jib-multimodule/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/examples/jib-sync/goose.yaml b/examples/jib-sync/goose.yaml deleted file mode 100644 index 24f4fefd77e..00000000000 --- a/examples/jib-sync/goose.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: skaffold/v2beta5 -kind: Config -build: - artifacts: - - image: test-file-sync - jib: - type: must-use-profile - sync: {} - -profiles: -- name: maven - patches: - - op: add - path: /build/artifacts/0/jib/args - value: - - --no-transfer-progress - - op: replace - path: /build/artifacts/0/jib/type - value: maven -- name: gradle - patches: - - op: replace - path: /build/artifacts/0/jib/type - value: gradle diff --git a/examples/jib-sync/skaffold-gradle.yaml b/examples/jib-sync/skaffold-gradle.yaml index 7250cccc165..ada2bcc348f 100644 --- a/examples/jib-sync/skaffold-gradle.yaml +++ b/examples/jib-sync/skaffold-gradle.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/examples/jib-sync/skaffold-maven.yaml b/examples/jib-sync/skaffold-maven.yaml index a1aff752bc5..422e5da3a8c 100644 --- a/examples/jib-sync/skaffold-maven.yaml +++ b/examples/jib-sync/skaffold-maven.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/examples/jib/skaffold.yaml b/examples/jib/skaffold.yaml index 87419a7533f..1a1707d2708 100644 --- a/examples/jib/skaffold.yaml +++ b/examples/jib/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/examples/kaniko/skaffold.yaml b/examples/kaniko/skaffold.yaml index f7d37aa9627..4c1badb0b35 100644 --- a/examples/kaniko/skaffold.yaml +++ b/examples/kaniko/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/examples/kustomize/skaffold-kustomize-args.yaml b/examples/kustomize/skaffold-kustomize-args.yaml index 3c475b101d7..bd6e9a21ae4 100644 --- a/examples/kustomize/skaffold-kustomize-args.yaml +++ b/examples/kustomize/skaffold-kustomize-args.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config deploy: kustomize: diff --git a/examples/kustomize/skaffold.yaml b/examples/kustomize/skaffold.yaml index 276e66ab73d..839cf213ad5 100644 --- a/examples/kustomize/skaffold.yaml +++ b/examples/kustomize/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config deploy: kustomize: {} diff --git a/examples/microservices/skaffold.yaml b/examples/microservices/skaffold.yaml index 0610fa1e5c6..7766da3b7b6 100644 --- a/examples/microservices/skaffold.yaml +++ b/examples/microservices/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/examples/nodejs/skaffold.yaml b/examples/nodejs/skaffold.yaml index 880aad983cc..aa18d1e6ca2 100644 --- a/examples/nodejs/skaffold.yaml +++ b/examples/nodejs/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: diff --git a/examples/profile-patches/skaffold.yaml b/examples/profile-patches/skaffold.yaml index 2193fb2909f..21f66598837 100644 --- a/examples/profile-patches/skaffold.yaml +++ b/examples/profile-patches/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: # only build and deploy "base-service" on main profile diff --git a/examples/profiles/skaffold.yaml b/examples/profiles/skaffold.yaml index 2d120284cc6..f0ef59403b0 100644 --- a/examples/profiles/skaffold.yaml +++ b/examples/profiles/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: # only build and deploy "world-service" on main profile diff --git a/examples/react-reload/skaffold.yaml b/examples/react-reload/skaffold.yaml index 00074bdf8be..1a108870e56 100644 --- a/examples/react-reload/skaffold.yaml +++ b/examples/react-reload/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/examples/ruby/skaffold.yaml b/examples/ruby/skaffold.yaml index cc716d78fb0..58d361f4c2b 100644 --- a/examples/ruby/skaffold.yaml +++ b/examples/ruby/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/examples/structure-tests/skaffold.yaml b/examples/structure-tests/skaffold.yaml index 5fafb7d386c..238d314d241 100644 --- a/examples/structure-tests/skaffold.yaml +++ b/examples/structure-tests/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/examples/tagging-with-environment-variables/skaffold.yaml b/examples/tagging-with-environment-variables/skaffold.yaml index edfa4fdee9d..694a193fcdb 100644 --- a/examples/tagging-with-environment-variables/skaffold.yaml +++ b/examples/tagging-with-environment-variables/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config build: artifacts: diff --git a/examples/templated-fields/skaffold.yaml b/examples/templated-fields/skaffold.yaml index 91c54e99358..aa2b6ce0778 100644 --- a/examples/templated-fields/skaffold.yaml +++ b/examples/templated-fields/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta6 +apiVersion: skaffold/v2beta7 kind: Config metadata: name: my-app diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 8311fdd72c8..5462fd288bb 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -22,7 +22,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util" ) -// This config version is not yet released, it is SAFE TO MODIFY the structs in this file. +// !!! WARNING !!! This config version is already released, please DO NOT MODIFY the structs in this file. const Version string = "skaffold/v2beta7" // NewSkaffoldConfig creates a SkaffoldConfig From 36284dd9abc5b59b3a5e0e46fc32eaba5ea0921e Mon Sep 17 00:00:00 2001 From: David Gageot Date: Mon, 7 Sep 2020 08:47:45 +0200 Subject: [PATCH 132/138] Remove duplication (#4759) Signed-off-by: David Gageot --- pkg/skaffold/build/bazel/build_test.go | 13 ++++++++----- pkg/skaffold/build/buildpacks/build_test.go | 2 +- pkg/skaffold/build/buildpacks/fetcher_test.go | 8 +++++++- pkg/skaffold/build/cache/lookup_test.go | 10 ++++++++-- pkg/skaffold/build/cache/retrieve_test.go | 4 ++-- pkg/skaffold/build/jib/gradle_test.go | 4 ++-- pkg/skaffold/build/jib/jib_test.go | 7 +++++++ pkg/skaffold/build/jib/maven_test.go | 4 ++-- pkg/skaffold/build/local/docker_test.go | 12 +++++++++++- pkg/skaffold/build/local/local_test.go | 2 +- pkg/skaffold/test/test_test.go | 10 ++++++++-- 11 files changed, 57 insertions(+), 19 deletions(-) diff --git a/pkg/skaffold/build/bazel/build_test.go b/pkg/skaffold/build/bazel/build_test.go index 9cd5583620c..b04201e88b3 100644 --- a/pkg/skaffold/build/bazel/build_test.go +++ b/pkg/skaffold/build/bazel/build_test.go @@ -33,7 +33,8 @@ func TestBuildBazel(t *testing.T) { t.Override(&util.DefaultExecCommand, testutil.CmdRun("bazel build //:app.tar").AndRunOut("bazel info bazel-bin", "bin")) testutil.CreateFakeImageTar("bazel:app", "bin/app.tar") - localDocker := docker.NewLocalDaemon(&testutil.FakeAPIClient{}, nil, false, nil) + builder := NewArtifactBuilder(fakeLocalDaemon(), nil, false) + artifact := &latest.Artifact{ Workspace: ".", ArtifactType: latest.ArtifactType{ @@ -42,8 +43,6 @@ func TestBuildBazel(t *testing.T) { }, }, } - - builder := NewArtifactBuilder(localDocker, nil, false) _, err := builder.Build(context.Background(), ioutil.Discard, artifact, "img:tag") t.CheckNoError(err) @@ -52,6 +51,8 @@ func TestBuildBazel(t *testing.T) { func TestBuildBazelFailInvalidTarget(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { + builder := NewArtifactBuilder(nil, nil, false) + artifact := &latest.Artifact{ ArtifactType: latest.ArtifactType{ BazelArtifact: &latest.BazelArtifact{ @@ -59,8 +60,6 @@ func TestBuildBazelFailInvalidTarget(t *testing.T) { }, }, } - - builder := NewArtifactBuilder(nil, nil, false) _, err := builder.Build(context.Background(), ioutil.Discard, artifact, "img:tag") t.CheckErrorContains("the bazel build target should end with .tar", err) @@ -98,3 +97,7 @@ func TestBuildImageTag(t *testing.T) { testutil.CheckDeepEqual(t, "bazel:skaffold_example", imageTag) } + +func fakeLocalDaemon() docker.LocalDaemon { + return docker.NewLocalDaemon(&testutil.FakeAPIClient{}, nil, false, nil) +} diff --git a/pkg/skaffold/build/buildpacks/build_test.go b/pkg/skaffold/build/buildpacks/build_test.go index cab6b4e7f90..c39d160ab00 100644 --- a/pkg/skaffold/build/buildpacks/build_test.go +++ b/pkg/skaffold/build/buildpacks/build_test.go @@ -258,7 +258,7 @@ value = "VALUE2" Add(test.artifact.BuildpackArtifact.Builder, "builderImageID"). Add(test.artifact.BuildpackArtifact.RunImage, "runImageID"). Add("img:latest", "builtImageID") - localDocker := docker.NewLocalDaemon(test.api, nil, false, nil) + localDocker := fakeLocalDaemon(test.api) builder := NewArtifactBuilder(localDocker, test.pushImages, test.mode) _, err := builder.Build(context.Background(), ioutil.Discard, test.artifact, test.tag) diff --git a/pkg/skaffold/build/buildpacks/fetcher_test.go b/pkg/skaffold/build/buildpacks/fetcher_test.go index 29e58d30558..d838b4a9747 100644 --- a/pkg/skaffold/build/buildpacks/fetcher_test.go +++ b/pkg/skaffold/build/buildpacks/fetcher_test.go @@ -21,6 +21,8 @@ import ( "context" "testing" + "github.com/docker/docker/client" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/testutil" ) @@ -45,7 +47,7 @@ func TestFetcher(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { api := &testutil.FakeAPIClient{} - docker := docker.NewLocalDaemon(api, nil, false, nil) + docker := fakeLocalDaemon(api) var out bytes.Buffer @@ -56,3 +58,7 @@ func TestFetcher(t *testing.T) { }) } } + +func fakeLocalDaemon(api client.CommonAPIClient) docker.LocalDaemon { + return docker.NewLocalDaemon(api, nil, false, nil) +} diff --git a/pkg/skaffold/build/cache/lookup_test.go b/pkg/skaffold/build/cache/lookup_test.go index ea20f8c7942..53a0c24ad5e 100644 --- a/pkg/skaffold/build/cache/lookup_test.go +++ b/pkg/skaffold/build/cache/lookup_test.go @@ -22,6 +22,8 @@ import ( "reflect" "testing" + "github.com/docker/docker/client" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/testutil" @@ -106,7 +108,7 @@ func TestLookupLocal(t *testing.T) { cache := &cache{ imagesAreLocal: true, artifactCache: test.cache, - client: docker.NewLocalDaemon(test.api, nil, false, nil), + client: fakeLocalDaemon(test.api), hashForArtifact: test.hasher, } details := cache.lookupArtifacts(context.Background(), map[string]string{"artifact": "tag"}, []*latest.Artifact{{ @@ -190,7 +192,7 @@ func TestLookupRemote(t *testing.T) { cache := &cache{ imagesAreLocal: false, artifactCache: test.cache, - client: docker.NewLocalDaemon(test.api, nil, false, nil), + client: fakeLocalDaemon(test.api), hashForArtifact: test.hasher, } details := cache.lookupArtifacts(context.Background(), map[string]string{"artifact": "tag"}, []*latest.Artifact{{ @@ -216,3 +218,7 @@ func failingHasher(errMessage string) func(context.Context, *latest.Artifact) (s return "", errors.New(errMessage) } } + +func fakeLocalDaemon(api client.CommonAPIClient) docker.LocalDaemon { + return docker.NewLocalDaemon(api, nil, false, nil) +} diff --git a/pkg/skaffold/build/cache/retrieve_test.go b/pkg/skaffold/build/cache/retrieve_test.go index 173713f8c46..5cdf98d1d63 100644 --- a/pkg/skaffold/build/cache/retrieve_test.go +++ b/pkg/skaffold/build/cache/retrieve_test.go @@ -111,7 +111,7 @@ func TestCacheBuildLocal(t *testing.T) { // Mock Docker t.Override(&docker.DefaultAuthHelper, stubAuth{}) - dockerDaemon := docker.NewLocalDaemon(&testutil.FakeAPIClient{}, nil, false, nil) + dockerDaemon := fakeLocalDaemon(&testutil.FakeAPIClient{}) t.Override(&docker.NewAPIClient, func(docker.Config) (docker.LocalDaemon, error) { return dockerDaemon, nil }) @@ -195,7 +195,7 @@ func TestCacheBuildRemote(t *testing.T) { }) // Mock Docker - dockerDaemon := docker.NewLocalDaemon(&testutil.FakeAPIClient{}, nil, false, nil) + dockerDaemon := fakeLocalDaemon(&testutil.FakeAPIClient{}) t.Override(&docker.NewAPIClient, func(docker.Config) (docker.LocalDaemon, error) { return dockerDaemon, nil }) diff --git a/pkg/skaffold/build/jib/gradle_test.go b/pkg/skaffold/build/jib/gradle_test.go index f9c1abe8fb2..74bc22aa4bc 100644 --- a/pkg/skaffold/build/jib/gradle_test.go +++ b/pkg/skaffold/build/jib/gradle_test.go @@ -73,7 +73,7 @@ func TestBuildJibGradleToDocker(t *testing.T) { t.Override(&gradleBuildArgsFunc, getGradleBuildArgsFuncFake(t, MinimumJibGradleVersion)) t.Override(&util.DefaultExecCommand, test.commands) api := (&testutil.FakeAPIClient{}).Add("img:tag", "imageID") - localDocker := docker.NewLocalDaemon(api, nil, false, nil) + localDocker := fakeLocalDaemon(api) builder := NewArtifactBuilder(localDocker, nil, false, false) result, err := builder.Build(context.Background(), ioutil.Discard, &latest.Artifact{ @@ -137,7 +137,7 @@ func TestBuildJibGradleToRegistry(t *testing.T) { } return "", errors.New("unknown remote tag") }) - localDocker := docker.NewLocalDaemon(&testutil.FakeAPIClient{}, nil, false, nil) + localDocker := fakeLocalDaemon(&testutil.FakeAPIClient{}) builder := NewArtifactBuilder(localDocker, nil, true, false) result, err := builder.Build(context.Background(), ioutil.Discard, &latest.Artifact{ diff --git a/pkg/skaffold/build/jib/jib_test.go b/pkg/skaffold/build/jib/jib_test.go index 45a29f35e1a..3ffc28d7d37 100644 --- a/pkg/skaffold/build/jib/jib_test.go +++ b/pkg/skaffold/build/jib/jib_test.go @@ -22,6 +22,9 @@ import ( "path/filepath" "testing" + "github.com/docker/docker/client" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/testutil" @@ -218,3 +221,7 @@ func TestGetProjectKey(t *testing.T) { testutil.CheckDeepEqual(t, test.expected, projectKey) } } + +func fakeLocalDaemon(api client.CommonAPIClient) docker.LocalDaemon { + return docker.NewLocalDaemon(api, nil, false, nil) +} diff --git a/pkg/skaffold/build/jib/maven_test.go b/pkg/skaffold/build/jib/maven_test.go index f10e406d958..6e37781e61c 100644 --- a/pkg/skaffold/build/jib/maven_test.go +++ b/pkg/skaffold/build/jib/maven_test.go @@ -73,7 +73,7 @@ func TestBuildJibMavenToDocker(t *testing.T) { t.NewTempDir().Touch("pom.xml").Chdir() t.Override(&util.DefaultExecCommand, test.commands) api := (&testutil.FakeAPIClient{}).Add("img:tag", "imageID") - localDocker := docker.NewLocalDaemon(api, nil, false, nil) + localDocker := fakeLocalDaemon(api) builder := NewArtifactBuilder(localDocker, nil, false, false) result, err := builder.Build(context.Background(), ioutil.Discard, &latest.Artifact{ @@ -133,7 +133,7 @@ func TestBuildJibMavenToRegistry(t *testing.T) { } return "", errors.New("unknown remote tag") }) - localDocker := docker.NewLocalDaemon(&testutil.FakeAPIClient{}, nil, false, nil) + localDocker := fakeLocalDaemon(&testutil.FakeAPIClient{}) builder := NewArtifactBuilder(localDocker, nil, true, false) result, err := builder.Build(context.Background(), ioutil.Discard, &latest.Artifact{ diff --git a/pkg/skaffold/build/local/docker_test.go b/pkg/skaffold/build/local/docker_test.go index 9fb1389d94d..553473e89b5 100644 --- a/pkg/skaffold/build/local/docker_test.go +++ b/pkg/skaffold/build/local/docker_test.go @@ -23,6 +23,8 @@ import ( "path/filepath" "testing" + "github.com/docker/docker/client" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/cluster" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" @@ -86,7 +88,7 @@ func TestDockerCLIBuild(t *testing.T) { t.Override(&cluster.GetClient, func() cluster.Client { return fakeMinikubeClient{} }) t.Override(&util.OSEnviron, func() []string { return []string{"KEY=VALUE"} }) t.Override(&docker.NewAPIClient, func(docker.Config) (docker.LocalDaemon, error) { - return docker.NewLocalDaemon(&testutil.FakeAPIClient{}, test.extraEnv, false, nil), nil + return fakeLocalDaemonWithExtraEnv(test.extraEnv), nil }) builder, err := NewBuilder(&mockConfig{ @@ -109,6 +111,14 @@ func TestDockerCLIBuild(t *testing.T) { } } +func fakeLocalDaemon(api client.CommonAPIClient) docker.LocalDaemon { + return docker.NewLocalDaemon(api, nil, false, nil) +} + +func fakeLocalDaemonWithExtraEnv(extraEnv []string) docker.LocalDaemon { + return docker.NewLocalDaemon(&testutil.FakeAPIClient{}, extraEnv, false, nil) +} + type fakeMinikubeClient struct{} func (fakeMinikubeClient) IsMinikube(kubeContext string) bool { return false } diff --git a/pkg/skaffold/build/local/local_test.go b/pkg/skaffold/build/local/local_test.go index ffaa2f3c1b8..100bd6759a4 100644 --- a/pkg/skaffold/build/local/local_test.go +++ b/pkg/skaffold/build/local/local_test.go @@ -233,7 +233,7 @@ func TestLocalRun(t *testing.T) { fakeWarner := &warnings.Collect{} t.Override(&warnings.Printf, fakeWarner.Warnf) t.Override(&docker.NewAPIClient, func(docker.Config) (docker.LocalDaemon, error) { - return docker.NewLocalDaemon(test.api, nil, false, nil), nil + return fakeLocalDaemon(test.api), nil }) t.Override(&docker.EvalBuildArgs, func(mode config.RunMode, workspace string, a *latest.DockerArtifact) (map[string]*string, error) { return a.BuildArgs, nil diff --git a/pkg/skaffold/test/test_test.go b/pkg/skaffold/test/test_test.go index 7460b04ad7f..4aa068bac30 100644 --- a/pkg/skaffold/test/test_test.go +++ b/pkg/skaffold/test/test_test.go @@ -25,6 +25,8 @@ import ( "path/filepath" "testing" + "github.com/docker/docker/client" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" @@ -154,7 +156,7 @@ func TestTestSuccessRemoteImage(t *testing.T) { t.NewTempDir().Touch("test.yaml").Chdir() t.Override(&util.DefaultExecCommand, testutil.CmdRun("container-structure-test test -v warn --image image:tag --config test.yaml")) t.Override(&docker.NewAPIClient, func(docker.Config) (docker.LocalDaemon, error) { - return docker.NewLocalDaemon(&testutil.FakeAPIClient{}, nil, false, nil), nil + return fakeLocalDaemon(&testutil.FakeAPIClient{}), nil }) cfg := &mockConfig{ @@ -179,7 +181,7 @@ func TestTestFailureRemoteImage(t *testing.T) { t.NewTempDir().Touch("test.yaml").Chdir() t.Override(&util.DefaultExecCommand, testutil.CmdRun("container-structure-test test -v warn --image image:tag --config test.yaml")) t.Override(&docker.NewAPIClient, func(docker.Config) (docker.LocalDaemon, error) { - return docker.NewLocalDaemon(&testutil.FakeAPIClient{ErrImagePull: true}, nil, false, nil), nil + return fakeLocalDaemon(&testutil.FakeAPIClient{ErrImagePull: true}), nil }) cfg := &mockConfig{ @@ -252,6 +254,10 @@ func TestTestMuted(t *testing.T) { }) } +func fakeLocalDaemon(api client.CommonAPIClient) docker.LocalDaemon { + return docker.NewLocalDaemon(api, nil, false, nil) +} + type mockConfig struct { runcontext.RunContext // Embedded to provide the default values. workingDir string From 4cccbd62ad65eaeccf1bdb54edcf07c72f30a5fc Mon Sep 17 00:00:00 2001 From: David Gageot Date: Mon, 7 Sep 2020 10:19:51 +0200 Subject: [PATCH 133/138] Pass a context to DefaultAuthHelper.GetAllConfigs() (#4760) * Pass a context to DefaultAuthHelper.GetAllConfigs() Signed-off-by: David Gageot * Add tests Signed-off-by: David Gageot --- pkg/skaffold/build/cache/retrieve_test.go | 8 +++- pkg/skaffold/build/local/local_test.go | 4 +- pkg/skaffold/docker/auth.go | 29 ++++++++++++-- pkg/skaffold/docker/auth_test.go | 46 ++++++++++++++++++++++- pkg/skaffold/docker/image.go | 23 ++---------- 5 files changed, 82 insertions(+), 28 deletions(-) diff --git a/pkg/skaffold/build/cache/retrieve_test.go b/pkg/skaffold/build/cache/retrieve_test.go index 5cdf98d1d63..3c6c293ac4a 100644 --- a/pkg/skaffold/build/cache/retrieve_test.go +++ b/pkg/skaffold/build/cache/retrieve_test.go @@ -85,8 +85,12 @@ func (b *mockBuilder) BuildAndTest(ctx context.Context, out io.Writer, tags tag. type stubAuth struct{} -func (t stubAuth) GetAuthConfig(string) (types.AuthConfig, error) { return types.AuthConfig{}, nil } -func (t stubAuth) GetAllAuthConfigs() (map[string]types.AuthConfig, error) { return nil, nil } +func (t stubAuth) GetAuthConfig(string) (types.AuthConfig, error) { + return types.AuthConfig{}, nil +} +func (t stubAuth) GetAllAuthConfigs(context.Context) (map[string]types.AuthConfig, error) { + return nil, nil +} func TestCacheBuildLocal(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { diff --git a/pkg/skaffold/build/local/local_test.go b/pkg/skaffold/build/local/local_test.go index 100bd6759a4..a765d4085e8 100644 --- a/pkg/skaffold/build/local/local_test.go +++ b/pkg/skaffold/build/local/local_test.go @@ -43,7 +43,9 @@ type testAuthHelper struct{} func (t testAuthHelper) GetAuthConfig(string) (types.AuthConfig, error) { return types.AuthConfig{}, nil } -func (t testAuthHelper) GetAllAuthConfigs() (map[string]types.AuthConfig, error) { return nil, nil } +func (t testAuthHelper) GetAllAuthConfigs(context.Context) (map[string]types.AuthConfig, error) { + return nil, nil +} func TestLocalRun(t *testing.T) { tests := []struct { diff --git a/pkg/skaffold/docker/auth.go b/pkg/skaffold/docker/auth.go index cd94b2e9df4..96f106948d5 100644 --- a/pkg/skaffold/docker/auth.go +++ b/pkg/skaffold/docker/auth.go @@ -57,7 +57,7 @@ func init() { // Ideally this shouldn't be public, but the LocalBuilder needs to use it. type AuthConfigHelper interface { GetAuthConfig(registry string) (types.AuthConfig, error) - GetAllAuthConfigs() (map[string]types.AuthConfig, error) + GetAllAuthConfigs(ctx context.Context) (map[string]types.AuthConfig, error) } type credsHelper struct{} @@ -73,7 +73,7 @@ func loadDockerConfig() (*configfile.ConfigFile, error) { return cf, nil } -func (credsHelper) GetAuthConfig(registry string) (types.AuthConfig, error) { +func (h credsHelper) GetAuthConfig(registry string) (types.AuthConfig, error) { cf, err := loadDockerConfig() if err != nil { return types.AuthConfig{}, err @@ -87,7 +87,30 @@ func (credsHelper) GetAuthConfig(registry string) (types.AuthConfig, error) { return types.AuthConfig(auth), nil } -func (credsHelper) GetAllAuthConfigs() (map[string]types.AuthConfig, error) { +// GetAllAuthConfigs retrieves all the auth configs. +// Because this can take a long time, we make sure it can be interrupted by the user. +func (h credsHelper) GetAllAuthConfigs(ctx context.Context) (map[string]types.AuthConfig, error) { + type result struct { + configs map[string]types.AuthConfig + err error + } + + auth := make(chan result) + + go func() { + configs, err := h.doGetAllAuthConfigs() + auth <- result{configs, err} + }() + + select { + case <-ctx.Done(): + return nil, ctx.Err() + case r := <-auth: + return r.configs, r.err + } +} + +func (h credsHelper) doGetAllAuthConfigs() (map[string]types.AuthConfig, error) { cf, err := loadDockerConfig() if err != nil { return nil, err diff --git a/pkg/skaffold/docker/auth_test.go b/pkg/skaffold/docker/auth_test.go index 01f905286bb..d6f1e55edce 100644 --- a/pkg/skaffold/docker/auth_test.go +++ b/pkg/skaffold/docker/auth_test.go @@ -19,6 +19,7 @@ package docker import ( "context" "fmt" + "runtime" "testing" "github.com/docker/docker/api/types" @@ -44,11 +45,52 @@ var allAuthConfig = map[string]types.AuthConfig{ func (t testAuthHelper) GetAuthConfig(string) (types.AuthConfig, error) { return gcrAuthConfig, t.getAuthConfigErr } - -func (t testAuthHelper) GetAllAuthConfigs() (map[string]types.AuthConfig, error) { +func (t testAuthHelper) GetAllAuthConfigs(context.Context) (map[string]types.AuthConfig, error) { return allAuthConfig, t.getAllAuthConfigsErr } +func TestGetAllAuthConfigs(t *testing.T) { + testutil.Run(t, "auto-configure gcr.io", func(t *testutil.T) { + if runtime.GOOS == "windows" { + t.Skip("test doesn't work on windows") + } + + tmpDir := t.NewTempDir(). + Write("config.json", `{"credHelpers":{"my.registry":"helper"}}`). + Write("docker-credential-gcloud", `#!/bin/sh +read server +echo "{\"Username\":\"\",\"Secret\":\"TOKEN_$server\"}"`). + Write("docker-credential-helper", `#!/bin/sh +read server +echo "{\"Username\":\"\",\"Secret\":\"TOKEN_$server\"}"`) + t.Override(&configDir, tmpDir.Root()) + t.SetEnvs(map[string]string{"PATH": tmpDir.Root()}) + + auth, err := DefaultAuthHelper.GetAllAuthConfigs(context.Background()) + + t.CheckNoError(err) + t.CheckDeepEqual(map[string]types.AuthConfig{ + "asia.gcr.io": {IdentityToken: "TOKEN_asia.gcr.io"}, + "eu.gcr.io": {IdentityToken: "TOKEN_eu.gcr.io"}, + "gcr.io": {IdentityToken: "TOKEN_gcr.io"}, + "my.registry": {IdentityToken: "TOKEN_my.registry"}, + "marketplace.gcr.io": {IdentityToken: "TOKEN_marketplace.gcr.io"}, + "staging-k8s.gcr.io": {IdentityToken: "TOKEN_staging-k8s.gcr.io"}, + "us.gcr.io": {IdentityToken: "TOKEN_us.gcr.io"}, + }, auth) + }) + + testutil.Run(t, "invalid config.json", func(t *testutil.T) { + tmpDir := t.NewTempDir().Write("config.json", "invalid json") + t.Override(&configDir, tmpDir.Root()) + + auth, err := DefaultAuthHelper.GetAllAuthConfigs(context.Background()) + + t.CheckError(true, err) + t.CheckEmpty(auth) + }) +} + func TestGetEncodedRegistryAuth(t *testing.T) { tests := []struct { description string diff --git a/pkg/skaffold/docker/image.go b/pkg/skaffold/docker/image.go index 33262bc35d6..3d925c45168 100644 --- a/pkg/skaffold/docker/image.go +++ b/pkg/skaffold/docker/image.go @@ -156,25 +156,6 @@ func (l *localDaemon) ConfigFile(ctx context.Context, image string) (*v1.ConfigF return cfg, nil } -func authConfig(ctx context.Context) map[string]types.AuthConfig { - auth := make(chan map[string]types.AuthConfig) - - go func() { - // Like `docker build`, we ignore the errors - // See https://github.com/docker/cli/blob/75c1bb1f33d7cedbaf48404597d5bf9818199480/cli/command/image/build.go#L364 - authConfigs, _ := DefaultAuthHelper.GetAllAuthConfigs() - auth <- authConfigs - }() - - // Because this can take a long time, we make sure it can be interrupted by the user. - select { - case <-ctx.Done(): - return nil - case r := <-auth: - return r - } -} - // Build performs a docker build and returns the imageID. func (l *localDaemon) Build(ctx context.Context, out io.Writer, workspace string, a *latest.DockerArtifact, ref string, mode config.RunMode) (string, error) { logrus.Debugf("Running docker build: context: %s, dockerfile: %s", workspace, a.DockerfilePath) @@ -184,7 +165,9 @@ func (l *localDaemon) Build(ctx context.Context, out io.Writer, workspace string return "", fmt.Errorf("unable to evaluate build args: %w", err) } - authConfigs := authConfig(ctx) + // Like `docker build`, we ignore the errors + // See https://github.com/docker/cli/blob/75c1bb1f33d7cedbaf48404597d5bf9818199480/cli/command/image/build.go#L364 + authConfigs, _ := DefaultAuthHelper.GetAllAuthConfigs(ctx) buildCtx, buildCtxWriter := io.Pipe() go func() { From 5b1f94025b260398f3f354feee348c90ea2e3862 Mon Sep 17 00:00:00 2001 From: Andrey Shlykov Date: Tue, 8 Sep 2020 17:31:21 +0300 Subject: [PATCH 134/138] Prepare v2beta8 (#4762) --- docs/config.toml | 2 +- docs/content/en/docs/references/cli/_index.md | 2 +- docs/content/en/schemas/v2beta8.json | 2458 +++++++++++++++++ integration/examples/bazel/skaffold.yaml | 2 +- .../examples/buildpacks-java/skaffold.yaml | 2 +- .../examples/buildpacks-node/skaffold.yaml | 2 +- .../examples/buildpacks-python/skaffold.yaml | 2 +- integration/examples/buildpacks/skaffold.yaml | 2 +- integration/examples/custom/skaffold.yaml | 2 +- integration/examples/gcb-kaniko/skaffold.yaml | 2 +- .../examples/generate-pipeline/skaffold.yaml | 2 +- .../getting-started-kustomize/skaffold.yaml | 2 +- .../examples/getting-started/skaffold.yaml | 2 +- .../examples/google-cloud-build/skaffold.yaml | 2 +- .../skaffold.yaml | 2 +- .../examples/helm-deployment/skaffold.yaml | 2 +- integration/examples/hot-reload/skaffold.yaml | 2 +- integration/examples/jib-gradle/skaffold.yaml | 2 +- .../examples/jib-multimodule/skaffold.yaml | 2 +- .../examples/jib-sync/skaffold-gradle.yaml | 2 +- .../examples/jib-sync/skaffold-maven.yaml | 2 +- integration/examples/jib/skaffold.yaml | 2 +- integration/examples/kaniko/skaffold.yaml | 2 +- .../kustomize/skaffold-kustomize-args.yaml | 2 +- integration/examples/kustomize/skaffold.yaml | 2 +- .../examples/microservices/skaffold.yaml | 2 +- integration/examples/nodejs/skaffold.yaml | 2 +- .../examples/profile-patches/skaffold.yaml | 2 +- integration/examples/profiles/skaffold.yaml | 2 +- .../examples/react-reload/skaffold.yaml | 2 +- integration/examples/ruby/skaffold.yaml | 2 +- .../examples/structure-tests/skaffold.yaml | 2 +- .../skaffold.yaml | 2 +- .../examples/templated-fields/skaffold.yaml | 2 +- integration/testdata/build/skaffold.yaml | 2 +- integration/testdata/debug/skaffold.yaml | 2 +- .../testdata/deploy-multiple/skaffold.yaml | 2 +- integration/testdata/dev/skaffold.yaml | 2 +- .../testdata/gke_loadbalancer/skaffold.yaml | 2 +- integration/testdata/hello/skaffold.yaml | 2 +- .../testdata/init/compose/skaffold.yaml | 2 +- integration/testdata/jib/skaffold.yaml | 2 +- .../kaniko-explicit-repo/skaffold.yaml | 2 +- .../app/skaffold.yaml | 2 +- .../kaniko-insecure-registry/skaffold.yaml | 2 +- .../kaniko-microservices/skaffold.yaml | 2 +- .../testdata/kaniko-sub-folder/skaffold.yaml | 2 +- .../testdata/kaniko-target/skaffold.yaml | 2 +- integration/testdata/tagPolicy/skaffold.yaml | 2 +- .../testdata/init/allcli/skaffold.yaml | 2 +- .../getting-started-kustomize/skaffold.yaml | 2 +- .../init/hello-no-manifest/skaffold.yaml | 2 +- .../testdata/init/hello/skaffold.yaml | 2 +- .../testdata/init/ignore-tags/skaffold.yaml | 2 +- .../testdata/init/microservices/skaffold.yaml | 2 +- pkg/skaffold/schema/latest/config.go | 4 +- pkg/skaffold/schema/v2beta6/upgrade.go | 2 +- pkg/skaffold/schema/v2beta6/upgrade_test.go | 2 +- pkg/skaffold/schema/v2beta7/config.go | 1030 +++++++ pkg/skaffold/schema/v2beta7/upgrade.go | 38 + pkg/skaffold/schema/v2beta7/upgrade_test.go | 182 ++ pkg/skaffold/schema/versions.go | 2 + 62 files changed, 3768 insertions(+), 58 deletions(-) create mode 100755 docs/content/en/schemas/v2beta8.json create mode 100755 pkg/skaffold/schema/v2beta7/config.go create mode 100755 pkg/skaffold/schema/v2beta7/upgrade.go create mode 100755 pkg/skaffold/schema/v2beta7/upgrade_test.go diff --git a/docs/config.toml b/docs/config.toml index fc954c20213..b3b36e4b7c5 100644 --- a/docs/config.toml +++ b/docs/config.toml @@ -82,7 +82,7 @@ weight = 1 copyright = "Skaffold Authors" privacy_policy = "https://policies.google.com/privacy" github_repo = "https://github.com/GoogleContainerTools/skaffold" -skaffold_version = "skaffold/v2beta7" +skaffold_version = "skaffold/v2beta8" # Google Custom Search Engine ID. Remove or comment out to disable search. gcs_engine_id = "013756393218025596041:3nojel67sum" diff --git a/docs/content/en/docs/references/cli/_index.md b/docs/content/en/docs/references/cli/_index.md index b53c778a87f..2432c36a463 100644 --- a/docs/content/en/docs/references/cli/_index.md +++ b/docs/content/en/docs/references/cli/_index.md @@ -674,7 +674,7 @@ Examples: Options: -f, --filename='skaffold.yaml': Path or URL to the Skaffold config file --overwrite=false: Overwrite original config with fixed config - --version='skaffold/v2beta7': Target schema version to upgrade to + --version='skaffold/v2beta8': Target schema version to upgrade to Usage: skaffold fix [options] diff --git a/docs/content/en/schemas/v2beta8.json b/docs/content/en/schemas/v2beta8.json new file mode 100755 index 00000000000..30c4d0bc965 --- /dev/null +++ b/docs/content/en/schemas/v2beta8.json @@ -0,0 +1,2458 @@ +{ + "type": "object", + "anyOf": [ + { + "$ref": "#/definitions/SkaffoldConfig" + } + ], + "$schema": "http://json-schema-org/draft-07/schema#", + "definitions": { + "Activation": { + "properties": { + "command": { + "type": "string", + "description": "a Skaffold command for which the profile is auto-activated.", + "x-intellij-html-description": "a Skaffold command for which the profile is auto-activated.", + "examples": [ + "dev" + ] + }, + "env": { + "type": "string", + "description": "a `key=pattern` pair. The profile is auto-activated if an Environment Variable `key` matches the pattern. If the pattern starts with `!`, activation happens if the remaining pattern is _not_ matched. The pattern matches if the Environment Variable value is exactly `pattern`, or the regex `pattern` is found in it. An empty `pattern` (e.g. `env: \"key=\"`) always only matches if the Environment Variable is undefined or empty.", + "x-intellij-html-description": "a key=pattern pair. The profile is auto-activated if an Environment Variable key matches the pattern. If the pattern starts with !, activation happens if the remaining pattern is not matched. The pattern matches if the Environment Variable value is exactly pattern, or the regex pattern is found in it. An empty pattern (e.g. env: "key=") always only matches if the Environment Variable is undefined or empty.", + "examples": [ + "ENV=production" + ] + }, + "kubeContext": { + "type": "string", + "description": "a Kubernetes context for which the profile is auto-activated.", + "x-intellij-html-description": "a Kubernetes context for which the profile is auto-activated.", + "examples": [ + "minikube" + ] + } + }, + "preferredOrder": [ + "env", + "kubeContext", + "command" + ], + "additionalProperties": false, + "description": "criteria by which a profile is auto-activated.", + "x-intellij-html-description": "criteria by which a profile is auto-activated." + }, + "Artifact": { + "required": [ + "image" + ], + "anyOf": [ + { + "properties": { + "context": { + "type": "string", + "description": "directory containing the artifact's sources.", + "x-intellij-html-description": "directory containing the artifact's sources.", + "default": "." + }, + "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": { + "$ref": "#/definitions/Sync", + "description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "x-intellij-html-description": "beta local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "default": "infer: [\"**/*\"]" + } + }, + "preferredOrder": [ + "image", + "context", + "sync" + ], + "additionalProperties": false + }, + { + "properties": { + "context": { + "type": "string", + "description": "directory containing the artifact's sources.", + "x-intellij-html-description": "directory containing the artifact's sources.", + "default": "." + }, + "docker": { + "$ref": "#/definitions/DockerArtifact", + "description": "*beta* describes an artifact built from a Dockerfile.", + "x-intellij-html-description": "beta describes an artifact built from a Dockerfile." + }, + "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": { + "$ref": "#/definitions/Sync", + "description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "x-intellij-html-description": "beta local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "default": "infer: [\"**/*\"]" + } + }, + "preferredOrder": [ + "image", + "context", + "sync", + "docker" + ], + "additionalProperties": false + }, + { + "properties": { + "bazel": { + "$ref": "#/definitions/BazelArtifact", + "description": "*beta* requires bazel CLI to be installed and the sources to contain [Bazel](https://bazel.build/) configuration files.", + "x-intellij-html-description": "beta requires bazel CLI to be installed and the sources to contain Bazel configuration files." + }, + "context": { + "type": "string", + "description": "directory containing the artifact's sources.", + "x-intellij-html-description": "directory containing the artifact's sources.", + "default": "." + }, + "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": { + "$ref": "#/definitions/Sync", + "description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "x-intellij-html-description": "beta local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "default": "infer: [\"**/*\"]" + } + }, + "preferredOrder": [ + "image", + "context", + "sync", + "bazel" + ], + "additionalProperties": false + }, + { + "properties": { + "context": { + "type": "string", + "description": "directory containing the artifact's sources.", + "x-intellij-html-description": "directory containing the artifact's sources.", + "default": "." + }, + "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" + ] + }, + "jib": { + "$ref": "#/definitions/JibArtifact", + "description": "builds images using the [Jib plugins for Maven or Gradle](https://github.com/GoogleContainerTools/jib/).", + "x-intellij-html-description": "builds images using the Jib plugins for Maven or Gradle." + }, + "sync": { + "$ref": "#/definitions/Sync", + "description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "x-intellij-html-description": "beta local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "default": "infer: [\"**/*\"]" + } + }, + "preferredOrder": [ + "image", + "context", + "sync", + "jib" + ], + "additionalProperties": false + }, + { + "properties": { + "context": { + "type": "string", + "description": "directory containing the artifact's sources.", + "x-intellij-html-description": "directory containing the artifact's sources.", + "default": "." + }, + "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" + ] + }, + "kaniko": { + "$ref": "#/definitions/KanikoArtifact", + "description": "builds images using [kaniko](https://github.com/GoogleContainerTools/kaniko).", + "x-intellij-html-description": "builds images using kaniko." + }, + "sync": { + "$ref": "#/definitions/Sync", + "description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "x-intellij-html-description": "beta local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "default": "infer: [\"**/*\"]" + } + }, + "preferredOrder": [ + "image", + "context", + "sync", + "kaniko" + ], + "additionalProperties": false + }, + { + "properties": { + "buildpacks": { + "$ref": "#/definitions/BuildpackArtifact", + "description": "builds images using [Cloud Native Buildpacks](https://buildpacks.io/).", + "x-intellij-html-description": "builds images using Cloud Native Buildpacks." + }, + "context": { + "type": "string", + "description": "directory containing the artifact's sources.", + "x-intellij-html-description": "directory containing the artifact's sources.", + "default": "." + }, + "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": { + "$ref": "#/definitions/Sync", + "description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "x-intellij-html-description": "beta local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "default": "infer: [\"**/*\"]" + } + }, + "preferredOrder": [ + "image", + "context", + "sync", + "buildpacks" + ], + "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": "*beta* builds images using a custom build script written by the user.", + "x-intellij-html-description": "beta 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": { + "$ref": "#/definitions/Sync", + "description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "x-intellij-html-description": "beta local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.", + "default": "infer: [\"**/*\"]" + } + }, + "preferredOrder": [ + "image", + "context", + "sync", + "custom" + ], + "additionalProperties": false + } + ], + "description": "items that need to be built, along with the context in which they should be built.", + "x-intellij-html-description": "items that need to be built, along with the context in which they should be built." + }, + "Auto": { + "description": "cannot be customized.", + "x-intellij-html-description": "cannot be customized." + }, + "BazelArtifact": { + "required": [ + "target" + ], + "properties": { + "args": { + "items": { + "type": "string" + }, + "type": "array", + "description": "additional args to pass to `bazel build`.", + "x-intellij-html-description": "additional args to pass to bazel build.", + "default": "[]", + "examples": [ + "[\"-flag\", \"--otherflag\"]" + ] + }, + "target": { + "type": "string", + "description": "`bazel build` target to run.", + "x-intellij-html-description": "bazel build target to run.", + "examples": [ + "//:skaffold_example.tar" + ] + } + }, + "preferredOrder": [ + "target", + "args" + ], + "additionalProperties": false, + "description": "describes an artifact built with [Bazel](https://bazel.build/).", + "x-intellij-html-description": "describes an artifact built with Bazel." + }, + "BuildConfig": { + "anyOf": [ + { + "properties": { + "artifacts": { + "items": { + "$ref": "#/definitions/Artifact" + }, + "type": "array", + "description": "the images you're going to be building.", + "x-intellij-html-description": "the images you're going to be building." + }, + "insecureRegistries": { + "items": { + "type": "string" + }, + "type": "array", + "description": "a list of registries declared by the user to be insecure. These registries will be connected to via HTTP instead of HTTPS.", + "x-intellij-html-description": "a list of registries declared by the user to be insecure. These registries will be connected to via HTTP instead of HTTPS.", + "default": "[]" + }, + "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: {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": [ + "artifacts", + "insecureRegistries", + "tagPolicy" + ], + "additionalProperties": false + }, + { + "properties": { + "artifacts": { + "items": { + "$ref": "#/definitions/Artifact" + }, + "type": "array", + "description": "the images you're going to be building.", + "x-intellij-html-description": "the images you're going to be building." + }, + "insecureRegistries": { + "items": { + "type": "string" + }, + "type": "array", + "description": "a list of registries declared by the user to be insecure. These registries will be connected to via HTTP instead of HTTPS.", + "x-intellij-html-description": "a list of registries declared by the user to be insecure. These registries will be connected to via HTTP instead of HTTPS.", + "default": "[]" + }, + "local": { + "$ref": "#/definitions/LocalBuild", + "description": "*beta* describes how to do a build on the local docker daemon and optionally push to a repository.", + "x-intellij-html-description": "beta describes how to do a build on the local docker daemon and optionally push to a repository." + }, + "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: {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": [ + "artifacts", + "insecureRegistries", + "tagPolicy", + "local" + ], + "additionalProperties": false + }, + { + "properties": { + "artifacts": { + "items": { + "$ref": "#/definitions/Artifact" + }, + "type": "array", + "description": "the images you're going to be building.", + "x-intellij-html-description": "the images you're going to be building." + }, + "googleCloudBuild": { + "$ref": "#/definitions/GoogleCloudBuild", + "description": "*beta* describes how to do a remote build on [Google Cloud Build](https://cloud.google.com/cloud-build/).", + "x-intellij-html-description": "beta describes how to do a remote build on Google Cloud Build." + }, + "insecureRegistries": { + "items": { + "type": "string" + }, + "type": "array", + "description": "a list of registries declared by the user to be insecure. These registries will be connected to via HTTP instead of HTTPS.", + "x-intellij-html-description": "a list of registries declared by the user to be insecure. These registries will be connected to via HTTP instead of HTTPS.", + "default": "[]" + }, + "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: {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": [ + "artifacts", + "insecureRegistries", + "tagPolicy", + "googleCloudBuild" + ], + "additionalProperties": false + }, + { + "properties": { + "artifacts": { + "items": { + "$ref": "#/definitions/Artifact" + }, + "type": "array", + "description": "the images you're going to be building.", + "x-intellij-html-description": "the images you're going to be building." + }, + "cluster": { + "$ref": "#/definitions/ClusterDetails", + "description": "*beta* describes how to do an on-cluster build.", + "x-intellij-html-description": "beta describes how to do an on-cluster build." + }, + "insecureRegistries": { + "items": { + "type": "string" + }, + "type": "array", + "description": "a list of registries declared by the user to be insecure. These registries will be connected to via HTTP instead of HTTPS.", + "x-intellij-html-description": "a list of registries declared by the user to be insecure. These registries will be connected to via HTTP instead of HTTPS.", + "default": "[]" + }, + "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: {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": [ + "artifacts", + "insecureRegistries", + "tagPolicy", + "cluster" + ], + "additionalProperties": false + } + ], + "description": "contains all the configuration for the build steps.", + "x-intellij-html-description": "contains all the configuration for the build steps." + }, + "BuildpackArtifact": { + "required": [ + "builder" + ], + "properties": { + "builder": { + "type": "string", + "description": "builder image used.", + "x-intellij-html-description": "builder image used." + }, + "buildpacks": { + "items": { + "type": "string" + }, + "type": "array", + "description": "a list of strings, where each string is a specific buildpack to use with the builder. If you specify buildpacks the builder image automatic detection will be ignored. These buildpacks will be used to build the Image from your source code. Order matters.", + "x-intellij-html-description": "a list of strings, where each string is a specific buildpack to use with the builder. If you specify buildpacks the builder image automatic detection will be ignored. These buildpacks will be used to build the Image from your source code. Order matters.", + "default": "[]" + }, + "dependencies": { + "$ref": "#/definitions/BuildpackDependencies", + "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." + }, + "env": { + "items": { + "type": "string" + }, + "type": "array", + "description": "environment variables, in the `key=value` form, passed to the build. Values can use the go template syntax.", + "x-intellij-html-description": "environment variables, in the key=value form, passed to the build. Values can use the go template syntax.", + "default": "[]", + "examples": [ + "[\"key1=value1\", \"key2=value2\", \"key3={{.ENV_VARIABLE}}\"]" + ] + }, + "projectDescriptor": { + "type": "string", + "description": "path to the project descriptor file.", + "x-intellij-html-description": "path to the project descriptor file.", + "default": "project.toml" + }, + "runImage": { + "type": "string", + "description": "overrides the stack's default run image.", + "x-intellij-html-description": "overrides the stack's default run image." + }, + "trustBuilder": { + "type": "boolean", + "description": "indicates that the builder should be trusted.", + "x-intellij-html-description": "indicates that the builder should be trusted.", + "default": "false" + } + }, + "preferredOrder": [ + "builder", + "runImage", + "env", + "buildpacks", + "trustBuilder", + "projectDescriptor", + "dependencies" + ], + "additionalProperties": false, + "description": "*alpha* describes an artifact built using [Cloud Native Buildpacks](https://buildpacks.io/). It can be used to build images out of project's sources without any additional configuration.", + "x-intellij-html-description": "alpha describes an artifact built using Cloud Native Buildpacks. It can be used to build images out of project's sources without any additional configuration." + }, + "BuildpackDependencies": { + "properties": { + "ignore": { + "items": { + "type": "string" + }, + "type": "array", + "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": { + "items": { + "type": "string" + }, + "type": "array", + "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": "[]" + } + }, + "preferredOrder": [ + "paths", + "ignore" + ], + "additionalProperties": false, + "description": "*alpha* used to specify dependencies for an artifact built by buildpacks.", + "x-intellij-html-description": "alpha used to specify dependencies for an artifact built by buildpacks." + }, + "ClusterDetails": { + "properties": { + "HTTPS_PROXY": { + "type": "string", + "description": "for kaniko pod.", + "x-intellij-html-description": "for kaniko pod." + }, + "HTTP_PROXY": { + "type": "string", + "description": "for kaniko pod.", + "x-intellij-html-description": "for kaniko pod." + }, + "annotations": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "description": "describes the Kubernetes annotations for the pod.", + "x-intellij-html-description": "describes the Kubernetes annotations for the pod.", + "default": "{}" + }, + "concurrency": { + "type": "integer", + "description": "how many artifacts can be built concurrently. 0 means \"no-limit\".", + "x-intellij-html-description": "how many artifacts can be built concurrently. 0 means "no-limit".", + "default": "0" + }, + "dockerConfig": { + "$ref": "#/definitions/DockerConfig", + "description": "describes how to mount the local Docker configuration into a pod.", + "x-intellij-html-description": "describes how to mount the local Docker configuration into a pod." + }, + "namespace": { + "type": "string", + "description": "Kubernetes namespace. Defaults to current namespace in Kubernetes configuration.", + "x-intellij-html-description": "Kubernetes namespace. Defaults to current namespace in Kubernetes configuration." + }, + "pullSecretMountPath": { + "type": "string", + "description": "path the pull secret will be mounted at within the running container.", + "x-intellij-html-description": "path the pull secret will be mounted at within the running container." + }, + "pullSecretName": { + "type": "string", + "description": "name of the Kubernetes secret for pulling base images and pushing the final image. If given, the secret needs to contain the Google Cloud service account secret key under the key `kaniko-secret`.", + "x-intellij-html-description": "name of the Kubernetes secret for pulling base images and pushing the final image. If given, the secret needs to contain the Google Cloud service account secret key under the key kaniko-secret.", + "default": "kaniko-secret" + }, + "pullSecretPath": { + "type": "string", + "description": "path to the Google Cloud service account secret key file.", + "x-intellij-html-description": "path to the Google Cloud service account secret key file." + }, + "randomDockerConfigSecret": { + "type": "boolean", + "description": "adds a random UUID postfix to the default name of the docker secret to facilitate parallel builds, e.g. docker-cfgfd154022-c761-416f-8eb3-cf8258450b85.", + "x-intellij-html-description": "adds a random UUID postfix to the default name of the docker secret to facilitate parallel builds, e.g. docker-cfgfd154022-c761-416f-8eb3-cf8258450b85.", + "default": "false" + }, + "randomPullSecret": { + "type": "boolean", + "description": "adds a random UUID postfix to the default name of the pull secret to facilitate parallel builds, e.g. kaniko-secretdocker-cfgfd154022-c761-416f-8eb3-cf8258450b85.", + "x-intellij-html-description": "adds a random UUID postfix to the default name of the pull secret to facilitate parallel builds, e.g. kaniko-secretdocker-cfgfd154022-c761-416f-8eb3-cf8258450b85.", + "default": "false" + }, + "resources": { + "$ref": "#/definitions/ResourceRequirements", + "description": "define the resource requirements for the kaniko pod.", + "x-intellij-html-description": "define the resource requirements for the kaniko pod." + }, + "runAsUser": { + "type": "integer", + "description": "defines the UID to request for running the container. If omitted, no SeurityContext will be specified for the pod and will therefore be inherited from the service account.", + "x-intellij-html-description": "defines the UID to request for running the container. If omitted, no SeurityContext will be specified for the pod and will therefore be inherited from the service account." + }, + "serviceAccount": { + "type": "string", + "description": "describes the Kubernetes service account to use for the pod. Defaults to 'default'.", + "x-intellij-html-description": "describes the Kubernetes service account to use for the pod. Defaults to 'default'." + }, + "timeout": { + "type": "string", + "description": "amount of time (in seconds) that this build is allowed to run. Defaults to 20 minutes (`20m`).", + "x-intellij-html-description": "amount of time (in seconds) that this build is allowed to run. Defaults to 20 minutes (20m)." + }, + "tolerations": { + "items": {}, + "type": "array", + "description": "describes the Kubernetes tolerations for the pod.", + "x-intellij-html-description": "describes the Kubernetes tolerations for the pod.", + "default": "[]" + }, + "volumes": { + "items": {}, + "type": "array", + "description": "defines container mounts for ConfigMap and Secret resources.", + "x-intellij-html-description": "defines container mounts for ConfigMap and Secret resources.", + "default": "[]" + } + }, + "preferredOrder": [ + "HTTP_PROXY", + "HTTPS_PROXY", + "pullSecretPath", + "pullSecretName", + "pullSecretMountPath", + "namespace", + "timeout", + "dockerConfig", + "serviceAccount", + "tolerations", + "annotations", + "runAsUser", + "resources", + "concurrency", + "volumes", + "randomPullSecret", + "randomDockerConfigSecret" + ], + "additionalProperties": false, + "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 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": [ + "buildCommand", + "dependencies" + ], + "additionalProperties": false, + "description": "*beta* 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": "beta 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": { + "command": { + "type": "string", + "description": "represents a custom command that skaffold executes to obtain dependencies. The output of this command *must* be a valid JSON array.", + "x-intellij-html-description": "represents a custom command that skaffold executes to obtain dependencies. The output of this command must be a valid JSON array." + }, + "dockerfile": { + "$ref": "#/definitions/DockerfileDependency", + "description": "should be set if the artifact is built from a Dockerfile, from which skaffold can determine dependencies.", + "x-intellij-html-description": "should be set if the artifact is built from a Dockerfile, from which skaffold can determine dependencies." + }, + "ignore": { + "items": { + "type": "string" + }, + "type": "array", + "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": { + "items": { + "type": "string" + }, + "type": "array", + "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": "[]" + } + }, + "preferredOrder": [ + "dockerfile", + "command", + "paths", + "ignore" + ], + "additionalProperties": false, + "description": "*beta* used to specify dependencies for an artifact built by a custom build script. Either `dockerfile` or `paths` should be specified for file watching to work as expected.", + "x-intellij-html-description": "beta used to specify dependencies for an artifact built by a custom build script. Either dockerfile or paths should be specified for file watching to work as expected." + }, + "CustomTemplateTagger": { + "required": [ + "template" + ], + "properties": { + "components": { + "items": { + "$ref": "#/definitions/TaggerComponent" + }, + "type": "array", + "description": "TaggerComponents that the template (see field above) can be executed against.", + "x-intellij-html-description": "TaggerComponents that the template (see field above) can be executed against." + }, + "template": { + "type": "string", + "description": "used to produce the image name and tag. See golang [text/template](https://golang.org/pkg/text/template/). The template is executed against the provided components with those variables injected.", + "x-intellij-html-description": "used to produce the image name and tag. See golang text/template. The template is executed against the provided components with those variables injected.", + "examples": [ + "{{.DATE}}" + ] + } + }, + "preferredOrder": [ + "template", + "components" + ], + "additionalProperties": false, + "description": "*beta* tags images with a configurable template string.", + "x-intellij-html-description": "beta tags images with a configurable template string." + }, + "DateTimeTagger": { + "properties": { + "format": { + "type": "string", + "description": "formats the date and time. See [#Time.Format](https://golang.org/pkg/time/#Time.Format).", + "x-intellij-html-description": "formats the date and time. See #Time.Format.", + "default": "2006-01-02_15-04-05.999_MST" + }, + "timezone": { + "type": "string", + "description": "sets the timezone for the date and time. See [Time.LoadLocation](https://golang.org/pkg/time/#Time.LoadLocation). Defaults to the local timezone.", + "x-intellij-html-description": "sets the timezone for the date and time. See Time.LoadLocation. Defaults to the local timezone." + } + }, + "preferredOrder": [ + "format", + "timezone" + ], + "additionalProperties": false, + "description": "*beta* tags images with the build timestamp.", + "x-intellij-html-description": "beta tags images with the build timestamp." + }, + "DeployConfig": { + "properties": { + "helm": { + "$ref": "#/definitions/HelmDeploy", + "description": "*beta* uses the `helm` CLI to apply the charts to the cluster.", + "x-intellij-html-description": "beta uses the helm CLI to apply the charts to the cluster." + }, + "kpt": { + "$ref": "#/definitions/KptDeploy", + "description": "*alpha* uses the `kpt` CLI to manage and deploy manifests.", + "x-intellij-html-description": "alpha uses the kpt CLI to manage and deploy manifests." + }, + "kubeContext": { + "type": "string", + "description": "Kubernetes context that Skaffold should deploy to.", + "x-intellij-html-description": "Kubernetes context that Skaffold should deploy to.", + "examples": [ + "minikube" + ] + }, + "kubectl": { + "$ref": "#/definitions/KubectlDeploy", + "description": "*beta* uses a client side `kubectl apply` to deploy manifests. You'll need a `kubectl` CLI version installed that's compatible with your cluster.", + "x-intellij-html-description": "beta uses a client side kubectl apply to deploy manifests. You'll need a kubectl CLI version installed that's compatible with your cluster." + }, + "kustomize": { + "$ref": "#/definitions/KustomizeDeploy", + "description": "*beta* uses the `kustomize` CLI to \"patch\" a deployment for a target environment.", + "x-intellij-html-description": "beta uses the kustomize CLI to "patch" a deployment for a target environment." + }, + "logs": { + "$ref": "#/definitions/LogsConfig", + "description": "configures how container logs are printed as a result of a deployment.", + "x-intellij-html-description": "configures how container logs are printed as a result of a deployment." + }, + "statusCheckDeadlineSeconds": { + "type": "integer", + "description": "*beta* deadline for deployments to stabilize in seconds.", + "x-intellij-html-description": "beta deadline for deployments to stabilize in seconds." + } + }, + "preferredOrder": [ + "helm", + "kpt", + "kubectl", + "kustomize", + "statusCheckDeadlineSeconds", + "kubeContext", + "logs" + ], + "additionalProperties": false, + "description": "contains all the configuration needed by the deploy steps.", + "x-intellij-html-description": "contains all the configuration needed by the deploy steps." + }, + "DockerArtifact": { + "properties": { + "buildArgs": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "description": "arguments passed to the docker build.", + "x-intellij-html-description": "arguments passed to the docker build.", + "default": "{}", + "examples": [ + "{\"key1\": \"value1\", \"key2\": \"value2\"}" + ] + }, + "cacheFrom": { + "items": { + "type": "string" + }, + "type": "array", + "description": "the Docker images used as cache sources.", + "x-intellij-html-description": "the Docker images used as cache sources.", + "default": "[]", + "examples": [ + "[\"golang:1.10.1-alpine3.7\", \"alpine:3.7\"]" + ] + }, + "dockerfile": { + "type": "string", + "description": "locates the Dockerfile relative to workspace.", + "x-intellij-html-description": "locates the Dockerfile relative to workspace.", + "default": "Dockerfile" + }, + "network": { + "type": "string", + "description": "passed through to docker and overrides the network configuration of docker builder. If unset, use whatever is configured in the underlying docker daemon. Valid modes are `host`: use the host's networking stack. `bridge`: use the bridged network configuration. `none`: no networking in the container.", + "x-intellij-html-description": "passed through to docker and overrides the network configuration of docker builder. If unset, use whatever is configured in the underlying docker daemon. Valid modes are host: use the host's networking stack. bridge: use the bridged network configuration. none: no networking in the container.", + "enum": [ + "host", + "bridge", + "none" + ] + }, + "noCache": { + "type": "boolean", + "description": "used to pass in --no-cache to docker build to prevent caching.", + "x-intellij-html-description": "used to pass in --no-cache to docker build to prevent caching.", + "default": "false" + }, + "target": { + "type": "string", + "description": "Dockerfile target name to build.", + "x-intellij-html-description": "Dockerfile target name to build." + } + }, + "preferredOrder": [ + "dockerfile", + "target", + "buildArgs", + "network", + "cacheFrom", + "noCache" + ], + "additionalProperties": false, + "description": "describes an artifact built from a Dockerfile, usually using `docker build`.", + "x-intellij-html-description": "describes an artifact built from a Dockerfile, usually using docker build." + }, + "DockerConfig": { + "properties": { + "path": { + "type": "string", + "description": "path to the docker `config.json`.", + "x-intellij-html-description": "path to the docker config.json." + }, + "secretName": { + "type": "string", + "description": "Kubernetes secret that contains the `config.json` Docker configuration. Note that the expected secret type is not 'kubernetes.io/dockerconfigjson' but 'Opaque'.", + "x-intellij-html-description": "Kubernetes secret that contains the config.json Docker configuration. Note that the expected secret type is not 'kubernetes.io/dockerconfigjson' but 'Opaque'." + } + }, + "preferredOrder": [ + "path", + "secretName" + ], + "additionalProperties": false, + "description": "contains information about the docker `config.json` to mount.", + "x-intellij-html-description": "contains information about the docker config.json to mount." + }, + "DockerfileDependency": { + "properties": { + "buildArgs": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "description": "key/value pairs used to resolve values of `ARG` instructions in a Dockerfile. Values can be constants or environment variables via the go template syntax.", + "x-intellij-html-description": "key/value pairs used to resolve values of ARG instructions in a Dockerfile. Values can be constants or environment variables via the go template syntax.", + "default": "{}", + "examples": [ + "{\"key1\": \"value1\", \"key2\": \"value2\", \"key3\": \"'{{.ENV_VARIABLE}}'\"}" + ] + }, + "path": { + "type": "string", + "description": "locates the Dockerfile relative to workspace.", + "x-intellij-html-description": "locates the Dockerfile relative to workspace." + } + }, + "preferredOrder": [ + "path", + "buildArgs" + ], + "additionalProperties": false, + "description": "*beta* used to specify a custom build artifact that is built from a Dockerfile. This allows skaffold to determine dependencies from the Dockerfile.", + "x-intellij-html-description": "beta used to specify a custom build artifact that is built from a Dockerfile. This allows skaffold to determine dependencies from the Dockerfile." + }, + "EnvTemplateTagger": { + "required": [ + "template" + ], + "properties": { + "template": { + "type": "string", + "description": "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.", + "x-intellij-html-description": "used to produce the image name and tag. See golang text/template. The template is executed against the current environment, with those variables injected.", + "examples": [ + "{{.RELEASE}}" + ] + } + }, + "preferredOrder": [ + "template" + ], + "additionalProperties": false, + "description": "*beta* tags images with a configurable template string.", + "x-intellij-html-description": "beta tags images with a configurable template string." + }, + "GitTagger": { + "properties": { + "prefix": { + "type": "string", + "description": "adds a fixed prefix to the tag.", + "x-intellij-html-description": "adds a fixed prefix to the tag." + }, + "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. `TreeSha`: use the full tree hash of the artifact workingdir. `AbbrevTreeSha`: use the abbreviated tree hash of the artifact workingdir.", + "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. TreeSha: use the full tree hash of the artifact workingdir. AbbrevTreeSha: use the abbreviated tree hash of the artifact workingdir." + } + }, + "preferredOrder": [ + "variant", + "prefix" + ], + "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." + }, + "GoogleCloudBuild": { + "properties": { + "concurrency": { + "type": "integer", + "description": "how many artifacts can be built concurrently. 0 means \"no-limit\".", + "x-intellij-html-description": "how many artifacts can be built concurrently. 0 means "no-limit".", + "default": "0" + }, + "diskSizeGb": { + "type": "integer", + "description": "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).", + "x-intellij-html-description": "disk size of the VM that runs the build. See Cloud Build Reference." + }, + "dockerImage": { + "type": "string", + "description": "image that runs a Docker build. See [Cloud Builders](https://cloud.google.com/cloud-build/docs/cloud-builders).", + "x-intellij-html-description": "image that runs a Docker build. See Cloud Builders.", + "default": "gcr.io/cloud-builders/docker" + }, + "gradleImage": { + "type": "string", + "description": "image that runs a Gradle build. See [Cloud Builders](https://cloud.google.com/cloud-build/docs/cloud-builders).", + "x-intellij-html-description": "image that runs a Gradle build. See Cloud Builders.", + "default": "gcr.io/cloud-builders/gradle" + }, + "kanikoImage": { + "type": "string", + "description": "image that runs a Kaniko build. See [Cloud Builders](https://cloud.google.com/cloud-build/docs/cloud-builders).", + "x-intellij-html-description": "image that runs a Kaniko build. See Cloud Builders.", + "default": "gcr.io/kaniko-project/executor" + }, + "logStreamingOption": { + "type": "string", + "description": "specifies the behavior when writing build logs to Google Cloud Storage. Valid options are: `STREAM_DEFAULT`: Service may automatically determine build log streaming behavior. `STREAM_ON`: Build logs should be streamed to Google Cloud Storage. `STREAM_OFF`: Build logs should not be streamed to Google Cloud Storage; they will be written when the build is completed. See [Cloud Build Reference](https://cloud.google.com/cloud-build/docs/api/reference/rest/v1/projects.builds#logstreamingoption).", + "x-intellij-html-description": "specifies the behavior when writing build logs to Google Cloud Storage. Valid options are: STREAM_DEFAULT: Service may automatically determine build log streaming behavior. STREAM_ON: Build logs should be streamed to Google Cloud Storage. STREAM_OFF: Build logs should not be streamed to Google Cloud Storage; they will be written when the build is completed. See Cloud Build Reference." + }, + "logging": { + "type": "string", + "description": "specifies the logging mode. Valid modes are: `LOGGING_UNSPECIFIED`: The service determines the logging mode. `LEGACY`: Stackdriver logging and Cloud Storage logging are enabled (default). `GCS_ONLY`: Only Cloud Storage logging is enabled. See [Cloud Build Reference](https://cloud.google.com/cloud-build/docs/api/reference/rest/v1/projects.builds#loggingmode).", + "x-intellij-html-description": "specifies the logging mode. Valid modes are: LOGGING_UNSPECIFIED: The service determines the logging mode. LEGACY: Stackdriver logging and Cloud Storage logging are enabled (default). GCS_ONLY: Only Cloud Storage logging is enabled. See Cloud Build Reference." + }, + "machineType": { + "type": "string", + "description": "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).", + "x-intellij-html-description": "type of the VM that runs the build. See Cloud Build Reference." + }, + "mavenImage": { + "type": "string", + "description": "image that runs a Maven build. See [Cloud Builders](https://cloud.google.com/cloud-build/docs/cloud-builders).", + "x-intellij-html-description": "image that runs a Maven build. See Cloud Builders.", + "default": "gcr.io/cloud-builders/mvn" + }, + "packImage": { + "type": "string", + "description": "image that runs a Cloud Native Buildpacks build. See [Cloud Builders](https://cloud.google.com/cloud-build/docs/cloud-builders).", + "x-intellij-html-description": "image that runs a Cloud Native Buildpacks build. See Cloud Builders.", + "default": "gcr.io/k8s-skaffold/pack" + }, + "projectId": { + "type": "string", + "description": "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.", + "x-intellij-html-description": "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." + }, + "timeout": { + "type": "string", + "description": "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).", + "x-intellij-html-description": "amount of time (in seconds) that this build should be allowed to run. See Cloud Build Reference." + }, + "workerPool": { + "type": "string", + "description": "configures a pool of workers to run the build.", + "x-intellij-html-description": "configures a pool of workers to run the build." + } + }, + "preferredOrder": [ + "projectId", + "diskSizeGb", + "machineType", + "timeout", + "logging", + "logStreamingOption", + "dockerImage", + "kanikoImage", + "mavenImage", + "gradleImage", + "packImage", + "concurrency", + "workerPool" + ], + "additionalProperties": false, + "description": "*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.", + "x-intellij-html-description": "beta describes how to do a remote build on Google Cloud Build. 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." + }, + "HelmConventionConfig": { + "properties": { + "explicitRegistry": { + "type": "boolean", + "description": "separates `image.registry` to the image config syntax. Useful for some charts e.g. `postgresql`.", + "x-intellij-html-description": "separates image.registry to the image config syntax. Useful for some charts e.g. postgresql.", + "default": "false" + } + }, + "preferredOrder": [ + "explicitRegistry" + ], + "additionalProperties": false, + "description": "image config in the syntax of image.repository and image.tag.", + "x-intellij-html-description": "image config in the syntax of image.repository and image.tag." + }, + "HelmDeploy": { + "required": [ + "releases" + ], + "properties": { + "flags": { + "$ref": "#/definitions/HelmDeployFlags", + "description": "additional option flags that are passed on the command line to `helm`.", + "x-intellij-html-description": "additional option flags that are passed on the command line to helm." + }, + "releases": { + "items": { + "$ref": "#/definitions/HelmRelease" + }, + "type": "array", + "description": "a list of Helm releases.", + "x-intellij-html-description": "a list of Helm releases." + } + }, + "preferredOrder": [ + "releases", + "flags" + ], + "additionalProperties": false, + "description": "*beta* uses the `helm` CLI to apply the charts to the cluster.", + "x-intellij-html-description": "beta uses the helm CLI to apply the charts to the cluster." + }, + "HelmDeployFlags": { + "properties": { + "global": { + "items": { + "type": "string" + }, + "type": "array", + "description": "additional flags passed on every command.", + "x-intellij-html-description": "additional flags passed on every command.", + "default": "[]" + }, + "install": { + "items": { + "type": "string" + }, + "type": "array", + "description": "additional flags passed to (`helm install`).", + "x-intellij-html-description": "additional flags passed to (helm install).", + "default": "[]" + }, + "upgrade": { + "items": { + "type": "string" + }, + "type": "array", + "description": "additional flags passed to (`helm upgrade`).", + "x-intellij-html-description": "additional flags passed to (helm upgrade).", + "default": "[]" + } + }, + "preferredOrder": [ + "global", + "install", + "upgrade" + ], + "additionalProperties": false, + "description": "additional option flags that are passed on the command line to `helm`.", + "x-intellij-html-description": "additional option flags that are passed on the command line to helm." + }, + "HelmFQNConfig": { + "properties": { + "property": { + "type": "string", + "description": "defines the image config.", + "x-intellij-html-description": "defines the image config." + } + }, + "preferredOrder": [ + "property" + ], + "additionalProperties": false, + "description": "image config to use the FullyQualifiedImageName as param to set.", + "x-intellij-html-description": "image config to use the FullyQualifiedImageName as param to set." + }, + "HelmImageStrategy": { + "anyOf": [ + { + "additionalProperties": false + }, + { + "properties": { + "fqn": { + "$ref": "#/definitions/HelmFQNConfig", + "description": "image configuration uses the syntax `IMAGE-NAME=IMAGE-REPOSITORY:IMAGE-TAG`.", + "x-intellij-html-description": "image configuration uses the syntax IMAGE-NAME=IMAGE-REPOSITORY:IMAGE-TAG." + } + }, + "preferredOrder": [ + "fqn" + ], + "additionalProperties": false + }, + { + "properties": { + "helm": { + "$ref": "#/definitions/HelmConventionConfig", + "description": "image configuration uses the syntax `IMAGE-NAME.repository=IMAGE-REPOSITORY, IMAGE-NAME.tag=IMAGE-TAG`.", + "x-intellij-html-description": "image configuration uses the syntax IMAGE-NAME.repository=IMAGE-REPOSITORY, IMAGE-NAME.tag=IMAGE-TAG." + } + }, + "preferredOrder": [ + "helm" + ], + "additionalProperties": false + } + ], + "description": "adds image configurations to the Helm `values` file.", + "x-intellij-html-description": "adds image configurations to the Helm values file." + }, + "HelmPackaged": { + "properties": { + "appVersion": { + "type": "string", + "description": "sets the `appVersion` on the chart to this version.", + "x-intellij-html-description": "sets the appVersion on the chart to this version." + }, + "version": { + "type": "string", + "description": "sets the `version` on the chart to this semver version.", + "x-intellij-html-description": "sets the version on the chart to this semver version." + } + }, + "preferredOrder": [ + "version", + "appVersion" + ], + "additionalProperties": false, + "description": "parameters for packaging helm chart (`helm package`).", + "x-intellij-html-description": "parameters for packaging helm chart (helm package)." + }, + "HelmRelease": { + "required": [ + "name", + "chartPath" + ], + "properties": { + "artifactOverrides": { + "description": "key value pairs. If present, Skaffold will send `--set-string` flag to Helm CLI and append all pairs after the flag.", + "x-intellij-html-description": "key value pairs. If present, Skaffold will send --set-string flag to Helm CLI and append all pairs after the flag." + }, + "chartPath": { + "type": "string", + "description": "path to the Helm chart.", + "x-intellij-html-description": "path to the Helm chart." + }, + "imageStrategy": { + "$ref": "#/definitions/HelmImageStrategy", + "description": "adds image configurations to the Helm `values` file.", + "x-intellij-html-description": "adds image configurations to the Helm values file." + }, + "name": { + "type": "string", + "description": "name of the Helm release.", + "x-intellij-html-description": "name of the Helm release." + }, + "namespace": { + "type": "string", + "description": "Kubernetes namespace.", + "x-intellij-html-description": "Kubernetes namespace." + }, + "overrides": { + "description": "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).", + "x-intellij-html-description": "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)." + }, + "packaged": { + "$ref": "#/definitions/HelmPackaged", + "description": "parameters for packaging helm chart (`helm package`).", + "x-intellij-html-description": "parameters for packaging helm chart (helm package)." + }, + "recreatePods": { + "type": "boolean", + "description": "if `true`, Skaffold will send `--recreate-pods` flag to Helm CLI when upgrading a new version of a chart in subsequent dev loop deploy.", + "x-intellij-html-description": "if true, Skaffold will send --recreate-pods flag to Helm CLI when upgrading a new version of a chart in subsequent dev loop deploy.", + "default": "false" + }, + "remote": { + "type": "boolean", + "description": "specifies whether the chart path is remote, or exists on the host filesystem.", + "x-intellij-html-description": "specifies whether the chart path is remote, or exists on the host filesystem.", + "default": "false" + }, + "setFiles": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "description": "key-value pairs. If present, Skaffold will send `--set-file` flag to Helm CLI and append all pairs after the flag.", + "x-intellij-html-description": "key-value pairs. If present, Skaffold will send --set-file flag to Helm CLI and append all pairs after the flag.", + "default": "{}" + }, + "setValueTemplates": { + "description": "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.", + "x-intellij-html-description": "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." + }, + "setValues": { + "description": "key-value pairs. If present, Skaffold will send `--set` flag to Helm CLI and append all pairs after the flag.", + "x-intellij-html-description": "key-value pairs. If present, Skaffold will send --set flag to Helm CLI and append all pairs after the flag." + }, + "skipBuildDependencies": { + "type": "boolean", + "description": "should build dependencies be skipped. Ignored when `remote: true`.", + "x-intellij-html-description": "should build dependencies be skipped. Ignored when remote: true.", + "default": "false" + }, + "upgradeOnChange": { + "type": "boolean", + "description": "specifies whether to upgrade helm chart on code changes. Default is `true` when helm chart is local (`remote: false`). Default is `false` if `remote: true`.", + "x-intellij-html-description": "specifies whether to upgrade helm chart on code changes. Default is true when helm chart is local (remote: false). Default is false if remote: true." + }, + "useHelmSecrets": { + "type": "boolean", + "description": "instructs skaffold to use secrets plugin on deployment.", + "x-intellij-html-description": "instructs skaffold to use secrets plugin on deployment.", + "default": "false" + }, + "valuesFiles": { + "items": { + "type": "string" + }, + "type": "array", + "description": "paths to the Helm `values` files.", + "x-intellij-html-description": "paths to the Helm values files.", + "default": "[]" + }, + "version": { + "type": "string", + "description": "version of the chart.", + "x-intellij-html-description": "version of the chart." + }, + "wait": { + "type": "boolean", + "description": "if `true`, Skaffold will send `--wait` flag to Helm CLI.", + "x-intellij-html-description": "if true, Skaffold will send --wait flag to Helm CLI.", + "default": "false" + } + }, + "preferredOrder": [ + "name", + "chartPath", + "valuesFiles", + "artifactOverrides", + "namespace", + "version", + "setValues", + "setValueTemplates", + "setFiles", + "wait", + "recreatePods", + "skipBuildDependencies", + "useHelmSecrets", + "remote", + "upgradeOnChange", + "overrides", + "packaged", + "imageStrategy" + ], + "additionalProperties": false, + "description": "describes a helm release to be deployed.", + "x-intellij-html-description": "describes a helm release to be deployed." + }, + "JSONPatch": { + "required": [ + "path" + ], + "properties": { + "from": { + "type": "string", + "description": "source position in the yaml, used for `copy` or `move` operations.", + "x-intellij-html-description": "source position in the yaml, used for copy or move operations." + }, + "op": { + "type": "string", + "description": "operation carried by the patch: `add`, `remove`, `replace`, `move`, `copy` or `test`.", + "x-intellij-html-description": "operation carried by the patch: add, remove, replace, move, copy or test.", + "default": "replace" + }, + "path": { + "type": "string", + "description": "position in the yaml where the operation takes place. For example, this targets the `dockerfile` of the first artifact built.", + "x-intellij-html-description": "position in the yaml where the operation takes place. For example, this targets the dockerfile of the first artifact built.", + "examples": [ + "/build/artifacts/0/docker/dockerfile" + ] + }, + "value": { + "description": "value to apply. Can be any portion of yaml.", + "x-intellij-html-description": "value to apply. Can be any portion of yaml." + } + }, + "preferredOrder": [ + "op", + "path", + "from", + "value" + ], + "additionalProperties": false, + "description": "patch to be applied by a profile.", + "x-intellij-html-description": "patch to be applied by a profile." + }, + "JibArtifact": { + "properties": { + "args": { + "items": { + "type": "string" + }, + "type": "array", + "description": "additional build flags passed to the builder.", + "x-intellij-html-description": "additional build flags passed to the builder.", + "default": "[]", + "examples": [ + "[\"--no-build-cache\"]" + ] + }, + "project": { + "type": "string", + "description": "selects which sub-project to build for multi-module builds.", + "x-intellij-html-description": "selects which sub-project to build for multi-module builds." + }, + "type": { + "type": "string", + "description": "the Jib builder type; normally determined automatically. Valid types are `maven`: for Maven. `gradle`: for Gradle.", + "x-intellij-html-description": "the Jib builder type; normally determined automatically. Valid types are maven: for Maven. gradle: for Gradle.", + "enum": [ + "maven", + "gradle" + ] + } + }, + "preferredOrder": [ + "project", + "args", + "type" + ], + "additionalProperties": false, + "description": "builds images using the [Jib plugins for Maven and Gradle](https://github.com/GoogleContainerTools/jib/).", + "x-intellij-html-description": "builds images using the Jib plugins for Maven and Gradle." + }, + "KanikoArtifact": { + "properties": { + "buildArgs": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "description": "arguments passed to the docker build. It also accepts environment variables via the go template syntax.", + "x-intellij-html-description": "arguments passed to the docker build. It also accepts environment variables via the go template syntax.", + "default": "{}", + "examples": [ + "{\"key1\": \"value1\", \"key2\": \"value2\", \"key3\": \"'{{.ENV_VARIABLE}}'\"}" + ] + }, + "cache": { + "$ref": "#/definitions/KanikoCache", + "description": "configures Kaniko caching. If a cache is specified, Kaniko will use a remote cache which will speed up builds.", + "x-intellij-html-description": "configures Kaniko caching. If a cache is specified, Kaniko will use a remote cache which will speed up builds." + }, + "dockerfile": { + "type": "string", + "description": "locates the Dockerfile relative to workspace.", + "x-intellij-html-description": "locates the Dockerfile relative to workspace.", + "default": "Dockerfile" + }, + "env": { + "items": {}, + "type": "array", + "description": "environment variables passed to the kaniko pod.", + "x-intellij-html-description": "environment variables passed to the kaniko pod.", + "default": "[]" + }, + "flags": { + "items": { + "type": "string" + }, + "type": "array", + "description": "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`.", + "x-intellij-html-description": "additional flags to be passed to Kaniko command line. See Kaniko Additional Flags. Deprecated - instead the named, unique fields should be used, e.g. buildArgs, cache, target.", + "default": "[]" + }, + "image": { + "type": "string", + "description": "Docker image used by the Kaniko pod. Defaults to the latest released version of `gcr.io/kaniko-project/executor`.", + "x-intellij-html-description": "Docker image used by the Kaniko pod. Defaults to the latest released version of gcr.io/kaniko-project/executor." + }, + "initImage": { + "type": "string", + "description": "image used to run init container which mounts kaniko context.", + "x-intellij-html-description": "image used to run init container which mounts kaniko context." + }, + "reproducible": { + "type": "boolean", + "description": "used to strip timestamps out of the built image.", + "x-intellij-html-description": "used to strip timestamps out of the built image.", + "default": "false" + }, + "skipTLS": { + "type": "boolean", + "description": "skips TLS verification when pulling and pushing the image.", + "x-intellij-html-description": "skips TLS verification when pulling and pushing the image.", + "default": "false" + }, + "target": { + "type": "string", + "description": "Dockerfile target name to build.", + "x-intellij-html-description": "Dockerfile target name to build." + }, + "volumeMounts": { + "items": {}, + "type": "array", + "description": "volume mounts passed to kaniko pod.", + "x-intellij-html-description": "volume mounts passed to kaniko pod.", + "default": "[]" + } + }, + "preferredOrder": [ + "flags", + "dockerfile", + "target", + "buildArgs", + "env", + "initImage", + "image", + "cache", + "reproducible", + "skipTLS", + "volumeMounts" + ], + "additionalProperties": false, + "description": "describes an artifact built from a Dockerfile, with kaniko.", + "x-intellij-html-description": "describes an artifact built from a Dockerfile, with kaniko." + }, + "KanikoCache": { + "properties": { + "hostPath": { + "type": "string", + "description": "specifies a path on the host that is mounted to each pod as read only cache volume containing base images. If set, must exist on each node and prepopulated with kaniko-warmer.", + "x-intellij-html-description": "specifies a path on the host that is mounted to each pod as read only cache volume containing base images. If set, must exist on each node and prepopulated with kaniko-warmer." + }, + "repo": { + "type": "string", + "description": "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).", + "x-intellij-html-description": "a remote repository to store cached layers. If none is specified, one will be inferred from the image name. See Kaniko Caching." + } + }, + "preferredOrder": [ + "repo", + "hostPath" + ], + "additionalProperties": false, + "description": "configures Kaniko caching. If a cache is specified, Kaniko will use a remote cache which will speed up builds.", + "x-intellij-html-description": "configures Kaniko caching. If a cache is specified, Kaniko will use a remote cache which will speed up builds." + }, + "KptDeploy": { + "properties": { + "applyDir": { + "type": "string", + "description": "path to the directory to deploy to the cluster.", + "x-intellij-html-description": "path to the directory to deploy to the cluster." + }, + "dir": { + "type": "string", + "description": "path to the directory to run kpt functions against.", + "x-intellij-html-description": "path to the directory to run kpt functions against." + }, + "fn": { + "$ref": "#/definitions/KptFn", + "description": "adds additional configurations for `kpt fn`.", + "x-intellij-html-description": "adds additional configurations for kpt fn." + }, + "live": { + "$ref": "#/definitions/KptLive", + "description": "adds additional configurations for `kpt live`.", + "x-intellij-html-description": "adds additional configurations for kpt live." + } + }, + "preferredOrder": [ + "applyDir", + "dir", + "fn", + "live" + ], + "additionalProperties": false, + "description": "*alpha* uses the `kpt` CLI to manage and deploy manifests.", + "x-intellij-html-description": "alpha uses the kpt CLI to manage and deploy manifests." + }, + "KptFn": { + "properties": { + "fnPath": { + "type": "string", + "description": "a directory to read functions from instead of the configuration directory.", + "x-intellij-html-description": "a directory to read functions from instead of the configuration directory." + }, + "globalScope": { + "type": "boolean", + "description": "sets global scope for functions.", + "x-intellij-html-description": "sets global scope for functions.", + "default": "false" + }, + "image": { + "type": "string", + "description": "an image to be run as a function in lieu of running functions from a directory.", + "x-intellij-html-description": "an image to be run as a function in lieu of running functions from a directory." + }, + "mount": { + "items": { + "type": "string" + }, + "type": "array", + "description": "a list of storage options to mount to the fn image.", + "x-intellij-html-description": "a list of storage options to mount to the fn image.", + "default": "[]" + }, + "network": { + "type": "boolean", + "description": "enables network access for functions that declare it.", + "x-intellij-html-description": "enables network access for functions that declare it.", + "default": "false" + }, + "networkName": { + "type": "string", + "description": "docker network to run the container in (default \"bridge\").", + "x-intellij-html-description": "docker network to run the container in (default "bridge")." + } + }, + "preferredOrder": [ + "fnPath", + "image", + "networkName", + "globalScope", + "network", + "mount" + ], + "additionalProperties": false, + "description": "adds additional configurations used when calling `kpt fn`.", + "x-intellij-html-description": "adds additional configurations used when calling kpt fn." + }, + "KptLive": { + "properties": { + "apply": { + "$ref": "#/definitions/KptLiveApply", + "description": "adds additional configurations for `kpt live apply` commands.", + "x-intellij-html-description": "adds additional configurations for kpt live apply commands." + }, + "inventoryID": { + "type": "string", + "description": "identifier for a group of applied resources. This configuration is used when users do not specify `KptDeploy.ApplyDir` and `.kpt-hydrated/inventory-template.yaml` does not exist.", + "x-intellij-html-description": "identifier for a group of applied resources. This configuration is used when users do not specify KptDeploy.ApplyDir and .kpt-hydrated/inventory-template.yaml does not exist." + }, + "inventoryNamespace": { + "type": "string", + "description": "sets the namespace scope for `kpt live init`.", + "x-intellij-html-description": "sets the namespace scope for kpt live init." + } + }, + "preferredOrder": [ + "apply", + "inventoryID", + "inventoryNamespace" + ], + "additionalProperties": false, + "description": "adds additional configurations used when calling `kpt live`.", + "x-intellij-html-description": "adds additional configurations used when calling kpt live." + }, + "KptLiveApply": { + "properties": { + "pollPeriod": { + "type": "string", + "description": "sets for the polling period for resource statuses. Default to 2s.", + "x-intellij-html-description": "sets for the polling period for resource statuses. Default to 2s." + }, + "prunePropagationPolicy": { + "type": "string", + "description": "sets the propagation policy for pruning. Possible settings are Background, Foreground, Orphan. Default to \"Background\".", + "x-intellij-html-description": "sets the propagation policy for pruning. Possible settings are Background, Foreground, Orphan. Default to "Background"." + }, + "pruneTimeout": { + "type": "string", + "description": "sets the time threshold to wait for all pruned resources to be deleted.", + "x-intellij-html-description": "sets the time threshold to wait for all pruned resources to be deleted." + }, + "reconcileTimeout": { + "type": "string", + "description": "sets the time threshold to wait for all resources to reach the current status.", + "x-intellij-html-description": "sets the time threshold to wait for all resources to reach the current status." + } + }, + "preferredOrder": [ + "pollPeriod", + "prunePropagationPolicy", + "pruneTimeout", + "reconcileTimeout" + ], + "additionalProperties": false, + "description": "adds additional configurations used when calling `kpt live apply`.", + "x-intellij-html-description": "adds additional configurations used when calling kpt live apply." + }, + "KubectlDeploy": { + "properties": { + "flags": { + "$ref": "#/definitions/KubectlFlags", + "description": "additional flags passed to `kubectl`.", + "x-intellij-html-description": "additional flags passed to kubectl." + }, + "manifests": { + "items": { + "type": "string" + }, + "type": "array", + "description": "the Kubernetes yaml or json manifests.", + "x-intellij-html-description": "the Kubernetes yaml or json manifests.", + "default": "[\"k8s/*.yaml\"]" + }, + "remoteManifests": { + "items": { + "type": "string" + }, + "type": "array", + "description": "Kubernetes manifests in remote clusters.", + "x-intellij-html-description": "Kubernetes manifests in remote clusters.", + "default": "[]" + } + }, + "preferredOrder": [ + "manifests", + "remoteManifests", + "flags" + ], + "additionalProperties": false, + "description": "*beta* uses a client side `kubectl apply` to deploy manifests. You'll need a `kubectl` CLI version installed that's compatible with your cluster.", + "x-intellij-html-description": "beta uses a client side kubectl apply to deploy manifests. You'll need a kubectl CLI version installed that's compatible with your cluster." + }, + "KubectlFlags": { + "properties": { + "apply": { + "items": { + "type": "string" + }, + "type": "array", + "description": "additional flags passed on creations (`kubectl apply`).", + "x-intellij-html-description": "additional flags passed on creations (kubectl apply).", + "default": "[]" + }, + "delete": { + "items": { + "type": "string" + }, + "type": "array", + "description": "additional flags passed on deletions (`kubectl delete`).", + "x-intellij-html-description": "additional flags passed on deletions (kubectl delete).", + "default": "[]" + }, + "disableValidation": { + "type": "boolean", + "description": "passes the `--validate=false` flag to supported `kubectl` commands when enabled.", + "x-intellij-html-description": "passes the --validate=false flag to supported kubectl commands when enabled.", + "default": "false" + }, + "global": { + "items": { + "type": "string" + }, + "type": "array", + "description": "additional flags passed on every command.", + "x-intellij-html-description": "additional flags passed on every command.", + "default": "[]" + } + }, + "preferredOrder": [ + "global", + "apply", + "delete", + "disableValidation" + ], + "additionalProperties": false, + "description": "additional flags passed on the command line to kubectl either on every command (Global), on creations (Apply) or deletions (Delete).", + "x-intellij-html-description": "additional flags passed on the command line to kubectl either on every command (Global), on creations (Apply) or deletions (Delete)." + }, + "KustomizeDeploy": { + "properties": { + "buildArgs": { + "items": { + "type": "string" + }, + "type": "array", + "description": "additional args passed to `kustomize build`.", + "x-intellij-html-description": "additional args passed to kustomize build.", + "default": "[]" + }, + "flags": { + "$ref": "#/definitions/KubectlFlags", + "description": "additional flags passed to `kubectl`.", + "x-intellij-html-description": "additional flags passed to kubectl." + }, + "paths": { + "items": { + "type": "string" + }, + "type": "array", + "description": "path to Kustomization files.", + "x-intellij-html-description": "path to Kustomization files.", + "default": "[\".\"]" + } + }, + "preferredOrder": [ + "paths", + "flags", + "buildArgs" + ], + "additionalProperties": false, + "description": "*beta* uses the `kustomize` CLI to \"patch\" a deployment for a target environment.", + "x-intellij-html-description": "beta uses the kustomize CLI to "patch" a deployment for a target environment." + }, + "LocalBuild": { + "properties": { + "concurrency": { + "type": "integer", + "description": "how many artifacts can be built concurrently. 0 means \"no-limit\".", + "x-intellij-html-description": "how many artifacts can be built concurrently. 0 means "no-limit".", + "default": "1" + }, + "push": { + "type": "boolean", + "description": "should images be pushed to a registry. If not specified, images are pushed only if the current Kubernetes context connects to a remote cluster.", + "x-intellij-html-description": "should images be pushed to a registry. If not specified, images are pushed only if the current Kubernetes context connects to a remote cluster." + }, + "useBuildkit": { + "type": "boolean", + "description": "use BuildKit to build Docker images.", + "x-intellij-html-description": "use BuildKit to build Docker images.", + "default": "false" + }, + "useDockerCLI": { + "type": "boolean", + "description": "use `docker` command-line interface instead of Docker Engine APIs.", + "x-intellij-html-description": "use docker command-line interface instead of Docker Engine APIs.", + "default": "false" + } + }, + "preferredOrder": [ + "push", + "useDockerCLI", + "useBuildkit", + "concurrency" + ], + "additionalProperties": false, + "description": "*beta* describes how to do a build on the local docker daemon and optionally push to a repository.", + "x-intellij-html-description": "beta describes how to do a build on the local docker daemon and optionally push to a repository." + }, + "LogsConfig": { + "properties": { + "prefix": { + "type": "string", + "description": "defines the prefix shown on each log line. Valid values are `container`: prefix logs lines with the name of the container. `podAndContainer`: prefix logs lines with the names of the pod and of the container. `auto`: same as `podAndContainer` except that the pod name is skipped if it's the same as the container name. `none`: don't add a prefix.", + "x-intellij-html-description": "defines the prefix shown on each log line. Valid values are container: prefix logs lines with the name of the container. podAndContainer: prefix logs lines with the names of the pod and of the container. auto: same as podAndContainer except that the pod name is skipped if it's the same as the container name. none: don't add a prefix.", + "default": "auto", + "enum": [ + "container", + "podAndContainer", + "auto", + "none" + ] + } + }, + "preferredOrder": [ + "prefix" + ], + "additionalProperties": false, + "description": "configures how container logs are printed as a result of a deployment.", + "x-intellij-html-description": "configures how container logs are printed as a result of a deployment." + }, + "Metadata": { + "properties": { + "name": { + "type": "string", + "description": "an identifier for the project.", + "x-intellij-html-description": "an identifier for the project." + } + }, + "preferredOrder": [ + "name" + ], + "additionalProperties": false, + "description": "holds an optional name of the project.", + "x-intellij-html-description": "holds an optional name of the project." + }, + "PortForwardResource": { + "properties": { + "address": { + "type": "string", + "description": "local address to bind to. Defaults to the loopback address 127.0.0.1.", + "x-intellij-html-description": "local address to bind to. Defaults to the loopback address 127.0.0.1." + }, + "localPort": { + "type": "integer", + "description": "local port to forward to. If the port is unavailable, Skaffold will choose a random open port to forward to. *Optional*.", + "x-intellij-html-description": "local port to forward to. If the port is unavailable, Skaffold will choose a random open port to forward to. Optional." + }, + "namespace": { + "type": "string", + "description": "namespace of the resource to port forward.", + "x-intellij-html-description": "namespace of the resource to port forward." + }, + "port": { + "type": "integer", + "description": "resource port that will be forwarded.", + "x-intellij-html-description": "resource port that will be forwarded." + }, + "resourceName": { + "type": "string", + "description": "name of the Kubernetes resource to port forward.", + "x-intellij-html-description": "name of the Kubernetes resource to port forward." + }, + "resourceType": { + "type": "string", + "description": "Kubernetes type that should be port forwarded. Acceptable resource types include: `Service`, `Pod` and Controller resource type that has a pod spec: `ReplicaSet`, `ReplicationController`, `Deployment`, `StatefulSet`, `DaemonSet`, `Job`, `CronJob`.", + "x-intellij-html-description": "Kubernetes type that should be port forwarded. Acceptable resource types include: Service, Pod and Controller resource type that has a pod spec: ReplicaSet, ReplicationController, Deployment, StatefulSet, DaemonSet, Job, CronJob." + } + }, + "preferredOrder": [ + "resourceType", + "resourceName", + "namespace", + "port", + "address", + "localPort" + ], + "additionalProperties": false, + "description": "describes a resource to port forward.", + "x-intellij-html-description": "describes a resource to port forward." + }, + "Profile": { + "required": [ + "name" + ], + "properties": { + "activation": { + "items": { + "$ref": "#/definitions/Activation" + }, + "type": "array", + "description": "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.", + "x-intellij-html-description": "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." + }, + "build": { + "$ref": "#/definitions/BuildConfig", + "description": "describes how images are built.", + "x-intellij-html-description": "describes how images are built." + }, + "deploy": { + "$ref": "#/definitions/DeployConfig", + "description": "describes how images are deployed.", + "x-intellij-html-description": "describes how images are deployed." + }, + "name": { + "type": "string", + "description": "a unique profile name.", + "x-intellij-html-description": "a unique profile name.", + "examples": [ + "profile-prod" + ] + }, + "patches": { + "items": { + "$ref": "#/definitions/JSONPatch" + }, + "type": "array", + "description": "patches applied to the configuration. Patches use the JSON patch notation.", + "x-intellij-html-description": "patches applied to the configuration. Patches use the JSON patch notation." + }, + "portForward": { + "items": { + "$ref": "#/definitions/PortForwardResource" + }, + "type": "array", + "description": "describes user defined resources to port-forward.", + "x-intellij-html-description": "describes user defined resources to port-forward." + }, + "test": { + "items": { + "$ref": "#/definitions/TestCase" + }, + "type": "array", + "description": "describes how images are tested.", + "x-intellij-html-description": "describes how images are tested." + } + }, + "preferredOrder": [ + "name", + "activation", + "patches", + "build", + "test", + "deploy", + "portForward" + ], + "additionalProperties": false, + "description": "used to override any `build`, `test` or `deploy` configuration.", + "x-intellij-html-description": "used to override any build, test or deploy configuration." + }, + "ResourceRequirement": { + "properties": { + "cpu": { + "type": "string", + "description": "the number cores to be used.", + "x-intellij-html-description": "the number cores to be used.", + "examples": [ + "2`, `2.0` or `200m" + ] + }, + "ephemeralStorage": { + "type": "string", + "description": "the amount of Ephemeral storage to allocate to the pod.", + "x-intellij-html-description": "the amount of Ephemeral storage to allocate to the pod.", + "examples": [ + "1Gi` or `1000Mi" + ] + }, + "memory": { + "type": "string", + "description": "the amount of memory to allocate to the pod.", + "x-intellij-html-description": "the amount of memory to allocate to the pod.", + "examples": [ + "1Gi` or `1000Mi" + ] + }, + "resourceStorage": { + "type": "string", + "description": "the amount of resource storage to allocate to the pod.", + "x-intellij-html-description": "the amount of resource storage to allocate to the pod.", + "examples": [ + "1Gi` or `1000Mi" + ] + } + }, + "preferredOrder": [ + "cpu", + "memory", + "ephemeralStorage", + "resourceStorage" + ], + "additionalProperties": false, + "description": "stores the CPU/Memory requirements for the pod.", + "x-intellij-html-description": "stores the CPU/Memory requirements for the pod." + }, + "ResourceRequirements": { + "properties": { + "limits": { + "$ref": "#/definitions/ResourceRequirement", + "description": "[resource limits](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container) for the Kaniko pod.", + "x-intellij-html-description": "resource limits for the Kaniko pod." + }, + "requests": { + "$ref": "#/definitions/ResourceRequirement", + "description": "[resource requests](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container) for the Kaniko pod.", + "x-intellij-html-description": "resource requests for the Kaniko pod." + } + }, + "preferredOrder": [ + "requests", + "limits" + ], + "additionalProperties": false, + "description": "describes the resource requirements for the kaniko pod.", + "x-intellij-html-description": "describes the resource requirements for the kaniko pod." + }, + "ResourceType": { + "type": "string", + "description": "describes the Kubernetes resource types used for port forwarding.", + "x-intellij-html-description": "describes the Kubernetes resource types used for port forwarding." + }, + "ShaTagger": { + "description": "*beta* tags images with their sha256 digest.", + "x-intellij-html-description": "beta tags images with their sha256 digest." + }, + "SkaffoldConfig": { + "required": [ + "apiVersion", + "kind" + ], + "properties": { + "apiVersion": { + "type": "string", + "description": "version of the configuration.", + "x-intellij-html-description": "version of the configuration." + }, + "build": { + "$ref": "#/definitions/BuildConfig", + "description": "describes how images are built.", + "x-intellij-html-description": "describes how images are built." + }, + "deploy": { + "$ref": "#/definitions/DeployConfig", + "description": "describes how images are deployed.", + "x-intellij-html-description": "describes how images are deployed." + }, + "kind": { + "type": "string", + "description": "always `Config`.", + "x-intellij-html-description": "always Config.", + "default": "Config" + }, + "metadata": { + "$ref": "#/definitions/Metadata", + "description": "holds additional information about the config.", + "x-intellij-html-description": "holds additional information about the config." + }, + "portForward": { + "items": { + "$ref": "#/definitions/PortForwardResource" + }, + "type": "array", + "description": "describes user defined resources to port-forward.", + "x-intellij-html-description": "describes user defined resources to port-forward." + }, + "profiles": { + "items": { + "$ref": "#/definitions/Profile" + }, + "type": "array", + "description": "*beta* can override be used to `build`, `test` or `deploy` configuration.", + "x-intellij-html-description": "beta can override be used to build, test or deploy configuration." + }, + "test": { + "items": { + "$ref": "#/definitions/TestCase" + }, + "type": "array", + "description": "describes how images are tested.", + "x-intellij-html-description": "describes how images are tested." + } + }, + "preferredOrder": [ + "apiVersion", + "kind", + "metadata", + "build", + "test", + "deploy", + "portForward", + "profiles" + ], + "additionalProperties": false, + "description": "holds the fields parsed from the Skaffold configuration file (skaffold.yaml).", + "x-intellij-html-description": "holds the fields parsed from the Skaffold configuration file (skaffold.yaml)." + }, + "Sync": { + "properties": { + "auto": { + "$ref": "#/definitions/Auto", + "description": "delegates discovery of sync rules to the build system. Only available for jib and buildpacks.", + "x-intellij-html-description": "delegates discovery of sync rules to the build system. Only available for jib and buildpacks." + }, + "infer": { + "items": { + "type": "string" + }, + "type": "array", + "description": "file patterns which may be synced into the container The container destination is inferred by the builder based on the instructions of a Dockerfile. Available for docker and kaniko artifacts and custom artifacts that declare dependencies on a dockerfile.", + "x-intellij-html-description": "file patterns which may be synced into the container The container destination is inferred by the builder based on the instructions of a Dockerfile. Available for docker and kaniko artifacts and custom artifacts that declare dependencies on a dockerfile.", + "default": "[]" + }, + "manual": { + "items": { + "$ref": "#/definitions/SyncRule" + }, + "type": "array", + "description": "manual sync rules indicating the source and destination.", + "x-intellij-html-description": "manual sync rules indicating the source and destination." + } + }, + "preferredOrder": [ + "manual", + "infer", + "auto" + ], + "additionalProperties": false, + "description": "*beta* specifies what files to sync into the container. This is a list of sync rules indicating the intent to sync for source files. If no files are listed, sync all the files and infer the destination.", + "x-intellij-html-description": "beta specifies what files to sync into the container. This is a list of sync rules indicating the intent to sync for source files. If no files are listed, sync all the files and infer the destination.", + "default": "infer: [\"**/*\"]" + }, + "SyncRule": { + "required": [ + "src", + "dest" + ], + "properties": { + "dest": { + "type": "string", + "description": "destination path in the container where the files should be synced to.", + "x-intellij-html-description": "destination path in the container where the files should be synced to.", + "examples": [ + "\"app/\"" + ] + }, + "src": { + "type": "string", + "description": "a glob pattern to match local paths against. Directories should be delimited by `/` on all platforms.", + "x-intellij-html-description": "a glob pattern to match local paths against. Directories should be delimited by / on all platforms.", + "examples": [ + "\"css/**/*.css\"" + ] + }, + "strip": { + "type": "string", + "description": "specifies the path prefix to remove from the source path when transplanting the files into the destination folder.", + "x-intellij-html-description": "specifies the path prefix to remove from the source path when transplanting the files into the destination folder.", + "examples": [ + "\"css/\"" + ] + } + }, + "preferredOrder": [ + "src", + "dest", + "strip" + ], + "additionalProperties": false, + "description": "specifies which local files to sync to remote folders.", + "x-intellij-html-description": "specifies which local files to sync to remote folders." + }, + "TagPolicy": { + "properties": { + "customTemplate": { + "$ref": "#/definitions/CustomTemplateTagger", + "description": "*beta* tags images with a configurable template string *composed of other taggers*.", + "x-intellij-html-description": "beta tags images with a configurable template string composed of other taggers." + }, + "dateTime": { + "$ref": "#/definitions/DateTimeTagger", + "description": "*beta* tags images with the build timestamp.", + "x-intellij-html-description": "beta tags images with the build timestamp." + }, + "envTemplate": { + "$ref": "#/definitions/EnvTemplateTagger", + "description": "*beta* tags images with a configurable template string.", + "x-intellij-html-description": "beta tags images with a configurable template string." + }, + "gitCommit": { + "$ref": "#/definitions/GitTagger", + "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." + }, + "sha256": { + "$ref": "#/definitions/ShaTagger", + "description": "*beta* tags images with their sha256 digest.", + "x-intellij-html-description": "beta tags images with their sha256 digest." + } + }, + "preferredOrder": [ + "gitCommit", + "sha256", + "envTemplate", + "dateTime", + "customTemplate" + ], + "additionalProperties": false, + "description": "contains all the configuration for the tagging step.", + "x-intellij-html-description": "contains all the configuration for the tagging step." + }, + "TaggerComponent": { + "anyOf": [ + { + "properties": { + "name": { + "type": "string", + "description": "an identifier for the component.", + "x-intellij-html-description": "an identifier for the component." + } + }, + "preferredOrder": [ + "name" + ], + "additionalProperties": false + }, + { + "properties": { + "gitCommit": { + "$ref": "#/definitions/GitTagger", + "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." + }, + "name": { + "type": "string", + "description": "an identifier for the component.", + "x-intellij-html-description": "an identifier for the component." + } + }, + "preferredOrder": [ + "name", + "gitCommit" + ], + "additionalProperties": false + }, + { + "properties": { + "name": { + "type": "string", + "description": "an identifier for the component.", + "x-intellij-html-description": "an identifier for the component." + }, + "sha256": { + "$ref": "#/definitions/ShaTagger", + "description": "*beta* tags images with their sha256 digest.", + "x-intellij-html-description": "beta tags images with their sha256 digest." + } + }, + "preferredOrder": [ + "name", + "sha256" + ], + "additionalProperties": false + }, + { + "properties": { + "envTemplate": { + "$ref": "#/definitions/EnvTemplateTagger", + "description": "*beta* tags images with a configurable template string.", + "x-intellij-html-description": "beta tags images with a configurable template string." + }, + "name": { + "type": "string", + "description": "an identifier for the component.", + "x-intellij-html-description": "an identifier for the component." + } + }, + "preferredOrder": [ + "name", + "envTemplate" + ], + "additionalProperties": false + }, + { + "properties": { + "dateTime": { + "$ref": "#/definitions/DateTimeTagger", + "description": "*beta* tags images with the build timestamp.", + "x-intellij-html-description": "beta tags images with the build timestamp." + }, + "name": { + "type": "string", + "description": "an identifier for the component.", + "x-intellij-html-description": "an identifier for the component." + } + }, + "preferredOrder": [ + "name", + "dateTime" + ], + "additionalProperties": false + }, + { + "properties": { + "customTemplate": { + "$ref": "#/definitions/CustomTemplateTagger", + "description": "*beta* tags images with a configurable template string *composed of other taggers*.", + "x-intellij-html-description": "beta tags images with a configurable template string composed of other taggers." + }, + "name": { + "type": "string", + "description": "an identifier for the component.", + "x-intellij-html-description": "an identifier for the component." + } + }, + "preferredOrder": [ + "name", + "customTemplate" + ], + "additionalProperties": false + } + ], + "description": "*beta* a component of CustomTemplateTagger.", + "x-intellij-html-description": "beta a component of CustomTemplateTagger." + }, + "TestCase": { + "required": [ + "image" + ], + "properties": { + "image": { + "type": "string", + "description": "artifact on which to run those tests.", + "x-intellij-html-description": "artifact on which to run those tests.", + "examples": [ + "gcr.io/k8s-skaffold/example" + ] + }, + "structureTests": { + "items": { + "type": "string" + }, + "type": "array", + "description": "the [Container Structure Tests](https://github.com/GoogleContainerTools/container-structure-test) to run on that artifact.", + "x-intellij-html-description": "the Container Structure Tests to run on that artifact.", + "default": "[]", + "examples": [ + "[\"./test/*\"]" + ] + } + }, + "preferredOrder": [ + "image", + "structureTests" + ], + "additionalProperties": false, + "description": "a list of structure tests to run on images that Skaffold builds.", + "x-intellij-html-description": "a list of structure tests to run on images that Skaffold builds." + } + } +} diff --git a/integration/examples/bazel/skaffold.yaml b/integration/examples/bazel/skaffold.yaml index 4bc1c9ce18c..04262665a7d 100644 --- a/integration/examples/bazel/skaffold.yaml +++ b/integration/examples/bazel/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/buildpacks-java/skaffold.yaml b/integration/examples/buildpacks-java/skaffold.yaml index 04237cad528..299025c5987 100644 --- a/integration/examples/buildpacks-java/skaffold.yaml +++ b/integration/examples/buildpacks-java/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/buildpacks-node/skaffold.yaml b/integration/examples/buildpacks-node/skaffold.yaml index 5f34b414c89..269b607ba6b 100644 --- a/integration/examples/buildpacks-node/skaffold.yaml +++ b/integration/examples/buildpacks-node/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/buildpacks-python/skaffold.yaml b/integration/examples/buildpacks-python/skaffold.yaml index 3fedbd626cb..988c6287621 100644 --- a/integration/examples/buildpacks-python/skaffold.yaml +++ b/integration/examples/buildpacks-python/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/buildpacks/skaffold.yaml b/integration/examples/buildpacks/skaffold.yaml index f5ee9475dee..f77579e2a68 100644 --- a/integration/examples/buildpacks/skaffold.yaml +++ b/integration/examples/buildpacks/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/custom/skaffold.yaml b/integration/examples/custom/skaffold.yaml index 498dfb1f189..1108350f5bc 100644 --- a/integration/examples/custom/skaffold.yaml +++ b/integration/examples/custom/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/gcb-kaniko/skaffold.yaml b/integration/examples/gcb-kaniko/skaffold.yaml index 1db45ad5549..77335596f0d 100644 --- a/integration/examples/gcb-kaniko/skaffold.yaml +++ b/integration/examples/gcb-kaniko/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: googleCloudBuild: diff --git a/integration/examples/generate-pipeline/skaffold.yaml b/integration/examples/generate-pipeline/skaffold.yaml index fdfaafcfced..4163200c9a9 100644 --- a/integration/examples/generate-pipeline/skaffold.yaml +++ b/integration/examples/generate-pipeline/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/getting-started-kustomize/skaffold.yaml b/integration/examples/getting-started-kustomize/skaffold.yaml index 35a2919b26a..3509572525d 100644 --- a/integration/examples/getting-started-kustomize/skaffold.yaml +++ b/integration/examples/getting-started-kustomize/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config metadata: name: getting-started-kustomize diff --git a/integration/examples/getting-started/skaffold.yaml b/integration/examples/getting-started/skaffold.yaml index 65a4f4fdfe6..45e14901ae6 100644 --- a/integration/examples/getting-started/skaffold.yaml +++ b/integration/examples/getting-started/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/google-cloud-build/skaffold.yaml b/integration/examples/google-cloud-build/skaffold.yaml index ff2f351855b..e167e448b86 100644 --- a/integration/examples/google-cloud-build/skaffold.yaml +++ b/integration/examples/google-cloud-build/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: googleCloudBuild: diff --git a/integration/examples/helm-deployment-dependencies/skaffold.yaml b/integration/examples/helm-deployment-dependencies/skaffold.yaml index c3fc6cd06c7..ef6b2057cd3 100644 --- a/integration/examples/helm-deployment-dependencies/skaffold.yaml +++ b/integration/examples/helm-deployment-dependencies/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: tagPolicy: diff --git a/integration/examples/helm-deployment/skaffold.yaml b/integration/examples/helm-deployment/skaffold.yaml index 09baacb88b5..bb59a88ab7f 100644 --- a/integration/examples/helm-deployment/skaffold.yaml +++ b/integration/examples/helm-deployment/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/hot-reload/skaffold.yaml b/integration/examples/hot-reload/skaffold.yaml index 9a787fd98a3..a21b7e9da3b 100644 --- a/integration/examples/hot-reload/skaffold.yaml +++ b/integration/examples/hot-reload/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/jib-gradle/skaffold.yaml b/integration/examples/jib-gradle/skaffold.yaml index 75bd9a1be59..c884b0209da 100644 --- a/integration/examples/jib-gradle/skaffold.yaml +++ b/integration/examples/jib-gradle/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/jib-multimodule/skaffold.yaml b/integration/examples/jib-multimodule/skaffold.yaml index a671cc903d1..c47580d9ba1 100644 --- a/integration/examples/jib-multimodule/skaffold.yaml +++ b/integration/examples/jib-multimodule/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/jib-sync/skaffold-gradle.yaml b/integration/examples/jib-sync/skaffold-gradle.yaml index ada2bcc348f..3c20ef48099 100644 --- a/integration/examples/jib-sync/skaffold-gradle.yaml +++ b/integration/examples/jib-sync/skaffold-gradle.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/jib-sync/skaffold-maven.yaml b/integration/examples/jib-sync/skaffold-maven.yaml index 422e5da3a8c..2a0be293ceb 100644 --- a/integration/examples/jib-sync/skaffold-maven.yaml +++ b/integration/examples/jib-sync/skaffold-maven.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/jib/skaffold.yaml b/integration/examples/jib/skaffold.yaml index 1a1707d2708..b93bc04ae67 100644 --- a/integration/examples/jib/skaffold.yaml +++ b/integration/examples/jib/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/kaniko/skaffold.yaml b/integration/examples/kaniko/skaffold.yaml index 4c1badb0b35..343433e700c 100644 --- a/integration/examples/kaniko/skaffold.yaml +++ b/integration/examples/kaniko/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/kustomize/skaffold-kustomize-args.yaml b/integration/examples/kustomize/skaffold-kustomize-args.yaml index bd6e9a21ae4..4d7fbb85919 100644 --- a/integration/examples/kustomize/skaffold-kustomize-args.yaml +++ b/integration/examples/kustomize/skaffold-kustomize-args.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config deploy: kustomize: diff --git a/integration/examples/kustomize/skaffold.yaml b/integration/examples/kustomize/skaffold.yaml index 839cf213ad5..90cf54edb62 100644 --- a/integration/examples/kustomize/skaffold.yaml +++ b/integration/examples/kustomize/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config deploy: kustomize: {} diff --git a/integration/examples/microservices/skaffold.yaml b/integration/examples/microservices/skaffold.yaml index 7766da3b7b6..557f6f6e264 100644 --- a/integration/examples/microservices/skaffold.yaml +++ b/integration/examples/microservices/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/nodejs/skaffold.yaml b/integration/examples/nodejs/skaffold.yaml index aa18d1e6ca2..08c6b227ec8 100644 --- a/integration/examples/nodejs/skaffold.yaml +++ b/integration/examples/nodejs/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: diff --git a/integration/examples/profile-patches/skaffold.yaml b/integration/examples/profile-patches/skaffold.yaml index 21f66598837..ad1614c4aec 100644 --- a/integration/examples/profile-patches/skaffold.yaml +++ b/integration/examples/profile-patches/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: # only build and deploy "base-service" on main profile diff --git a/integration/examples/profiles/skaffold.yaml b/integration/examples/profiles/skaffold.yaml index f0ef59403b0..ae35a2724f2 100644 --- a/integration/examples/profiles/skaffold.yaml +++ b/integration/examples/profiles/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: # only build and deploy "world-service" on main profile diff --git a/integration/examples/react-reload/skaffold.yaml b/integration/examples/react-reload/skaffold.yaml index 1a108870e56..e628a785c04 100644 --- a/integration/examples/react-reload/skaffold.yaml +++ b/integration/examples/react-reload/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/ruby/skaffold.yaml b/integration/examples/ruby/skaffold.yaml index 58d361f4c2b..fe3073b24e2 100644 --- a/integration/examples/ruby/skaffold.yaml +++ b/integration/examples/ruby/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/structure-tests/skaffold.yaml b/integration/examples/structure-tests/skaffold.yaml index 238d314d241..d9042d97f1f 100644 --- a/integration/examples/structure-tests/skaffold.yaml +++ b/integration/examples/structure-tests/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 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 694a193fcdb..7d38302ed96 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/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/examples/templated-fields/skaffold.yaml b/integration/examples/templated-fields/skaffold.yaml index aa2b6ce0778..00ec300dc6d 100644 --- a/integration/examples/templated-fields/skaffold.yaml +++ b/integration/examples/templated-fields/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config metadata: name: my-app diff --git a/integration/testdata/build/skaffold.yaml b/integration/testdata/build/skaffold.yaml index 292a367546a..2f5c73497bc 100644 --- a/integration/testdata/build/skaffold.yaml +++ b/integration/testdata/build/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: local: diff --git a/integration/testdata/debug/skaffold.yaml b/integration/testdata/debug/skaffold.yaml index e92943bd234..72a92df4b2c 100644 --- a/integration/testdata/debug/skaffold.yaml +++ b/integration/testdata/debug/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/testdata/deploy-multiple/skaffold.yaml b/integration/testdata/deploy-multiple/skaffold.yaml index 5ad3944ac59..7181b0b0494 100644 --- a/integration/testdata/deploy-multiple/skaffold.yaml +++ b/integration/testdata/deploy-multiple/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/testdata/dev/skaffold.yaml b/integration/testdata/dev/skaffold.yaml index 03e90806daf..b05d60601ca 100644 --- a/integration/testdata/dev/skaffold.yaml +++ b/integration/testdata/dev/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/testdata/gke_loadbalancer/skaffold.yaml b/integration/testdata/gke_loadbalancer/skaffold.yaml index 3b0a2f342d8..f80863ab376 100644 --- a/integration/testdata/gke_loadbalancer/skaffold.yaml +++ b/integration/testdata/gke_loadbalancer/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/testdata/hello/skaffold.yaml b/integration/testdata/hello/skaffold.yaml index 6df607a33e3..733c42e0e96 100644 --- a/integration/testdata/hello/skaffold.yaml +++ b/integration/testdata/hello/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/testdata/init/compose/skaffold.yaml b/integration/testdata/init/compose/skaffold.yaml index 7fa20a070f6..51bc3ad7311 100644 --- a/integration/testdata/init/compose/skaffold.yaml +++ b/integration/testdata/init/compose/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config metadata: name: compose diff --git a/integration/testdata/jib/skaffold.yaml b/integration/testdata/jib/skaffold.yaml index ab9187c71db..386f15c0cb8 100644 --- a/integration/testdata/jib/skaffold.yaml +++ b/integration/testdata/jib/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/testdata/kaniko-explicit-repo/skaffold.yaml b/integration/testdata/kaniko-explicit-repo/skaffold.yaml index 46441874460..b270ba1e294 100644 --- a/integration/testdata/kaniko-explicit-repo/skaffold.yaml +++ b/integration/testdata/kaniko-explicit-repo/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/testdata/kaniko-insecure-registry/app/skaffold.yaml b/integration/testdata/kaniko-insecure-registry/app/skaffold.yaml index 65a4f4fdfe6..45e14901ae6 100644 --- a/integration/testdata/kaniko-insecure-registry/app/skaffold.yaml +++ b/integration/testdata/kaniko-insecure-registry/app/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/testdata/kaniko-insecure-registry/skaffold.yaml b/integration/testdata/kaniko-insecure-registry/skaffold.yaml index 107b9581131..ea44dae5dce 100644 --- a/integration/testdata/kaniko-insecure-registry/skaffold.yaml +++ b/integration/testdata/kaniko-insecure-registry/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config profiles: - name: build-artifact diff --git a/integration/testdata/kaniko-microservices/skaffold.yaml b/integration/testdata/kaniko-microservices/skaffold.yaml index efcb025994d..a406136c7d0 100644 --- a/integration/testdata/kaniko-microservices/skaffold.yaml +++ b/integration/testdata/kaniko-microservices/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/testdata/kaniko-sub-folder/skaffold.yaml b/integration/testdata/kaniko-sub-folder/skaffold.yaml index e7789728442..f6edcba428f 100644 --- a/integration/testdata/kaniko-sub-folder/skaffold.yaml +++ b/integration/testdata/kaniko-sub-folder/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/testdata/kaniko-target/skaffold.yaml b/integration/testdata/kaniko-target/skaffold.yaml index 32e9305dc1e..84e60e5620e 100644 --- a/integration/testdata/kaniko-target/skaffold.yaml +++ b/integration/testdata/kaniko-target/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/integration/testdata/tagPolicy/skaffold.yaml b/integration/testdata/tagPolicy/skaffold.yaml index eee3732fda0..e42c5318105 100644 --- a/integration/testdata/tagPolicy/skaffold.yaml +++ b/integration/testdata/tagPolicy/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config build: artifacts: diff --git a/pkg/skaffold/initializer/testdata/init/allcli/skaffold.yaml b/pkg/skaffold/initializer/testdata/init/allcli/skaffold.yaml index 0574309c5e5..75e32dcaf58 100644 --- a/pkg/skaffold/initializer/testdata/init/allcli/skaffold.yaml +++ b/pkg/skaffold/initializer/testdata/init/allcli/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config metadata: name: allcli diff --git a/pkg/skaffold/initializer/testdata/init/getting-started-kustomize/skaffold.yaml b/pkg/skaffold/initializer/testdata/init/getting-started-kustomize/skaffold.yaml index f5b6a689014..ff70e19aeb7 100644 --- a/pkg/skaffold/initializer/testdata/init/getting-started-kustomize/skaffold.yaml +++ b/pkg/skaffold/initializer/testdata/init/getting-started-kustomize/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config metadata: name: getting-started-kustomize diff --git a/pkg/skaffold/initializer/testdata/init/hello-no-manifest/skaffold.yaml b/pkg/skaffold/initializer/testdata/init/hello-no-manifest/skaffold.yaml index 86ec6b96667..1a59551ce36 100644 --- a/pkg/skaffold/initializer/testdata/init/hello-no-manifest/skaffold.yaml +++ b/pkg/skaffold/initializer/testdata/init/hello-no-manifest/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config metadata: name: hello diff --git a/pkg/skaffold/initializer/testdata/init/hello/skaffold.yaml b/pkg/skaffold/initializer/testdata/init/hello/skaffold.yaml index 86ec6b96667..1a59551ce36 100644 --- a/pkg/skaffold/initializer/testdata/init/hello/skaffold.yaml +++ b/pkg/skaffold/initializer/testdata/init/hello/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config metadata: name: hello diff --git a/pkg/skaffold/initializer/testdata/init/ignore-tags/skaffold.yaml b/pkg/skaffold/initializer/testdata/init/ignore-tags/skaffold.yaml index cffa038bd49..e6463b15b19 100644 --- a/pkg/skaffold/initializer/testdata/init/ignore-tags/skaffold.yaml +++ b/pkg/skaffold/initializer/testdata/init/ignore-tags/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config metadata: name: ignore-tags diff --git a/pkg/skaffold/initializer/testdata/init/microservices/skaffold.yaml b/pkg/skaffold/initializer/testdata/init/microservices/skaffold.yaml index e393272f77e..f45cc253b6d 100644 --- a/pkg/skaffold/initializer/testdata/init/microservices/skaffold.yaml +++ b/pkg/skaffold/initializer/testdata/init/microservices/skaffold.yaml @@ -1,4 +1,4 @@ -apiVersion: skaffold/v2beta7 +apiVersion: skaffold/v2beta8 kind: Config metadata: name: microservices diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 5462fd288bb..55a798d64f6 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -22,8 +22,8 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util" ) -// !!! WARNING !!! This config version is already released, please DO NOT MODIFY the structs in this file. -const Version string = "skaffold/v2beta7" +// This config version is not yet released, it is SAFE TO MODIFY the structs in this file. +const Version string = "skaffold/v2beta8" // NewSkaffoldConfig creates a SkaffoldConfig func NewSkaffoldConfig() util.VersionedConfig { diff --git a/pkg/skaffold/schema/v2beta6/upgrade.go b/pkg/skaffold/schema/v2beta6/upgrade.go index 1be6696fe29..305bd433f81 100755 --- a/pkg/skaffold/schema/v2beta6/upgrade.go +++ b/pkg/skaffold/schema/v2beta6/upgrade.go @@ -17,8 +17,8 @@ limitations under the License. package v2beta6 import ( - next "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util" + next "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v2beta7" pkgutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" ) diff --git a/pkg/skaffold/schema/v2beta6/upgrade_test.go b/pkg/skaffold/schema/v2beta6/upgrade_test.go index 156d9b19be2..87f28a63693 100755 --- a/pkg/skaffold/schema/v2beta6/upgrade_test.go +++ b/pkg/skaffold/schema/v2beta6/upgrade_test.go @@ -21,7 +21,7 @@ import ( yaml "gopkg.in/yaml.v2" - next "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + next "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v2beta7" "github.com/GoogleContainerTools/skaffold/testutil" ) diff --git a/pkg/skaffold/schema/v2beta7/config.go b/pkg/skaffold/schema/v2beta7/config.go new file mode 100755 index 00000000000..5fcb4a1bde9 --- /dev/null +++ b/pkg/skaffold/schema/v2beta7/config.go @@ -0,0 +1,1030 @@ +/* +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 v2beta7 + +import ( + v1 "k8s.io/api/core/v1" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util" +) + +// !!! WARNING !!! This config version is already released, please DO NOT MODIFY the structs in this file. +const Version string = "skaffold/v2beta7" + +// 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"` + + // Metadata holds additional information about the config. + Metadata Metadata `yaml:"metadata,omitempty"` + + // 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"` +} + +// Metadata holds an optional name of the project. +type Metadata struct { + // Name is an identifier for the project. + Name string `yaml:"name,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"` + + // PortForward describes user defined resources to port-forward. + PortForward []*PortForwardResource `yaml:"portForward,omitempty"` +} + +func (c *SkaffoldConfig) GetVersion() string { + return c.APIVersion +} + +// ResourceType describes the Kubernetes resource types used for port forwarding. +type ResourceType string + +// PortForwardResource describes a resource to port forward. +type PortForwardResource struct { + // Type is the Kubernetes type that should be port forwarded. + // Acceptable resource types include: `Service`, `Pod` and Controller resource type that has a pod spec: `ReplicaSet`, `ReplicationController`, `Deployment`, `StatefulSet`, `DaemonSet`, `Job`, `CronJob`. + Type ResourceType `yaml:"resourceType,omitempty"` + + // Name is the name of the Kubernetes resource to port forward. + Name string `yaml:"resourceName,omitempty"` + + // Namespace is the namespace of the resource to port forward. + Namespace string `yaml:"namespace,omitempty"` + + // Port is the resource port that will be forwarded. + Port int `yaml:"port,omitempty"` + + // Address is the local address to bind to. Defaults to the loopback address 127.0.0.1. + Address string `yaml:"address,omitempty"` + + // LocalPort is the local port to forward to. If the port is unavailable, Skaffold will choose a random open port to forward to. *Optional*. + LocalPort int `yaml:"localPort,omitempty"` +} + +// 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"` + + // CustomTemplateTagger *beta* tags images with a configurable template string *composed of other taggers*. + CustomTemplateTagger *CustomTemplateTagger `yaml:"customTemplate,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. + // `TreeSha`: use the full tree hash of the artifact workingdir. + // `AbbrevTreeSha`: use the abbreviated tree hash of the artifact workingdir. + Variant string `yaml:"variant,omitempty"` + + // Prefix adds a fixed prefix to the tag. + Prefix string `yaml:"prefix,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. + // For example: `{{.RELEASE}}`. + 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"` +} + +// CustomTemplateTagger *beta* tags images with a configurable template string. +type CustomTemplateTagger 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 provided components with those variables injected. + // For example: `{{.DATE}}` where DATE references a TaggerComponent. + Template string `yaml:"template,omitempty" yamltags:"required"` + + // Components lists TaggerComponents that the template (see field above) can be executed against. + Components []TaggerComponent `yaml:"components,omitempty"` +} + +// TaggerComponent *beta* is a component of CustomTemplateTagger. +type TaggerComponent struct { + // Name is an identifier for the component. + Name string `yaml:"name,omitempty"` + + // Component is a tagging strategy to be used in CustomTemplateTagger. + Component TagPolicy `yaml:",inline" yamltags:"skipTrim"` +} + +// 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"` + + // Concurrency is how many artifacts can be built concurrently. 0 means "no-limit". + // Defaults to `1`. + Concurrency *int `yaml:"concurrency,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"` + + // Logging specifies the logging mode. + // Valid modes are: + // `LOGGING_UNSPECIFIED`: The service determines the logging mode. + // `LEGACY`: Stackdriver logging and Cloud Storage logging are enabled (default). + // `GCS_ONLY`: Only Cloud Storage logging is enabled. + // See [Cloud Build Reference](https://cloud.google.com/cloud-build/docs/api/reference/rest/v1/projects.builds#loggingmode). + Logging string `yaml:"logging,omitempty"` + + // LogStreamingOption specifies the behavior when writing build logs to Google Cloud Storage. + // Valid options are: + // `STREAM_DEFAULT`: Service may automatically determine build log streaming behavior. + // `STREAM_ON`: Build logs should be streamed to Google Cloud Storage. + // `STREAM_OFF`: Build logs should not be streamed to Google Cloud Storage; they will be written when the build is completed. + // See [Cloud Build Reference](https://cloud.google.com/cloud-build/docs/api/reference/rest/v1/projects.builds#logstreamingoption). + LogStreamingOption string `yaml:"logStreamingOption,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"` + + // KanikoImage is the image that runs a Kaniko build. + // See [Cloud Builders](https://cloud.google.com/cloud-build/docs/cloud-builders). + // Defaults to `gcr.io/kaniko-project/executor`. + KanikoImage string `yaml:"kanikoImage,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"` + + // PackImage is the image that runs a Cloud Native Buildpacks build. + // See [Cloud Builders](https://cloud.google.com/cloud-build/docs/cloud-builders). + // Defaults to `gcr.io/k8s-skaffold/pack`. + PackImage string `yaml:"packImage,omitempty"` + + // Concurrency is how many artifacts can be built concurrently. 0 means "no-limit". + // Defaults to `0`. + Concurrency int `yaml:"concurrency,omitempty"` + + // WorkerPool configures a pool of workers to run the build. + WorkerPool string `yaml:"workerPool,omitempty"` +} + +// 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"` + // HostPath specifies a path on the host that is mounted to each pod as read only cache volume containing base images. + // If set, must exist on each node and prepopulated with kaniko-warmer. + HostPath string `yaml:"hostPath,omitempty"` +} + +// ClusterDetails *beta* describes how to do an on-cluster build. +type ClusterDetails struct { + // HTTPProxy for kaniko pod. + HTTPProxy string `yaml:"HTTP_PROXY,omitempty"` + + // HTTPSProxy for kaniko pod. + HTTPSProxy string `yaml:"HTTPS_PROXY,omitempty"` + + // PullSecretPath is the path to the Google Cloud service account secret key file. + PullSecretPath string `yaml:"pullSecretPath,omitempty"` + + // PullSecretName is the name of the Kubernetes secret for pulling base images + // and pushing the final image. If given, the secret needs to contain the Google Cloud + // service account secret key under the key `kaniko-secret`. + // Defaults to `kaniko-secret`. + PullSecretName string `yaml:"pullSecretName,omitempty"` + + // PullSecretMountPath is the path the pull secret will be mounted at within the running container. + PullSecretMountPath string `yaml:"pullSecretMountPath,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"` + + // ServiceAccountName describes the Kubernetes service account to use for the pod. + // Defaults to 'default'. + ServiceAccountName string `yaml:"serviceAccount,omitempty"` + + // Tolerations describes the Kubernetes tolerations for the pod. + Tolerations []v1.Toleration `yaml:"tolerations,omitempty"` + + // Annotations describes the Kubernetes annotations for the pod. + Annotations map[string]string `yaml:"annotations,omitempty"` + + // RunAsUser defines the UID to request for running the container. + // If omitted, no SeurityContext will be specified for the pod and will therefore be inherited + // from the service account. + RunAsUser *int64 `yaml:"runAsUser,omitempty"` + + // Resources define the resource requirements for the kaniko pod. + Resources *ResourceRequirements `yaml:"resources,omitempty"` + + // Concurrency is how many artifacts can be built concurrently. 0 means "no-limit". + // Defaults to `0`. + Concurrency int `yaml:"concurrency,omitempty"` + + // Volumes defines container mounts for ConfigMap and Secret resources. + Volumes []v1.Volume `yaml:"volumes,omitempty"` + + // RandomPullSecret adds a random UUID postfix to the default name of the pull secret to facilitate parallel builds, e.g. kaniko-secretdocker-cfgfd154022-c761-416f-8eb3-cf8258450b85. + RandomPullSecret bool `yaml:"randomPullSecret,omitempty"` + + // RandomDockerConfigSecret adds a random UUID postfix to the default name of the docker secret to facilitate parallel builds, e.g. docker-cfgfd154022-c761-416f-8eb3-cf8258450b85. + RandomDockerConfigSecret bool `yaml:"randomDockerConfigSecret,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 contains the `config.json` Docker configuration. + // Note that the expected secret type is not 'kubernetes.io/dockerconfigjson' but 'Opaque'. + 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"` + + // EphemeralStorage the amount of Ephemeral storage to allocate to the pod. + // For example: `1Gi` or `1000Mi`. + EphemeralStorage string `yaml:"ephemeralStorage,omitempty"` + + // ResourceStorage the amount of resource storage to allocate to the pod. + // For example: `1Gi` or `1000Mi`. + ResourceStorage string `yaml:"resourceStorage,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"` + + // StatusCheckDeadlineSeconds *beta* is the deadline for deployments to stabilize in seconds. + StatusCheckDeadlineSeconds int `yaml:"statusCheckDeadlineSeconds,omitempty"` + + // KubeContext is the Kubernetes context that Skaffold should deploy to. + // For example: `minikube`. + KubeContext string `yaml:"kubeContext,omitempty"` + + // Logs configures how container logs are printed as a result of a deployment. + Logs LogsConfig `yaml:"logs,omitempty"` +} + +// DeployType contains the specific implementation and parameters needed +// for the deploy step. All three deployer types can be used at the same +// time for hybrid workflows. +type DeployType struct { + // HelmDeploy *beta* uses the `helm` CLI to apply the charts to the cluster. + HelmDeploy *HelmDeploy `yaml:"helm,omitempty"` + + // KptDeploy *alpha* uses the `kpt` CLI to manage and deploy manifests. + KptDeploy *KptDeploy `yaml:"kpt,omitempty"` + + // 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"` + + // KustomizeDeploy *beta* uses the `kustomize` CLI to "patch" a deployment for a target environment. + KustomizeDeploy *KustomizeDeploy `yaml:"kustomize,omitempty"` +} + +// 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"` + + // DisableValidation passes the `--validate=false` flag to supported + // `kubectl` commands when enabled. + DisableValidation bool `yaml:"disableValidation,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 { + // KustomizePaths is the path to Kustomization files. + // Defaults to `["."]`. + KustomizePaths []string `yaml:"paths,omitempty"` + + // Flags are additional flags passed to `kubectl`. + Flags KubectlFlags `yaml:"flags,omitempty"` + + // BuildArgs are additional args passed to `kustomize build`. + BuildArgs []string `yaml:"buildArgs,omitempty"` +} + +// KptDeploy *alpha* uses the `kpt` CLI to manage and deploy manifests. +type KptDeploy struct { + // ApplyDir is the path to the directory to deploy to the cluster. + ApplyDir string `yaml:"applyDir,omitempty"` + + // Dir is the path to the directory to run kpt functions against. + Dir string `yaml:"dir,omitempty"` + + // Fn adds additional configurations for `kpt fn`. + Fn KptFn `yaml:"fn,omitempty"` + + // Live adds additional configurations for `kpt live`. + Live KptLive `yaml:"live,omitempty"` +} + +// KptFn adds additional configurations used when calling `kpt fn`. +type KptFn struct { + // FnPath is a directory to read functions from instead of the configuration directory. + FnPath string `yaml:"fnPath,omitempty"` + + // Image is an image to be run as a function in lieu of running functions from a directory. + Image string `yaml:"image,omitempty"` + + // NetworkName is the docker network to run the container in (default "bridge"). + NetworkName string `yaml:"networkName,omitempty"` + + // GlobalScope sets global scope for functions. + GlobalScope bool `yaml:"globalScope,omitempty"` + + // Network enables network access for functions that declare it. + Network bool `yaml:"network,omitempty"` + + // Mount is a list of storage options to mount to the fn image. + Mount []string `yaml:"mount,omitempty"` +} + +// KptLive adds additional configurations used when calling `kpt live`. +type KptLive struct { + // Apply adds additional configurations for `kpt live apply` commands. + Apply KptLiveApply `yaml:"apply,omitempty"` + + // InventoryID is the identifier for a group of applied resources. + // This configuration is used when users do not specify `KptDeploy.ApplyDir` + // and `.kpt-hydrated/inventory-template.yaml` does not exist. + InventoryID string `yaml:"inventoryID,omitempty"` + + // InventoryNamespace sets the namespace scope for `kpt live init`. + InventoryNamespace string `yaml:"inventoryNamespace,omitempty"` +} + +// KptLiveApply adds additional configurations used when calling `kpt live apply`. +type KptLiveApply struct { + // PollPeriod sets for the polling period for resource statuses. Default to 2s. + PollPeriod string `yaml:"pollPeriod,omitempty"` + + // PrunePropagationPolicy sets the propagation policy for pruning. + // Possible settings are Background, Foreground, Orphan. + // Default to "Background". + PrunePropagationPolicy string `yaml:"prunePropagationPolicy,omitempty"` + + // PruneTimeout sets the time threshold to wait for all pruned resources to be deleted. + PruneTimeout string `yaml:"pruneTimeout,omitempty"` + + // ReconcileTimeout sets the time threshold to wait for all resources to reach the current status. + ReconcileTimeout string `yaml:"reconcileTimeout,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"` + + // ArtifactOverrides are key value pairs. + // If present, Skaffold will send `--set-string` flag to Helm CLI and append all pairs after the flag. + ArtifactOverrides util.FlatMap `yaml:"artifactOverrides,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 util.FlatMap `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 util.FlatMap `yaml:"setValueTemplates,omitempty"` + + // SetFiles are key-value pairs. + // If present, Skaffold will send `--set-file` flag to Helm CLI and append all pairs after the flag. + SetFiles map[string]string `yaml:"setFiles,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 + // when upgrading a new version of a chart in subsequent dev loop deploy. + // Defaults to `false`. + RecreatePods bool `yaml:"recreatePods,omitempty"` + + // SkipBuildDependencies should build dependencies be skipped. + // Ignored when `remote: true`. + SkipBuildDependencies bool `yaml:"skipBuildDependencies,omitempty"` + + // UseHelmSecrets instructs skaffold to use secrets plugin on deployment. + UseHelmSecrets bool `yaml:"useHelmSecrets,omitempty"` + + // Remote specifies whether the chart path is remote, or exists on the host filesystem. + Remote bool `yaml:"remote,omitempty"` + + // UpgradeOnChange specifies whether to upgrade helm chart on code changes. + // Default is `true` when helm chart is local (`remote: false`). + // Default is `false` if `remote: true`. + UpgradeOnChange *bool `yaml:"upgradeOnChange,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 { + // ExplicitRegistry separates `image.registry` to the image config syntax. Useful for some charts e.g. `postgresql`. + ExplicitRegistry bool `yaml:"explicitRegistry,omitempty"` +} + +// LogsConfig configures how container logs are printed as a result of a deployment. +type LogsConfig struct { + // Prefix defines the prefix shown on each log line. Valid values are + // `container`: prefix logs lines with the name of the container. + // `podAndContainer`: prefix logs lines with the names of the pod and of the container. + // `auto`: same as `podAndContainer` except that the pod name is skipped if it's the same as the container name. + // `none`: don't add a prefix. + // Defaults to `auto`. + Prefix string `yaml:"prefix,omitempty"` +} + +// 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 *beta* lists local files synced to pods instead + // of triggering an image build when modified. + // If no files are listed, sync all the files and infer the destination. + // Defaults to `infer: ["**/*"]`. + Sync *Sync `yaml:"sync,omitempty"` + + // ArtifactType describes how to build an artifact. + ArtifactType `yaml:",inline"` +} + +// Sync *beta* specifies what files to sync into the container. +// This is a list of sync rules indicating the intent to sync for source files. +// If no files are listed, sync all the files and infer the destination. +// Defaults to `infer: ["**/*"]`. +type Sync struct { + // Manual lists manual sync rules indicating the source and destination. + Manual []*SyncRule `yaml:"manual,omitempty" yamltags:"oneOf=sync"` + + // Infer lists file patterns which may be synced into the container + // The container destination is inferred by the builder + // based on the instructions of a Dockerfile. + // Available for docker and kaniko artifacts and custom + // artifacts that declare dependencies on a dockerfile. + Infer []string `yaml:"infer,omitempty" yamltags:"oneOf=sync"` + + // Auto delegates discovery of sync rules to the build system. + // Only available for jib and buildpacks. + Auto *Auto `yaml:"auto,omitempty" yamltags:"oneOf=sync"` +} + +// SyncRule specifies which local files to sync to remote folders. +type SyncRule struct { + // Src is a glob pattern to match local paths against. + // Directories should be delimited by `/` on all platforms. + // For example: `"css/**/*.css"`. + Src string `yaml:"src,omitempty" yamltags:"required"` + + // Dest is the destination path in the container where the files should be synced to. + // For example: `"app/"` + Dest string `yaml:"dest,omitempty" yamltags:"required"` + + // Strip specifies the path prefix to remove from the source path when + // transplanting the files into the destination folder. + // For example: `"css/"` + Strip string `yaml:"strip,omitempty"` +} + +// Auto cannot be customized. +type Auto struct{} + +// Profile is 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"` + + // 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"` + + // Patches lists patches applied to the configuration. + // Patches use the JSON patch notation. + Patches []JSONPatch `yaml:"patches,omitempty"` + + // Pipeline contains the definitions to replace the default skaffold pipeline. + Pipeline `yaml:",inline"` +} + +// 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=pattern` pair. The profile is auto-activated if an Environment + // Variable `key` matches the pattern. If the pattern starts with `!`, activation + // happens if the remaining pattern is _not_ matched. The pattern matches if the + // Environment Variable value is exactly `pattern`, or the regex `pattern` is + // found in it. An empty `pattern` (e.g. `env: "key="`) always only matches if + // the Environment Variable is undefined or empty. + // 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"` + + // JibArtifact builds images using the + // [Jib plugins for Maven or Gradle](https://github.com/GoogleContainerTools/jib/). + JibArtifact *JibArtifact `yaml:"jib,omitempty" yamltags:"oneOf=artifact"` + + // KanikoArtifact builds images using [kaniko](https://github.com/GoogleContainerTools/kaniko). + KanikoArtifact *KanikoArtifact `yaml:"kaniko,omitempty" yamltags:"oneOf=artifact"` + + // BuildpackArtifact builds images using [Cloud Native Buildpacks](https://buildpacks.io/). + BuildpackArtifact *BuildpackArtifact `yaml:"buildpacks,omitempty" yamltags:"oneOf=artifact"` + + // CustomArtifact *beta* builds images using a custom build script written by the user. + CustomArtifact *CustomArtifact `yaml:"custom,omitempty" yamltags:"oneOf=artifact"` +} + +// BuildpackArtifact *alpha* describes an artifact built using [Cloud Native Buildpacks](https://buildpacks.io/). +// It can be used to build images out of project's sources without any additional configuration. +type BuildpackArtifact struct { + // Builder is the builder image used. + Builder string `yaml:"builder" yamltags:"required"` + + // RunImage overrides the stack's default run image. + RunImage string `yaml:"runImage,omitempty"` + + // Env are environment variables, in the `key=value` form, passed to the build. + // Values can use the go template syntax. + // For example: `["key1=value1", "key2=value2", "key3={{.ENV_VARIABLE}}"]`. + Env []string `yaml:"env,omitempty"` + + // Buildpacks is a list of strings, where each string is a specific buildpack to use with the builder. + // If you specify buildpacks the builder image automatic detection will be ignored. These buildpacks will be used to build the Image from your source code. + // Order matters. + Buildpacks []string `yaml:"buildpacks,omitempty"` + + // TrustBuilder indicates that the builder should be trusted. + TrustBuilder bool `yaml:"trustBuilder,omitempty"` + + // ProjectDescriptor is the path to the project descriptor file. + // Defaults to `project.toml` if it exists. + ProjectDescriptor string `yaml:"projectDescriptor,omitempty"` + + // Dependencies are the file dependencies that skaffold should watch for both rebuilding and file syncing for this artifact. + Dependencies *BuildpackDependencies `yaml:"dependencies,omitempty"` +} + +// BuildpackDependencies *alpha* is used to specify dependencies for an artifact built by buildpacks. +type BuildpackDependencies struct { + // 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 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"` +} + +// CustomArtifact *beta* 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 is the command executed to build the image. + BuildCommand string `yaml:"buildCommand,omitempty"` + // Dependencies are the file dependencies that skaffold should watch for both rebuilding and file syncing for this artifact. + Dependencies *CustomDependencies `yaml:"dependencies,omitempty"` +} + +// CustomDependencies *beta* is used to specify dependencies for an artifact built by a custom build script. +// Either `dockerfile` or `paths` should be specified for file watching to work as expected. +type CustomDependencies struct { + // Dockerfile should be set if the artifact is built from a Dockerfile, from which skaffold can determine dependencies. + Dockerfile *DockerfileDependency `yaml:"dockerfile,omitempty" yamltags:"oneOf=dependency"` + + // Command represents a custom command that skaffold executes to obtain dependencies. The output of this command *must* be a valid JSON array. + Command string `yaml:"command,omitempty" yamltags:"oneOf=dependency"` + + // 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 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"` +} + +// DockerfileDependency *beta* is used to specify a custom build artifact that is built from a Dockerfile. This allows skaffold to determine dependencies from the Dockerfile. +type DockerfileDependency struct { + // Path locates the Dockerfile relative to workspace. + Path string `yaml:"path,omitempty"` + + // BuildArgs are key/value pairs used to resolve values of `ARG` instructions in a Dockerfile. + // Values can be constants or environment variables via the go template syntax. + // For example: `{"key1": "value1", "key2": "value2", "key3": "'{{.ENV_VARIABLE}}'"}`. + BuildArgs map[string]*string `yaml:"buildArgs,omitempty"` +} + +// KanikoArtifact 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. + // It also accepts environment variables via the go template syntax. + // For example: `{"key1": "value1", "key2": "value2", "key3": "'{{.ENV_VARIABLE}}'"}`. + BuildArgs map[string]*string `yaml:"buildArgs,omitempty"` + + // Env are environment variables passed to the kaniko pod. + Env []v1.EnvVar `yaml:"env,omitempty"` + + // InitImage is the image used to run init container which mounts kaniko context. + InitImage string `yaml:"initImage,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"` + + // Reproducible is used to strip timestamps out of the built image. + Reproducible bool `yaml:"reproducible,omitempty"` + + // SkipTLS skips TLS verification when pulling and pushing the image. + SkipTLS bool `yaml:"skipTLS,omitempty"` + + // VolumeMounts are volume mounts passed to kaniko pod. + VolumeMounts []v1.VolumeMount `yaml:"volumeMounts,omitempty"` +} + +// DockerArtifact 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"` + + // NetworkMode is passed through to docker and overrides the + // network configuration of docker builder. If unset, use whatever + // is configured in the underlying docker daemon. Valid modes are + // `host`: use the host's networking stack. + // `bridge`: use the bridged network configuration. + // `none`: no networking in the container. + NetworkMode string `yaml:"network,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"` + + // NoCache used to pass in --no-cache to docker build to prevent caching. + NoCache bool `yaml:"noCache,omitempty"` +} + +// BazelArtifact 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"` +} + +// JibArtifact builds images using the +// [Jib plugins for Maven and Gradle](https://github.com/GoogleContainerTools/jib/). +type JibArtifact struct { + // Project selects which sub-project to build for multi-module builds. + Project string `yaml:"project,omitempty"` + + // Flags are additional build flags passed to the builder. + // For example: `["--no-build-cache"]`. + Flags []string `yaml:"args,omitempty"` + + // Type the Jib builder type; normally determined automatically. Valid types are + // `maven`: for Maven. + // `gradle`: for Gradle. + Type string `yaml:"type,omitempty"` +} diff --git a/pkg/skaffold/schema/v2beta7/upgrade.go b/pkg/skaffold/schema/v2beta7/upgrade.go new file mode 100755 index 00000000000..8ccfa48d0d8 --- /dev/null +++ b/pkg/skaffold/schema/v2beta7/upgrade.go @@ -0,0 +1,38 @@ +/* +Copyright 2020 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 v2beta7 + +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" +) + +// Upgrade upgrades a configuration to the next version. +// Config changes from v2beta7 to v2beta8 +func (c *SkaffoldConfig) Upgrade() (util.VersionedConfig, error) { + var newConfig next.SkaffoldConfig + pkgutil.CloneThroughJSON(c, &newConfig) + newConfig.APIVersion = next.Version + + err := util.UpgradePipelines(c, &newConfig, upgradeOnePipeline) + return &newConfig, err +} + +func upgradeOnePipeline(_, _ interface{}) error { + return nil +} diff --git a/pkg/skaffold/schema/v2beta7/upgrade_test.go b/pkg/skaffold/schema/v2beta7/upgrade_test.go new file mode 100755 index 00000000000..4e1b6d1ab14 --- /dev/null +++ b/pkg/skaffold/schema/v2beta7/upgrade_test.go @@ -0,0 +1,182 @@ +/* +Copyright 2020 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 v2beta7 + +import ( + "testing" + + yaml "gopkg.in/yaml.v2" + + next "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" + "github.com/GoogleContainerTools/skaffold/testutil" +) + +func TestUpgrade(t *testing.T) { + yaml := `apiVersion: skaffold/v2beta7 +kind: Config +build: + artifacts: + - image: gcr.io/k8s-skaffold/skaffold-example + docker: + dockerfile: path/to/Dockerfile + - image: gcr.io/k8s-skaffold/bazel + bazel: + target: //mytarget + - image: gcr.io/k8s-skaffold/jib-maven + jib: + args: ['-v', '--activate-profiles', 'prof'] + project: dir + - image: gcr.io/k8s-skaffold/jib-gradle + jib: + args: ['-v'] + - image: gcr.io/k8s-skaffold/buildpacks + buildpacks: + builder: gcr.io/buildpacks/builder:v1 + googleCloudBuild: + projectId: test-project +test: + - image: gcr.io/k8s-skaffold/skaffold-example + structureTests: + - ./test/* +deploy: + kubectl: + manifests: + - k8s-* + kustomize: + paths: + - kustomization-main +profiles: + - name: test profile + build: + artifacts: + - image: gcr.io/k8s-skaffold/skaffold-example + kaniko: + cache: {} + cluster: + pullSecretName: e2esecret + pullSecretPath: secret.json + namespace: default + test: + - image: gcr.io/k8s-skaffold/skaffold-example + structureTests: + - ./test/* + deploy: + kubectl: + manifests: + - k8s-* + kustomize: + paths: + - kustomization-test + - name: test local + build: + artifacts: + - image: gcr.io/k8s-skaffold/skaffold-example + docker: + dockerfile: path/to/Dockerfile + local: + push: false + deploy: + kubectl: + manifests: + - k8s-* + kustomize: {} +` + expected := `apiVersion: skaffold/v2beta8 +kind: Config +build: + artifacts: + - image: gcr.io/k8s-skaffold/skaffold-example + docker: + dockerfile: path/to/Dockerfile + - image: gcr.io/k8s-skaffold/bazel + bazel: + target: //mytarget + - image: gcr.io/k8s-skaffold/jib-maven + jib: + args: ['-v', '--activate-profiles', 'prof'] + project: dir + - image: gcr.io/k8s-skaffold/jib-gradle + jib: + args: ['-v'] + - image: gcr.io/k8s-skaffold/buildpacks + buildpacks: + builder: gcr.io/buildpacks/builder:v1 + googleCloudBuild: + projectId: test-project +test: + - image: gcr.io/k8s-skaffold/skaffold-example + structureTests: + - ./test/* +deploy: + kubectl: + manifests: + - k8s-* + kustomize: + paths: + - kustomization-main +profiles: + - name: test profile + build: + artifacts: + - image: gcr.io/k8s-skaffold/skaffold-example + kaniko: + cache: {} + cluster: + pullSecretName: e2esecret + pullSecretPath: secret.json + namespace: default + test: + - image: gcr.io/k8s-skaffold/skaffold-example + structureTests: + - ./test/* + deploy: + kubectl: + manifests: + - k8s-* + kustomize: + paths: + - kustomization-test + - name: test local + build: + artifacts: + - image: gcr.io/k8s-skaffold/skaffold-example + docker: + dockerfile: path/to/Dockerfile + local: + push: false + deploy: + kubectl: + manifests: + - k8s-* + kustomize: {} +` + 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 cb7ef316a93..19b1fe8ab49 100644 --- a/pkg/skaffold/schema/versions.go +++ b/pkg/skaffold/schema/versions.go @@ -61,6 +61,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v2beta4" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v2beta5" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v2beta6" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v2beta7" misc "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/yaml" ) @@ -103,6 +104,7 @@ var SchemaVersions = Versions{ {v2beta4.Version, v2beta4.NewSkaffoldConfig}, {v2beta5.Version, v2beta5.NewSkaffoldConfig}, {v2beta6.Version, v2beta6.NewSkaffoldConfig}, + {v2beta7.Version, v2beta7.NewSkaffoldConfig}, {latest.Version, latest.NewSkaffoldConfig}, } From 9cd560bb6c3c2e3bee3685b2c0ee5df90d281891 Mon Sep 17 00:00:00 2001 From: Nick Kubala Date: Wed, 9 Sep 2020 10:17:23 -0700 Subject: [PATCH 135/138] Surface error for render (#4758) --- pkg/skaffold/deploy/helm.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/skaffold/deploy/helm.go b/pkg/skaffold/deploy/helm.go index fd8eb5d25b5..45dbb54d37c 100644 --- a/pkg/skaffold/deploy/helm.go +++ b/pkg/skaffold/deploy/helm.go @@ -20,6 +20,7 @@ import ( "bufio" "bytes" "context" + "errors" "fmt" "io" "io/ioutil" @@ -287,9 +288,11 @@ func (h *HelmDeployer) Render(ctx context.Context, out io.Writer, builds []build args = append(args, "--namespace", r.Namespace) } - if err := h.exec(ctx, renderedManifests, false, args...); err != nil { - return err + outBuffer := new(bytes.Buffer) + if err := h.exec(ctx, outBuffer, false, args...); err != nil { + return errors.New(outBuffer.String()) } + renderedManifests.Write(outBuffer.Bytes()) } return outputRenderedManifests(renderedManifests.String(), filepath, out) From 0b02d1a4c572075a95c265504649f185803ef514 Mon Sep 17 00:00:00 2001 From: Kri5 Date: Thu, 10 Sep 2020 17:27:35 +0000 Subject: [PATCH 136/138] Expand home directory for setFiles in helm deployment (#4619) This allows to pass values coming from files located under the home directory, such as configuration or credentials. --- pkg/skaffold/deploy/helm.go | 8 ++++++-- pkg/skaffold/deploy/helm_test.go | 11 +++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/pkg/skaffold/deploy/helm.go b/pkg/skaffold/deploy/helm.go index 45dbb54d37c..f75fd58baef 100644 --- a/pkg/skaffold/deploy/helm.go +++ b/pkg/skaffold/deploy/helm.go @@ -559,8 +559,12 @@ func constructOverrideArgs(r *latest.HelmRelease, builds []build.Artifact, args } for k, v := range r.SetFiles { - record(v) - args = append(args, "--set-file", fmt.Sprintf("%s=%s", k, v)) + exp, err := homedir.Expand(v) + if err != nil { + return nil, fmt.Errorf("unable to expand %q: %w", v, err) + } + record(exp) + args = append(args, "--set-file", fmt.Sprintf("%s=%s", k, exp)) } envMap := map[string]string{} diff --git a/pkg/skaffold/deploy/helm_test.go b/pkg/skaffold/deploy/helm_test.go index b98130a9e92..62fcfcf45f5 100644 --- a/pkg/skaffold/deploy/helm_test.go +++ b/pkg/skaffold/deploy/helm_test.go @@ -23,6 +23,8 @@ import ( "path/filepath" "testing" + "github.com/mitchellh/go-homedir" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" @@ -112,7 +114,8 @@ var testDeployConfigSetFiles = latest.HelmDeploy{ }, Overrides: schemautil.HelmOverrides{Values: map[string]interface{}{"foo": "bar"}}, SetFiles: map[string]string{ - "value": "/some/file.yaml", + "expanded": "~/file.yaml", + "value": "/some/file.yaml", }, }}, } @@ -400,6 +403,10 @@ func TestHelmDeploy(t *testing.T) { if err != nil { t.Fatalf("tempdir: %v", err) } + home, err := homedir.Dir() + if err != nil { + t.Fatalf("Cannot get homedir: %v", err) + } tests := []struct { description string @@ -784,7 +791,7 @@ func TestHelmDeploy(t *testing.T) { CmdRunWithOutput("helm version --client", version20rc). AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig"). - AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set-file value=/some/file.yaml --kubeconfig kubeconfig"). + AndRun(fmt.Sprintf("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set-file expanded=%s --set-file value=/some/file.yaml --kubeconfig kubeconfig", filepath.Join(home, "file.yaml"))). AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"), helm: testDeployConfigSetFiles, builds: testBuilds, From ab9135046e33a9e45c063afd0abd802f94ca60e2 Mon Sep 17 00:00:00 2001 From: Gaurav <39389231+gsquared94@users.noreply.github.com> Date: Fri, 11 Sep 2020 03:13:45 +0530 Subject: [PATCH 137/138] Pass correct build args to `CreateDockerTarContext` (#4768) --- pkg/skaffold/build/cluster/kaniko.go | 5 +---- pkg/skaffold/diagnose/diagnose.go | 2 +- pkg/skaffold/docker/context.go | 5 ++--- pkg/skaffold/docker/context_test.go | 2 +- pkg/skaffold/docker/image.go | 2 +- 5 files changed, 6 insertions(+), 10 deletions(-) diff --git a/pkg/skaffold/build/cluster/kaniko.go b/pkg/skaffold/build/cluster/kaniko.go index ec1c63869ae..ba3d401ac95 100644 --- a/pkg/skaffold/build/cluster/kaniko.go +++ b/pkg/skaffold/build/cluster/kaniko.go @@ -86,10 +86,7 @@ func (b *Builder) copyKanikoBuildContext(ctx context.Context, workspace string, buildCtx, buildCtxWriter := io.Pipe() go func() { - err := docker.CreateDockerTarContext(ctx, buildCtxWriter, workspace, &latest.DockerArtifact{ - BuildArgs: artifact.BuildArgs, - DockerfilePath: artifact.DockerfilePath, - }, b.insecureRegistries) + err := docker.CreateDockerTarContext(ctx, buildCtxWriter, workspace, artifact.DockerfilePath, artifact.BuildArgs, b.insecureRegistries) if err != nil { buildCtxWriter.CloseWithError(fmt.Errorf("creating docker context: %w", err)) return diff --git a/pkg/skaffold/diagnose/diagnose.go b/pkg/skaffold/diagnose/diagnose.go index 7d2348550bc..dd24ea89768 100644 --- a/pkg/skaffold/diagnose/diagnose.go +++ b/pkg/skaffold/diagnose/diagnose.go @@ -136,7 +136,7 @@ func timeToComputeMTimes(deps []string) (time.Duration, error) { func sizeOfDockerContext(ctx context.Context, a *latest.Artifact, insecureRegistries map[string]bool) (int64, error) { buildCtx, buildCtxWriter := io.Pipe() go func() { - err := docker.CreateDockerTarContext(ctx, buildCtxWriter, a.Workspace, a.DockerArtifact, insecureRegistries) + err := docker.CreateDockerTarContext(ctx, buildCtxWriter, a.Workspace, a.DockerArtifact.DockerfilePath, a.DockerArtifact.BuildArgs, insecureRegistries) if err != nil { buildCtxWriter.CloseWithError(fmt.Errorf("creating docker context: %w", err)) return diff --git a/pkg/skaffold/docker/context.go b/pkg/skaffold/docker/context.go index dc581ad8d23..8c228391bf9 100644 --- a/pkg/skaffold/docker/context.go +++ b/pkg/skaffold/docker/context.go @@ -22,12 +22,11 @@ import ( "io" "path/filepath" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" ) -func CreateDockerTarContext(ctx context.Context, w io.Writer, workspace string, a *latest.DockerArtifact, insecureRegistries map[string]bool) error { - paths, err := GetDependencies(ctx, workspace, a.DockerfilePath, a.BuildArgs, insecureRegistries) +func CreateDockerTarContext(ctx context.Context, w io.Writer, workspace string, dockerfilePath string, buildArgs map[string]*string, insecureRegistries map[string]bool) error { + paths, err := GetDependencies(ctx, workspace, dockerfilePath, buildArgs, insecureRegistries) if err != nil { return fmt.Errorf("getting relative tar paths: %w", err) } diff --git a/pkg/skaffold/docker/context_test.go b/pkg/skaffold/docker/context_test.go index 6c30c84c212..aef027d5a2b 100644 --- a/pkg/skaffold/docker/context_test.go +++ b/pkg/skaffold/docker/context_test.go @@ -46,7 +46,7 @@ func TestDockerContext(t *testing.T) { reader, writer := io.Pipe() go func() { - err := CreateDockerTarContext(context.Background(), writer, dir, artifact, nil) + err := CreateDockerTarContext(context.Background(), writer, dir, artifact.DockerfilePath, artifact.BuildArgs, nil) if err != nil { writer.CloseWithError(err) } else { diff --git a/pkg/skaffold/docker/image.go b/pkg/skaffold/docker/image.go index 3d925c45168..ae75d1df892 100644 --- a/pkg/skaffold/docker/image.go +++ b/pkg/skaffold/docker/image.go @@ -171,7 +171,7 @@ func (l *localDaemon) Build(ctx context.Context, out io.Writer, workspace string buildCtx, buildCtxWriter := io.Pipe() go func() { - err := CreateDockerTarContext(ctx, buildCtxWriter, workspace, a, l.insecureRegistries) + err := CreateDockerTarContext(ctx, buildCtxWriter, workspace, a.DockerfilePath, buildArgs, l.insecureRegistries) if err != nil { buildCtxWriter.CloseWithError(fmt.Errorf("creating docker context: %w", err)) return From 677d665c37b4ef012c54f5f5420f49723f4cdaeb Mon Sep 17 00:00:00 2001 From: "Paul \"TBBle\" Hampson" Date: Fri, 11 Sep 2020 08:36:51 +1000 Subject: [PATCH 138/138] Clarify usage of ArtifactOverrides, ImageStrategy (#4487) * Clarify usage of ArtifactOverrides, ImageStrategy ArtifactOverrides was incorrectly claiming it needed "values file" syntax, i.e. structured YAML, but it actually takes `--set-string` syntax, i.e. dotted path. Also tied ArtifactOverrides directly to `ImageStrategy`, since the two work together. Signed-off-by: Paul "Hampy" Hampson * Remove duplicated omitempty tag Signed-off-by: Paul "Hampy" Hampson --- docs/content/en/schemas/v2beta6.json | 8 ++++---- pkg/skaffold/schema/latest/config.go | 11 +++++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/content/en/schemas/v2beta6.json b/docs/content/en/schemas/v2beta6.json index 32438e34588..bc8533b2100 100755 --- a/docs/content/en/schemas/v2beta6.json +++ b/docs/content/en/schemas/v2beta6.json @@ -1279,8 +1279,8 @@ ], "properties": { "artifactOverrides": { - "description": "key value pairs. If present, Skaffold will send `--set-string` flag to Helm CLI and append all pairs after the flag.", - "x-intellij-html-description": "key value pairs. If present, Skaffold will send --set-string flag to Helm CLI and append all pairs after the flag." + "description": "key value pairs where the key represents the parameter used in the `--set-string` Helm CLI flag to define a container image and the value corresponds to artifact i.e. `ImageName` defined in `Build.Artifacts` section. The resulting command-line is controlled by `ImageStrategy`.", + "x-intellij-html-description": "key value pairs where the key represents the parameter used in the --set-string Helm CLI flag to define a container image and the value corresponds to artifact i.e. ImageName defined in Build.Artifacts section. The resulting command-line is controlled by ImageStrategy." }, "chartPath": { "type": "string", @@ -1289,8 +1289,8 @@ }, "imageStrategy": { "$ref": "#/definitions/HelmImageStrategy", - "description": "adds image configurations to the Helm `values` file.", - "x-intellij-html-description": "adds image configurations to the Helm values file." + "description": "controls how an `ArtifactOverrides` entry is turned into `--set-string` Helm CLI flag or flags.", + "x-intellij-html-description": "controls how an ArtifactOverrides entry is turned into --set-string Helm CLI flag or flags." }, "name": { "type": "string", diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 55a798d64f6..979fcfe0954 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -604,9 +604,11 @@ type HelmRelease struct { // ValuesFiles are the paths to the Helm `values` files. ValuesFiles []string `yaml:"valuesFiles,omitempty"` - // ArtifactOverrides are key value pairs. - // If present, Skaffold will send `--set-string` flag to Helm CLI and append all pairs after the flag. - ArtifactOverrides util.FlatMap `yaml:"artifactOverrides,omitempty,omitempty"` + // ArtifactOverrides are key value pairs where the + // key represents the parameter used in the `--set-string` Helm CLI flag to define a container + // image and the value corresponds to artifact i.e. `ImageName` defined in `Build.Artifacts` section. + // The resulting command-line is controlled by `ImageStrategy`. + ArtifactOverrides util.FlatMap `yaml:"artifactOverrides,omitempty"` // Namespace is the Kubernetes namespace. Namespace string `yaml:"namespace,omitempty"` @@ -660,7 +662,8 @@ type HelmRelease struct { // Packaged parameters for packaging helm chart (`helm package`). Packaged *HelmPackaged `yaml:"packaged,omitempty"` - // ImageStrategy adds image configurations to the Helm `values` file. + // ImageStrategy controls how an `ArtifactOverrides` entry is + // turned into `--set-string` Helm CLI flag or flags. ImageStrategy HelmImageStrategy `yaml:"imageStrategy,omitempty"` }