From ae765bed7359616ab0ff762503c721e03ed21bdf Mon Sep 17 00:00:00 2001 From: Nick Kubala Date: Wed, 20 Feb 2019 13:16:00 -0800 Subject: [PATCH 1/2] restructure integration tests --- integration/build_test.go | 67 ++++ integration/config_test.go | 173 +++++++++ integration/deploy_test.go | 53 +++ integration/dev_test.go | 66 ++++ integration/examples/test-dev-job/foo | 1 + integration/fix_test.go | 47 +++ integration/init_test.go | 118 +++++++ integration/main_test.go | 59 ++++ integration/run_test.go | 491 +------------------------- integration/sync_test.go | 56 +++ integration/util.go | 88 +++++ 11 files changed, 733 insertions(+), 486 deletions(-) create mode 100644 integration/build_test.go create mode 100644 integration/config_test.go create mode 100644 integration/deploy_test.go create mode 100644 integration/dev_test.go create mode 100644 integration/examples/test-dev-job/foo create mode 100644 integration/fix_test.go create mode 100644 integration/init_test.go create mode 100644 integration/main_test.go create mode 100644 integration/sync_test.go create mode 100644 integration/util.go diff --git a/integration/build_test.go b/integration/build_test.go new file mode 100644 index 00000000000..f7a0c99571c --- /dev/null +++ b/integration/build_test.go @@ -0,0 +1,67 @@ +// +build integration + +/* +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 ( + "os/exec" + "testing" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" +) + +func TestBuild(t *testing.T) { + tests := []struct { + description string + dir string + args []string + }{ + { + description: "docker build", + dir: "testdata/build", + }, { + description: "git tagger", + dir: "testdata/tagPolicy", + args: []string{"-p", "gitCommit"}, + }, { + description: "sha256 tagger", + dir: "testdata/tagPolicy", + args: []string{"-p", "sha256"}, + }, { + description: "dateTime tagger", + dir: "testdata/tagPolicy", + args: []string{"-p", "dateTime"}, + }, { + description: "envTemplate tagger", + dir: "testdata/tagPolicy", + args: []string{"-p", "envTemplate"}, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + buildCmd := exec.Command("skaffold", append([]string{"build"}, test.args...)...) + buildCmd.Dir = test.dir + + out, err := util.RunCmdOut(buildCmd) + if err != nil { + t.Fatalf("testing error: %v, %s", err, out) + } + }) + } +} diff --git a/integration/config_test.go b/integration/config_test.go new file mode 100644 index 00000000000..91dc60eadb2 --- /dev/null +++ b/integration/config_test.go @@ -0,0 +1,173 @@ +// +build integration + +/* +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 ( + "fmt" + "os/exec" + "strings" + "testing" + + yaml "gopkg.in/yaml.v2" + + "github.com/GoogleContainerTools/skaffold/cmd/skaffold/app/cmd/config" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" + "github.com/GoogleContainerTools/skaffold/testutil" +) + +func TestListConfig(t *testing.T) { + baseConfig := &config.Config{ + Global: &config.ContextConfig{ + DefaultRepo: "global-repository", + }, + ContextConfigs: []*config.ContextConfig{ + { + Kubecontext: "test-context", + DefaultRepo: "context-local-repository", + }, + }, + } + + c, _ := yaml.Marshal(*baseConfig) + cfg, teardown := testutil.TempFile(t, "config", c) + defer teardown() + + type testListCase struct { + description string + kubectx string + expectedOutput []string + } + + var tests = []testListCase{ + { + description: "list for test-context", + kubectx: "test-context", + expectedOutput: []string{"default-repo: context-local-repository"}, + }, + { + description: "list all", + expectedOutput: []string{ + "global:", + "default-repo: global-repository", + "kube-context: test-context", + "default-repo: context-local-repository", + }, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + args := []string{"config", "list", "-c", cfg} + if test.kubectx != "" { + args = append(args, "-k", test.kubectx) + } else { + args = append(args, "--all") + } + cmd := exec.Command("skaffold", args...) + rawOut, err := util.RunCmdOut(cmd) + if err != nil { + t.Error(err) + } + out := string(rawOut) + for _, output := range test.expectedOutput { + if !strings.Contains(out, output) { + t.Errorf("expected output %s not found in output: %s", output, out) + } + } + }) + } +} + +func TestSetConfig(t *testing.T) { + baseConfig := &config.Config{ + Global: &config.ContextConfig{ + DefaultRepo: "global-repository", + }, + ContextConfigs: []*config.ContextConfig{ + { + Kubecontext: "test-context", + DefaultRepo: "context-local-repository", + }, + }, + } + + c, _ := yaml.Marshal(*baseConfig) + cfg, teardown := testutil.TempFile(t, "config", c) + defer teardown() + + type testSetCase struct { + description string + kubectx string + key string + shouldErr bool + } + + var tests = []testSetCase{ + { + description: "set default-repo for context", + kubectx: "test-context", + key: "default-repo", + }, + { + description: "set global default-repo", + key: "default-repo", + }, + { + description: "fail to set unrecognized value", + key: "doubt-this-will-ever-be-a-config-value", + shouldErr: true, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + value := util.RandomID() + args := []string{"config", "set", test.key, value} + args = append(args, "-c", cfg) + if test.kubectx != "" { + args = append(args, "-k", test.kubectx) + } else { + args = append(args, "--global") + } + cmd := exec.Command("skaffold", args...) + if err := util.RunCmd(cmd); err != nil { + if test.shouldErr { + return + } + t.Error(err) + } + + listArgs := []string{"config", "list", "-c", cfg} + if test.kubectx != "" { + listArgs = append(listArgs, "-k", test.kubectx) + } else { + listArgs = append(listArgs, "--all") + } + listCmd := exec.Command("skaffold", listArgs...) + out, err := util.RunCmdOut(listCmd) + if err != nil { + t.Error(err) + } + t.Log(string(out)) + if !strings.Contains(string(out), fmt.Sprintf("%s: %s", test.key, value)) { + t.Errorf("value %s not set correctly", test.key) + } + }) + } +} diff --git a/integration/deploy_test.go b/integration/deploy_test.go new file mode 100644 index 00000000000..5b2ce3a86ee --- /dev/null +++ b/integration/deploy_test.go @@ -0,0 +1,53 @@ +// +build integration + +/* +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 ( + "context" + "time" + + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "testing" + + kubernetesutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" +) + +func TestDeploy(t *testing.T) { + ns, deleteNs := SetupNamespace(t) + defer deleteNs() + + RunSkaffold(t, "deploy", "examples/kustomize", ns.Name, "", nil, "--images", "index.docker.io/library/busybox:1") + + depName := "kustomize-test" + if err := kubernetesutil.WaitForDeploymentToStabilize(context.Background(), Client, ns.Name, depName, 10*time.Minute); err != nil { + t.Fatalf("Timed out waiting for deployment to stabilize") + } + + dep, err := Client.AppsV1().Deployments(ns.Name).Get(depName, meta_v1.GetOptions{}) + if err != nil { + t.Fatalf("Could not find deployment: %s %s", ns.Name, depName) + } + + if dep.Spec.Template.Spec.Containers[0].Image != "index.docker.io/library/busybox:1" { + t.Fatalf("Wrong image name in kustomized deployment: %s", dep.Spec.Template.Spec.Containers[0].Image) + } + + RunSkaffold(t, "delete", "examples/kustomize", ns.Name, "", nil) +} diff --git a/integration/dev_test.go b/integration/dev_test.go new file mode 100644 index 00000000000..d9cdefb683d --- /dev/null +++ b/integration/dev_test.go @@ -0,0 +1,66 @@ +// +build integration + +/* +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 ( + "context" + "testing" + "time" + + kubernetesutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" +) + +func TestDev(t *testing.T) { + ns, deleteNs := SetupNamespace(t) + defer deleteNs() + + Run(t, "examples/test-dev-job", "touch", "foo") + defer Run(t, "examples/test-dev-job", "rm", "foo") + + cancel := make(chan bool) + go RunSkaffoldNoFail(cancel, "dev", "examples/test-dev-job", ns.Name, "", nil) + defer func() { cancel <- true }() + + jobName := "test-dev-job" + if err := kubernetesutil.WaitForJobToStabilize(context.Background(), Client, ns.Name, jobName, 10*time.Minute); err != nil { + t.Fatalf("Timed out waiting for job to stabilize") + } + + job, err := Client.BatchV1().Jobs(ns.Name).Get(jobName, meta_v1.GetOptions{}) + if err != nil { + t.Fatalf("Could not find job: %s %s", ns.Name, jobName) + } + + // Make a change to foo so that dev is forced to delete the job and redeploy + Run(t, "examples/test-dev-job", "sh", "-c", "echo bar > foo") + + // Make sure the UID of the old Job and the UID of the new Job is different + err = wait.PollImmediate(time.Millisecond*500, 10*time.Minute, func() (bool, error) { + newJob, err := Client.BatchV1().Jobs(ns.Name).Get(job.Name, meta_v1.GetOptions{}) + if err != nil { + return false, nil + } + return job.GetUID() != newJob.GetUID(), nil + }) + if err != nil { + t.Fatalf("redeploy failed: %v", err) + } +} diff --git a/integration/examples/test-dev-job/foo b/integration/examples/test-dev-job/foo new file mode 100644 index 00000000000..5716ca5987c --- /dev/null +++ b/integration/examples/test-dev-job/foo @@ -0,0 +1 @@ +bar diff --git a/integration/fix_test.go b/integration/fix_test.go new file mode 100644 index 00000000000..75cf7b5f51b --- /dev/null +++ b/integration/fix_test.go @@ -0,0 +1,47 @@ +// +build integration + +/* +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" + "os/exec" + "testing" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" +) + +func TestFix(t *testing.T) { + ns, deleteNs := SetupNamespace(t) + defer deleteNs() + + fixCmd := exec.Command("skaffold", "fix", "-f", "skaffold.yaml") + fixCmd.Dir = "testdata/fix" + out, err := util.RunCmdOut(fixCmd) + if err != nil { + t.Fatalf("testing error: %v", err) + } + + runCmd := exec.Command("skaffold", "run", "--namespace", ns.Name, "-f", "-") + runCmd.Dir = "testdata/fix" + runCmd.Stdin = bytes.NewReader(out) + + if err := util.RunCmd(runCmd); err != nil { + t.Fatalf("testing error: %v", err) + } +} diff --git a/integration/init_test.go b/integration/init_test.go new file mode 100644 index 00000000000..445ceb45bd3 --- /dev/null +++ b/integration/init_test.go @@ -0,0 +1,118 @@ +// +build integration + +/* +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 ( + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "testing" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" +) + +func TestInit(t *testing.T) { + type testCase struct { + name string + dir string + args []string + skipSkaffoldYaml bool + } + + tests := []testCase{ + { + name: "getting-started", + dir: "../examples/getting-started", + }, + { + name: "microservices", + dir: "../examples/microservices", + args: []string{ + "-a", "leeroy-app/Dockerfile=gcr.io/k8s-skaffold/leeroy-app", + "-a", "leeroy-web/Dockerfile=gcr.io/k8s-skaffold/leeroy-web", + }, + }, + { + name: "compose", + dir: "../examples/compose", + args: []string{"--compose-file", "docker-compose.yaml"}, + skipSkaffoldYaml: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if !test.skipSkaffoldYaml { + oldYamlPath := filepath.Join(test.dir, "skaffold.yaml") + oldYaml, err := removeOldSkaffoldYaml(oldYamlPath) + if err != nil { + t.Fatalf("removing original skaffold.yaml: %s", err) + } + defer restoreOldSkaffoldYaml(oldYaml, oldYamlPath) + } + + generatedYaml := "skaffold.yaml.out" + defer func() { + err := os.Remove(filepath.Join(test.dir, generatedYaml)) + if err != nil { + t.Errorf("error removing generated skaffold yaml: %v", err) + } + }() + initArgs := []string{"init", "--force", "-f", generatedYaml} + initArgs = append(initArgs, test.args...) + initCmd := exec.Command("skaffold", initArgs...) + initCmd.Dir = test.dir + + out, err := util.RunCmdOut(initCmd) + if err != nil { + t.Fatalf("running init: %v, output: %s", err, out) + } + + runCmd := exec.Command("skaffold", "run", "-f", generatedYaml) + runCmd.Dir = test.dir + out, err = util.RunCmdOut(runCmd) + if err != nil { + t.Fatalf("running skaffold on generated yaml: %v, output: %s", err, out) + } + }) + } +} + +func removeOldSkaffoldYaml(path string) ([]byte, error) { + skaffoldYaml, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + if err = os.Remove(path); err != nil { + return nil, err + } + return skaffoldYaml, nil +} + +func restoreOldSkaffoldYaml(contents []byte, path string) error { + f, err := os.Create(path) + if err != nil { + return err + } + if _, err := f.Write(contents); err != nil { + return err + } + return nil +} diff --git a/integration/main_test.go b/integration/main_test.go new file mode 100644 index 00000000000..5e94db9a359 --- /dev/null +++ b/integration/main_test.go @@ -0,0 +1,59 @@ +// +build integration + +/* +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 ( + "flag" + "os" + "os/exec" + "testing" + + kubernetesutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" + "github.com/sirupsen/logrus" +) + +var ( + gkeZone = flag.String("gke-zone", "us-central1-a", "gke zone") + gkeClusterName = flag.String("gke-cluster-name", "integration-tests", "name of the integration test cluster") + gcpProject = flag.String("gcp-project", "k8s-skaffold", "the gcp project where the integration test cluster lives") + remote = flag.Bool("remote", false, "if true, run tests on a remote GKE cluster") + + // Client kubernetes.Interface +) + +func TestMain(m *testing.M) { + flag.Parse() + if *remote { + cmd := exec.Command("gcloud", "container", "clusters", "get-credentials", *gkeClusterName, "--zone", *gkeZone, "--project", *gcpProject) + if err := util.RunCmd(cmd); err != nil { + logrus.Fatalf("Error authenticating to GKE cluster stdout: %v", err) + } + } + + var err error + Client, err = kubernetesutil.GetClientset() + if err != nil { + logrus.Fatalf("Test setup error: getting kubernetes client: %s", err) + } + + exitCode := m.Run() + + os.Exit(exitCode) +} diff --git a/integration/run_test.go b/integration/run_test.go index a7e2293c691..c8e8077d2e5 100644 --- a/integration/run_test.go +++ b/integration/run_test.go @@ -19,59 +19,13 @@ limitations under the License. package integration import ( - "bytes" "context" - "flag" - "fmt" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "strings" "testing" "time" - "github.com/GoogleContainerTools/skaffold/cmd/skaffold/app/cmd/config" kubernetesutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" - "github.com/GoogleContainerTools/skaffold/testutil" - "github.com/sirupsen/logrus" - yaml "gopkg.in/yaml.v2" - v1 "k8s.io/api/core/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes" ) -var ( - gkeZone = flag.String("gke-zone", "us-central1-a", "gke zone") - gkeClusterName = flag.String("gke-cluster-name", "integration-tests", "name of the integration test cluster") - gcpProject = flag.String("gcp-project", "k8s-skaffold", "the gcp project where the integration test cluster lives") - remote = flag.Bool("remote", false, "if true, run tests on a remote GKE cluster") - - client kubernetes.Interface -) - -func TestMain(m *testing.M) { - flag.Parse() - if *remote { - cmd := exec.Command("gcloud", "container", "clusters", "get-credentials", *gkeClusterName, "--zone", *gkeZone, "--project", *gcpProject) - if err := util.RunCmd(cmd); err != nil { - logrus.Fatalf("Error authenticating to GKE cluster stdout: %v", err) - } - } - - var err error - client, err = kubernetesutil.GetClientset() - if err != nil { - logrus.Fatalf("Test setup error: getting kubernetes client: %s", err) - } - - exitCode := m.Run() - - os.Exit(exitCode) -} - func TestRun(t *testing.T) { tests := []struct { description string @@ -156,459 +110,24 @@ func TestRun(t *testing.T) { t.Skip("skipping remote only test") } - ns, deleteNs := setupNamespace(t) + ns, deleteNs := SetupNamespace(t) defer deleteNs() - runSkaffold(t, "run", test.dir, ns.Name, test.filename, test.env) + RunSkaffold(t, "run", test.dir, ns.Name, test.filename, test.env) for _, p := range test.pods { - if err := kubernetesutil.WaitForPodReady(context.Background(), client.CoreV1().Pods(ns.Name), p); err != nil { + if err := kubernetesutil.WaitForPodReady(context.Background(), Client.CoreV1().Pods(ns.Name), p); err != nil { t.Fatalf("Timed out waiting for pod ready") } } for _, d := range test.deployments { - if err := kubernetesutil.WaitForDeploymentToStabilize(context.Background(), client, ns.Name, d, 10*time.Minute); err != nil { + if err := kubernetesutil.WaitForDeploymentToStabilize(context.Background(), Client, ns.Name, d, 10*time.Minute); err != nil { t.Fatalf("Timed out waiting for deployment to stabilize") } } - runSkaffold(t, "delete", test.dir, ns.Name, test.filename, test.env) - }) - } -} - -func TestDeploy(t *testing.T) { - ns, deleteNs := setupNamespace(t) - defer deleteNs() - - runSkaffold(t, "deploy", "examples/kustomize", ns.Name, "", nil, "--images", "index.docker.io/library/busybox:1") - - depName := "kustomize-test" - if err := kubernetesutil.WaitForDeploymentToStabilize(context.Background(), client, ns.Name, depName, 10*time.Minute); err != nil { - t.Fatalf("Timed out waiting for deployment to stabilize") - } - - dep, err := client.AppsV1().Deployments(ns.Name).Get(depName, meta_v1.GetOptions{}) - if err != nil { - t.Fatalf("Could not find deployment: %s %s", ns.Name, depName) - } - - if dep.Spec.Template.Spec.Containers[0].Image != "index.docker.io/library/busybox:1" { - t.Fatalf("Wrong image name in kustomized deployment: %s", dep.Spec.Template.Spec.Containers[0].Image) - } - - runSkaffold(t, "delete", "examples/kustomize", ns.Name, "", nil) -} - -func TestDev(t *testing.T) { - ns, deleteNs := setupNamespace(t) - defer deleteNs() - - run(t, "examples/test-dev-job", "touch", "foo") - defer run(t, "examples/test-dev-job", "rm", "foo") - - cancel := make(chan bool) - go runSkaffoldNoFail(cancel, "dev", "examples/test-dev-job", ns.Name, "", nil) - defer func() { cancel <- true }() - - jobName := "test-dev-job" - if err := kubernetesutil.WaitForJobToStabilize(context.Background(), client, ns.Name, jobName, 10*time.Minute); err != nil { - t.Fatalf("Timed out waiting for job to stabilize") - } - - job, err := client.BatchV1().Jobs(ns.Name).Get(jobName, meta_v1.GetOptions{}) - if err != nil { - t.Fatalf("Could not find job: %s %s", ns.Name, jobName) - } - - // Make a change to foo so that dev is forced to delete the job and redeploy - run(t, "examples/test-dev-job", "sh", "-c", "echo bar > foo") - - // Make sure the UID of the old Job and the UID of the new Job is different - err = wait.PollImmediate(time.Millisecond*500, 10*time.Minute, func() (bool, error) { - newJob, err := client.BatchV1().Jobs(ns.Name).Get(job.Name, meta_v1.GetOptions{}) - if err != nil { - return false, nil - } - return job.GetUID() != newJob.GetUID(), nil - }) - if err != nil { - t.Fatalf("redeploy failed: %v", err) - } -} - -func TestDevSync(t *testing.T) { - ns, deleteNs := setupNamespace(t) - defer deleteNs() - - cancel := make(chan bool) - go runSkaffoldNoFail(cancel, "dev", "examples/test-file-sync", ns.Name, "", nil) - defer func() { cancel <- true }() - - if err := kubernetesutil.WaitForPodReady(context.Background(), client.CoreV1().Pods(ns.Name), "test-file-sync"); err != nil { - t.Fatalf("Timed out waiting for pod ready") - } - - run(t, "examples/test-file-sync", "mkdir", "-p", "test") - run(t, "examples/test-file-sync", "touch", "test/foobar") - defer run(t, "examples/test-file-sync", "rm", "-rf", "test") - - err := wait.PollImmediate(time.Millisecond*500, 1*time.Minute, func() (bool, error) { - cmd := exec.Command("kubectl", "exec", "test-file-sync", "-n", ns.Name, "--", "ls", "/test") - _, err := util.RunCmdOut(cmd) - return err == nil, nil - }) - if err != nil { - t.Fatalf("checking if /test dir exists in container: %v", err) - } -} - -func runSkaffold(t *testing.T, command, dir, namespace, filename string, env []string, additionalArgs ...string) { - if err := runSkaffoldNoFail(make(chan bool), command, dir, namespace, filename, env, additionalArgs...); err != nil { - t.Fatalf("skaffold delete: %v", err) - } -} - -func runSkaffoldNoFail(cancel chan bool, command, dir, namespace, filename string, env []string, additionalArgs ...string) error { - args := []string{command, "--namespace", namespace} - if filename != "" { - args = append(args, "-f", filename) - } - args = append(args, additionalArgs...) - - cmd := exec.Command("skaffold", args...) - cmd.Dir = dir - cmd.Env = append(os.Environ(), env...) - cmd.Stderr = os.Stderr - cmd.Stdout = os.Stdout - - cmd.Start() - - result := make(chan error) - go func() { - err := cmd.Wait() - result <- err - }() - - select { - case err := <-result: - return err - case <-cancel: - return cmd.Process.Kill() - } -} - -func run(t *testing.T, dir, command string, args ...string) { - cmd := exec.Command(command, args...) - cmd.Dir = dir - if output, err := util.RunCmdOut(cmd); err != nil { - t.Fatalf("running command [%s %v]: %s %v", command, args, output, err) - } -} - -func setupNamespace(t *testing.T) (*v1.Namespace, func()) { - ns, err := client.CoreV1().Namespaces().Create(&v1.Namespace{ - ObjectMeta: meta_v1.ObjectMeta{ - GenerateName: "skaffold", - }, - }) - if err != nil { - t.Fatalf("creating namespace: %s", err) - } - - return ns, func() { - client.CoreV1().Namespaces().Delete(ns.Name, &meta_v1.DeleteOptions{}) - } -} - -func TestFix(t *testing.T) { - ns, deleteNs := setupNamespace(t) - defer deleteNs() - - fixCmd := exec.Command("skaffold", "fix", "-f", "skaffold.yaml") - fixCmd.Dir = "testdata/fix" - out, err := util.RunCmdOut(fixCmd) - if err != nil { - t.Fatalf("testing error: %v", err) - } - - runCmd := exec.Command("skaffold", "run", "--namespace", ns.Name, "-f", "-") - runCmd.Dir = "testdata/fix" - runCmd.Stdin = bytes.NewReader(out) - - if err := util.RunCmd(runCmd); err != nil { - t.Fatalf("testing error: %v", err) - } -} - -func TestBuild(t *testing.T) { - tests := []struct { - description string - dir string - args []string - }{ - { - description: "docker build", - dir: "testdata/build", - }, { - description: "git tagger", - dir: "testdata/tagPolicy", - args: []string{"-p", "gitCommit"}, - }, { - description: "sha256 tagger", - dir: "testdata/tagPolicy", - args: []string{"-p", "sha256"}, - }, { - description: "dateTime tagger", - dir: "testdata/tagPolicy", - args: []string{"-p", "dateTime"}, - }, { - description: "envTemplate tagger", - dir: "testdata/tagPolicy", - args: []string{"-p", "envTemplate"}, - }, - } - - for _, test := range tests { - t.Run(test.description, func(t *testing.T) { - buildCmd := exec.Command("skaffold", append([]string{"build"}, test.args...)...) - buildCmd.Dir = test.dir - - out, err := util.RunCmdOut(buildCmd) - if err != nil { - t.Fatalf("testing error: %v, %s", err, out) - } - }) - } -} - -func TestListConfig(t *testing.T) { - baseConfig := &config.Config{ - Global: &config.ContextConfig{ - DefaultRepo: "global-repository", - }, - ContextConfigs: []*config.ContextConfig{ - { - Kubecontext: "test-context", - DefaultRepo: "context-local-repository", - }, - }, - } - - c, _ := yaml.Marshal(*baseConfig) - cfg, teardown := testutil.TempFile(t, "config", c) - defer teardown() - - type testListCase struct { - description string - kubectx string - expectedOutput []string - } - - var tests = []testListCase{ - { - description: "list for test-context", - kubectx: "test-context", - expectedOutput: []string{"default-repo: context-local-repository"}, - }, - { - description: "list all", - expectedOutput: []string{ - "global:", - "default-repo: global-repository", - "kube-context: test-context", - "default-repo: context-local-repository", - }, - }, - } - - for _, test := range tests { - t.Run(test.description, func(t *testing.T) { - args := []string{"config", "list", "-c", cfg} - if test.kubectx != "" { - args = append(args, "-k", test.kubectx) - } else { - args = append(args, "--all") - } - cmd := exec.Command("skaffold", args...) - rawOut, err := util.RunCmdOut(cmd) - if err != nil { - t.Error(err) - } - out := string(rawOut) - for _, output := range test.expectedOutput { - if !strings.Contains(out, output) { - t.Errorf("expected output %s not found in output: %s", output, out) - } - } - }) - } -} - -func TestInit(t *testing.T) { - type testCase struct { - name string - dir string - args []string - skipSkaffoldYaml bool - } - - tests := []testCase{ - { - name: "getting-started", - dir: "../examples/getting-started", - }, - { - name: "microservices", - dir: "../examples/microservices", - args: []string{ - "-a", "leeroy-app/Dockerfile=gcr.io/k8s-skaffold/leeroy-app", - "-a", "leeroy-web/Dockerfile=gcr.io/k8s-skaffold/leeroy-web", - }, - }, - { - name: "compose", - dir: "../examples/compose", - args: []string{"--compose-file", "docker-compose.yaml"}, - skipSkaffoldYaml: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - if !test.skipSkaffoldYaml { - oldYamlPath := filepath.Join(test.dir, "skaffold.yaml") - oldYaml, err := removeOldSkaffoldYaml(oldYamlPath) - if err != nil { - t.Fatalf("removing original skaffold.yaml: %s", err) - } - defer restoreOldSkaffoldYaml(oldYaml, oldYamlPath) - } - - generatedYaml := "skaffold.yaml.out" - defer func() { - err := os.Remove(filepath.Join(test.dir, generatedYaml)) - if err != nil { - t.Errorf("error removing generated skaffold yaml: %v", err) - } - }() - initArgs := []string{"init", "--force", "-f", generatedYaml} - initArgs = append(initArgs, test.args...) - initCmd := exec.Command("skaffold", initArgs...) - initCmd.Dir = test.dir - - out, err := util.RunCmdOut(initCmd) - if err != nil { - t.Fatalf("running init: %v, output: %s", err, out) - } - - runCmd := exec.Command("skaffold", "run", "-f", generatedYaml) - runCmd.Dir = test.dir - out, err = util.RunCmdOut(runCmd) - if err != nil { - t.Fatalf("running skaffold on generated yaml: %v, output: %s", err, out) - } - }) - } -} - -func TestSetConfig(t *testing.T) { - baseConfig := &config.Config{ - Global: &config.ContextConfig{ - DefaultRepo: "global-repository", - }, - ContextConfigs: []*config.ContextConfig{ - { - Kubecontext: "test-context", - DefaultRepo: "context-local-repository", - }, - }, - } - - c, _ := yaml.Marshal(*baseConfig) - cfg, teardown := testutil.TempFile(t, "config", c) - defer teardown() - - type testSetCase struct { - description string - kubectx string - key string - shouldErr bool - } - - var tests = []testSetCase{ - { - description: "set default-repo for context", - kubectx: "test-context", - key: "default-repo", - }, - { - description: "set global default-repo", - key: "default-repo", - }, - { - description: "fail to set unrecognized value", - key: "doubt-this-will-ever-be-a-config-value", - shouldErr: true, - }, - } - - for _, test := range tests { - t.Run(test.description, func(t *testing.T) { - value := util.RandomID() - args := []string{"config", "set", test.key, value} - args = append(args, "-c", cfg) - if test.kubectx != "" { - args = append(args, "-k", test.kubectx) - } else { - args = append(args, "--global") - } - cmd := exec.Command("skaffold", args...) - if err := util.RunCmd(cmd); err != nil { - if test.shouldErr { - return - } - t.Error(err) - } - - listArgs := []string{"config", "list", "-c", cfg} - if test.kubectx != "" { - listArgs = append(listArgs, "-k", test.kubectx) - } else { - listArgs = append(listArgs, "--all") - } - listCmd := exec.Command("skaffold", listArgs...) - out, err := util.RunCmdOut(listCmd) - if err != nil { - t.Error(err) - } - t.Log(string(out)) - if !strings.Contains(string(out), fmt.Sprintf("%s: %s", test.key, value)) { - t.Errorf("value %s not set correctly", test.key) - } + RunSkaffold(t, "delete", test.dir, ns.Name, test.filename, test.env) }) } } - -func removeOldSkaffoldYaml(path string) ([]byte, error) { - skaffoldYaml, err := ioutil.ReadFile(path) - if err != nil { - return nil, err - } - if err = os.Remove(path); err != nil { - return nil, err - } - return skaffoldYaml, nil -} - -func restoreOldSkaffoldYaml(contents []byte, path string) error { - f, err := os.Create(path) - if err != nil { - return err - } - if _, err := f.Write(contents); err != nil { - return err - } - return nil -} diff --git a/integration/sync_test.go b/integration/sync_test.go new file mode 100644 index 00000000000..57af1c6cfe6 --- /dev/null +++ b/integration/sync_test.go @@ -0,0 +1,56 @@ +// +build integration + +/* +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 ( + "context" + "os/exec" + "testing" + "time" + + kubernetesutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" + "k8s.io/apimachinery/pkg/util/wait" +) + +func TestDevSync(t *testing.T) { + ns, deleteNs := SetupNamespace(t) + defer deleteNs() + + cancel := make(chan bool) + go RunSkaffoldNoFail(cancel, "dev", "examples/test-file-sync", ns.Name, "", nil) + defer func() { cancel <- true }() + + if err := kubernetesutil.WaitForPodReady(context.Background(), Client.CoreV1().Pods(ns.Name), "test-file-sync"); err != nil { + t.Fatalf("Timed out waiting for pod ready") + } + + Run(t, "examples/test-file-sync", "mkdir", "-p", "test") + Run(t, "examples/test-file-sync", "touch", "test/foobar") + defer Run(t, "examples/test-file-sync", "rm", "-rf", "test") + + err := wait.PollImmediate(time.Millisecond*500, 1*time.Minute, func() (bool, error) { + cmd := exec.Command("kubectl", "exec", "test-file-sync", "-n", ns.Name, "--", "ls", "/test") + _, err := util.RunCmdOut(cmd) + return err == nil, nil + }) + if err != nil { + t.Fatalf("checking if /test dir exists in container: %v", err) + } +} diff --git a/integration/util.go b/integration/util.go new file mode 100644 index 00000000000..d47bd2b2c59 --- /dev/null +++ b/integration/util.go @@ -0,0 +1,88 @@ +/* +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 ( + "os" + "os/exec" + "testing" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" + v1 "k8s.io/api/core/v1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +var Client kubernetes.Interface + +func RunSkaffold(t *testing.T, command, dir, namespace, filename string, env []string, additionalArgs ...string) { + if err := RunSkaffoldNoFail(make(chan bool), command, dir, namespace, filename, env, additionalArgs...); err != nil { + t.Fatalf("skaffold delete: %v", err) + } +} + +func RunSkaffoldNoFail(cancel chan bool, command, dir, namespace, filename string, env []string, additionalArgs ...string) error { + args := []string{command, "--namespace", namespace} + if filename != "" { + args = append(args, "-f", filename) + } + args = append(args, additionalArgs...) + + cmd := exec.Command("skaffold", args...) + cmd.Dir = dir + cmd.Env = append(os.Environ(), env...) + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + + cmd.Start() + + result := make(chan error) + go func() { + err := cmd.Wait() + result <- err + }() + + select { + case err := <-result: + return err + case <-cancel: + return cmd.Process.Kill() + } +} + +func Run(t *testing.T, dir, command string, args ...string) { + cmd := exec.Command(command, args...) + cmd.Dir = dir + if output, err := util.RunCmdOut(cmd); err != nil { + t.Fatalf("running command [%s %v]: %s %v", command, args, output, err) + } +} + +func SetupNamespace(t *testing.T) (*v1.Namespace, func()) { + ns, err := Client.CoreV1().Namespaces().Create(&v1.Namespace{ + ObjectMeta: meta_v1.ObjectMeta{ + GenerateName: "skaffold", + }, + }) + if err != nil { + t.Fatalf("creating namespace: %s", err) + } + + return ns, func() { + Client.CoreV1().Namespaces().Delete(ns.Name, &meta_v1.DeleteOptions{}) + } +} From 36ed26f6d749133ef9ef74b804360f94829590c5 Mon Sep 17 00:00:00 2001 From: Nick Kubala Date: Thu, 21 Feb 2019 13:53:12 -0800 Subject: [PATCH 2/2] [hack] add 5 second wait in TestDev to give the watcher time to start up --- integration/dev_test.go | 2 ++ integration/examples/test-dev-job/foo | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 integration/examples/test-dev-job/foo diff --git a/integration/dev_test.go b/integration/dev_test.go index d9cdefb683d..2e46ef68ff7 100644 --- a/integration/dev_test.go +++ b/integration/dev_test.go @@ -49,6 +49,8 @@ func TestDev(t *testing.T) { t.Fatalf("Could not find job: %s %s", ns.Name, jobName) } + time.Sleep(5 * time.Second) + // Make a change to foo so that dev is forced to delete the job and redeploy Run(t, "examples/test-dev-job", "sh", "-c", "echo bar > foo") diff --git a/integration/examples/test-dev-job/foo b/integration/examples/test-dev-job/foo deleted file mode 100644 index 5716ca5987c..00000000000 --- a/integration/examples/test-dev-job/foo +++ /dev/null @@ -1 +0,0 @@ -bar