diff --git a/integration/render_test.go b/integration/render_test.go new file mode 100644 index 00000000000..60ff769c9a6 --- /dev/null +++ b/integration/render_test.go @@ -0,0 +1,168 @@ +/* +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 ( + "bytes" + "context" + "testing" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" + "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 TestKubectlRender(t *testing.T) { + tests := []struct { + description string + builds []build.Artifact + input string + expectedOut string + }{ + { + description: "normal render", + builds: []build.Artifact{ + { + ImageName: "gcr.io/k8s-skaffold/skaffold", + Tag: "gcr.io/k8s-skaffold/skaffold:test", + }, + }, + input: `apiVersion: v1 +kind: Pod +spec: + containers: + - image: gcr.io/k8s-skaffold/skaffold + name: skaffold +`, + expectedOut: `apiVersion: v1 +kind: Pod +metadata: + namespace: default +spec: + containers: + - image: gcr.io/k8s-skaffold/skaffold:test + name: skaffold +`, + }, + { + description: "two artifacts", + 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", + }, + }, + input: `apiVersion: v1 +kind: Pod +spec: + containers: + - image: gcr.io/project/image1 + name: image1 + - image: gcr.io/project/image2 + name: image2 +`, + expectedOut: `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: "two artifacts, combined manifests", + 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", + }, + }, + input: `apiVersion: v1 +kind: Pod +spec: + containers: + - image: gcr.io/project/image1 + name: image1 +--- +apiVersion: v1 +kind: Pod +spec: + containers: + - image: gcr.io/project/image2 + name: image2 +`, + expectedOut: `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 +`, + }, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + t.NewTempDir(). + Write("deployment.yaml", test.input). + Chdir() + + deployer := deploy.NewKubectlDeployer(&runcontext.RunContext{ + WorkingDir: ".", + Cfg: latest.Pipeline{ + Deploy: latest.DeployConfig{ + DeployType: latest.DeployType{ + KubectlDeploy: &latest.KubectlDeploy{ + Manifests: []string{"deployment.yaml"}, + }, + }, + }, + }, + }) + var b bytes.Buffer + err := deployer.Render(context.Background(), &b, test.builds, "") + t.CheckErrorAndDeepEqual(false, err, test.expectedOut, b.String()) + }) + } +} diff --git a/pkg/skaffold/deploy/kubectl.go b/pkg/skaffold/deploy/kubectl.go index 577af7362d7..d5a613743cc 100644 --- a/pkg/skaffold/deploy/kubectl.go +++ b/pkg/skaffold/deploy/kubectl.go @@ -17,9 +17,12 @@ limitations under the License. package deploy import ( + "bufio" "bytes" "context" + "fmt" "io" + "os" "strings" "github.com/pkg/errors" @@ -73,65 +76,29 @@ func (k *KubectlDeployer) Labels() map[string]string { // Deploy templates the provided manifests with a simple `find and replace` and // runs `kubectl apply` on those manifests func (k *KubectlDeployer) Deploy(ctx context.Context, out io.Writer, builds []build.Artifact, labellers []Labeller) *Result { - if err := k.kubectl.CheckVersion(ctx); err != nil { - color.Default.Fprintln(out, "kubectl client version:", k.kubectl.Version(ctx)) - color.Default.Fprintln(out, err) - } + event.DeployInProgress() + manifests, err := k.renderManifests(ctx, out, builds) - manifests, err := k.readManifests(ctx) if err != nil { event.DeployFailed(err) - return NewDeployErrorResult(errors.Wrap(err, "reading manifests")) + return NewDeployErrorResult(err) } - for _, m := range k.RemoteManifests { - manifest, err := k.readRemoteManifest(ctx, m) - if err != nil { - return NewDeployErrorResult(errors.Wrap(err, "get remote manifests")) - } - - manifests = append(manifests, manifest) - } - - if len(k.originalImages) == 0 { - k.originalImages, err = manifests.GetImages() - if err != nil { - return NewDeployErrorResult(errors.Wrap(err, "get images from manifests")) - } - } - - logrus.Debugln("manifests", manifests.String()) - if len(manifests) == 0 { + event.DeployComplete() return NewDeploySuccessResult(nil) } - event.DeployInProgress() - - namespaces, err := manifests.CollectNamespaces() - if err != nil { - event.DeployInfoEvent(errors.Wrap(err, "could not fetch deployed resource namespace. "+ - "This might cause port-forward and deploy health-check to fail.")) - } - - manifests, err = manifests.ReplaceImages(builds, k.defaultRepo) - if err != nil { - event.DeployFailed(err) - return NewDeployErrorResult(errors.Wrap(err, "replacing images in manifests")) - } - manifests, err = manifests.SetLabels(merge(labellers...)) if err != nil { event.DeployFailed(err) return NewDeployErrorResult(errors.Wrap(err, "setting labels in manifests")) } - for _, transform := range manifestTransforms { - manifests, err = transform(manifests, builds, k.insecureRegistries) - if err != nil { - event.DeployFailed(err) - return NewDeployErrorResult(errors.Wrap(err, "unable to transform manifests")) - } + namespaces, err := manifests.CollectNamespaces() + if err != nil { + event.DeployInfoEvent(errors.Wrap(err, "could not fetch deployed resource namespace. "+ + "This might cause port-forward and deploy health-check to fail.")) } if err := k.kubectl.Apply(ctx, textio.NewPrefixWriter(out, " - "), manifests); err != nil { @@ -252,6 +219,68 @@ func (k *KubectlDeployer) readRemoteManifest(ctx context.Context, name string) ( return manifest.Bytes(), nil } -func (k *KubectlDeployer) Render(context.Context, io.Writer, []build.Artifact, string) error { - return errors.New("not yet implemented") +func (k *KubectlDeployer) Render(ctx context.Context, out io.Writer, builds []build.Artifact, filepath string) error { + manifests, err := k.renderManifests(ctx, out, builds) + + if err != nil { + return err + } + + manifestOut := out + if filepath != "" { + f, err := os.Open(filepath) + if err != nil { + return errors.Wrap(err, "opening file for writing manifests") + } + manifestOut = bufio.NewWriter(f) + } + + fmt.Fprintln(manifestOut, manifests.String()) + return nil +} + +func (k *KubectlDeployer) renderManifests(ctx context.Context, out io.Writer, builds []build.Artifact) (deploy.ManifestList, error) { + if err := k.kubectl.CheckVersion(ctx); err != nil { + color.Default.Fprintln(out, "kubectl client version:", k.kubectl.Version(ctx)) + color.Default.Fprintln(out, err) + } + + manifests, err := k.readManifests(ctx) + if err != nil { + return nil, errors.Wrap(err, "reading manifests") + } + + for _, m := range k.RemoteManifests { + manifest, err := k.readRemoteManifest(ctx, m) + if err != nil { + return nil, errors.Wrap(err, "get remote manifests") + } + + manifests = append(manifests, manifest) + } + + if len(k.originalImages) == 0 { + k.originalImages, err = manifests.GetImages() + if err != nil { + return nil, errors.Wrap(err, "get images from manifests") + } + } + + if len(manifests) == 0 { + return nil, nil + } + + manifests, err = manifests.ReplaceImages(builds, k.defaultRepo) + if err != nil { + return nil, errors.Wrap(err, "replacing images in manifests") + } + + for _, transform := range manifestTransforms { + manifests, err = transform(manifests, builds, k.insecureRegistries) + if err != nil { + return nil, errors.Wrap(err, "unable to transform manifests") + } + } + + return manifests, nil } diff --git a/pkg/skaffold/deploy/kubectl/cli.go b/pkg/skaffold/deploy/kubectl/cli.go index e0d61e25f57..970fd40d9d5 100644 --- a/pkg/skaffold/deploy/kubectl/cli.go +++ b/pkg/skaffold/deploy/kubectl/cli.go @@ -84,7 +84,6 @@ func (c *CLI) ReadManifests(ctx context.Context, manifests []string) (ManifestLi var manifestList ManifestList manifestList.Append(buf) - logrus.Debugln("manifests", manifestList.String()) return manifestList, nil } diff --git a/pkg/skaffold/deploy/kubectl_test.go b/pkg/skaffold/deploy/kubectl_test.go index 707b6556c95..33425b99964 100644 --- a/pkg/skaffold/deploy/kubectl_test.go +++ b/pkg/skaffold/deploy/kubectl_test.go @@ -17,6 +17,7 @@ limitations under the License. package deploy import ( + "bytes" "context" "fmt" "io/ioutil" @@ -461,28 +462,70 @@ func TestDependencies(t *testing.T) { func TestKubectlRender(t *testing.T) { tests := []struct { description string - shouldErr bool + builds []build.Artifact + input string }{ { - description: "calling render returns error", - shouldErr: true, + description: "normal render", + builds: []build.Artifact{ + { + ImageName: "gcr.io/k8s-skaffold/skaffold", + Tag: "gcr.io/k8s-skaffold/skaffold:test", + }, + }, + input: `apiVersion: v1 +kind: Pod +spec: + containers: + - image: gcr.io/k8s-skaffold/skaffold + name: skaffold +`, + }, + { + description: "two artifacts", + 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", + }, + }, + input: `apiVersion: v1 + kind: Pod + spec: + containers: + - image: gcr.io/project/image1 + name: image1 + - image: gcr.io/project/image2 + name: image2 + `, }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { + t.Override(&util.DefaultExecCommand, testutil. + CmdRunOut("kubectl version --client -ojson", kubectlVersion). + AndRunOut("kubectl --context kubecontext create --dry-run -oyaml -f deployment.yaml", test.input)) + deployer := NewKubectlDeployer(&runcontext.RunContext{ + WorkingDir: ".", Cfg: latest.Pipeline{ Deploy: latest.DeployConfig{ DeployType: latest.DeployType{ KubectlDeploy: &latest.KubectlDeploy{ - Manifests: []string{}, + Manifests: []string{"deployment.yaml"}, }, }, }, }, + KubeContext: testKubeContext, }) - actual := deployer.Render(context.Background(), ioutil.Discard, []build.Artifact{}, "tmp/dir") - t.CheckError(test.shouldErr, actual) + var b bytes.Buffer + err := deployer.Render(context.Background(), &b, test.builds, "") + t.CheckError(false, err) }) } }