github.com/GoogleContainerTools/skaffold/v2@v2.13.2/integration/deploy_test.go (about) 1 /* 2 Copyright 2019 The Skaffold Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package integration 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 "io" 24 "os" 25 "path/filepath" 26 "strings" 27 "testing" 28 29 "github.com/google/uuid" 30 31 "github.com/GoogleContainerTools/skaffold/v2/cmd/skaffold/app/flags" 32 "github.com/GoogleContainerTools/skaffold/v2/integration/skaffold" 33 "github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/util" 34 "github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/walk" 35 "github.com/GoogleContainerTools/skaffold/v2/testutil" 36 ) 37 38 func TestBuildDeploy(t *testing.T) { 39 MarkIntegrationTest(t, NeedsGcp) 40 41 ns, client := SetupNamespace(t) 42 43 outputBytes := skaffold.Build("--quiet", "--platform=linux/arm64,linux/amd64", "--cache-artifacts=false", "--tag", uuid.New().String()).InDir("examples/nodejs").InNs(ns.Name).RunOrFailOutput(t) 44 // Parse the Build Output 45 buildArtifacts, err := flags.ParseBuildOutput(outputBytes) 46 failNowIfError(t, err) 47 48 tmpDir := testutil.NewTempDir(t) 49 buildOutputFile := tmpDir.Path("build.out") 50 tmpDir.Write("build.out", string(outputBytes)) 51 52 // Run Deploy using the build output 53 // See https://github.com/GoogleContainerTools/skaffold/issues/2372 on why status-check=false 54 skaffold.Deploy("--build-artifacts", buildOutputFile, "--status-check=false").InDir("examples/nodejs").InNs(ns.Name).RunOrFail(t) 55 56 depApp := client.GetDeployment("node") 57 testutil.CheckDeepEqual(t, buildArtifacts.Builds[0].Tag, depApp.Spec.Template.Spec.Containers[0].Image) 58 } 59 60 func TestDeploy(t *testing.T) { 61 MarkIntegrationTest(t, CanRunWithoutGcp) 62 63 ns, client := SetupNamespace(t) 64 65 // `--default-repo=` is used to cancel the default repo that is set by default. 66 skaffold.Deploy("--images", "index.docker.io/library/busybox:1", "--default-repo=").InDir("examples/kustomize").InNs(ns.Name).RunOrFail(t) 67 68 dep := client.GetDeployment("kustomize-test") 69 testutil.CheckDeepEqual(t, "index.docker.io/library/busybox:1", dep.Spec.Template.Spec.Containers[0].Image) 70 } 71 72 func TestDeployWithBuildArtifacts(t *testing.T) { 73 MarkIntegrationTest(t, CanRunWithoutGcp) 74 75 ns, client := SetupNamespace(t) 76 77 // first build the artifacts and output to file 78 skaffold.Build("--file-output=images.json", "--default-repo=").InDir("examples/getting-started").RunOrFail(t) 79 80 // `--default-repo=` is used to cancel the default repo that is set by default. 81 skaffold.Deploy("--build-artifacts=images.json", "--default-repo=", "--load-images=true").InDir("examples/getting-started").InNs(ns.Name).RunOrFail(t) 82 83 pod := client.GetPod("getting-started") 84 testutil.CheckContains(t, "skaffold-example", pod.Spec.Containers[0].Image) 85 } 86 87 func TestDeployWithImages(t *testing.T) { 88 MarkIntegrationTest(t, CanRunWithoutGcp) 89 90 ns, client := SetupNamespace(t) 91 92 // first build the artifacts and output to file 93 skaffold.Build("--file-output=artifacts.json", "--default-repo=").InDir("examples/getting-started").RunOrFail(t) 94 95 var artifacts flags.BuildOutput 96 if ba, err := os.ReadFile("examples/getting-started/artifacts.json"); err != nil { 97 t.Fatal("could not read artifacts.json", err) 98 } else if err := json.Unmarshal(ba, &artifacts); err != nil { 99 t.Fatal("could not decode artifacts.json", err) 100 } 101 102 var images []string 103 for _, a := range artifacts.Builds { 104 images = append(images, a.ImageName+"="+a.Tag) 105 } 106 107 // `--default-repo=` is used to cancel the default repo that is set by default. 108 skaffold.Deploy("--images="+strings.Join(images, ","), "--default-repo=", "--load-images=true").InDir("examples/getting-started").InNs(ns.Name).RunOrFail(t) 109 110 pod := client.GetPod("getting-started") 111 testutil.CheckContains(t, "skaffold-example", pod.Spec.Containers[0].Image) 112 } 113 114 func TestDeployTail(t *testing.T) { 115 MarkIntegrationTest(t, CanRunWithoutGcp) 116 117 ns, _ := SetupNamespace(t) 118 119 // `--default-repo=` is used to cancel the default repo that is set by default. 120 out := skaffold.Deploy("--tail", "--images", "busybox:latest", "--default-repo=").InDir("testdata/deploy-hello-tail").InNs(ns.Name).RunLive(t) 121 122 WaitForLogs(t, out, "Hello world!") 123 } 124 125 func TestDeployTailDefaultNamespace(t *testing.T) { 126 MarkIntegrationTest(t, CanRunWithoutGcp) 127 128 // `--default-repo=` is used to cancel the default repo that is set by default. 129 out := skaffold.Deploy("--tail", "--images", "busybox:latest", "--default-repo=").InDir("testdata/deploy-hello-tail").RunLive(t) 130 131 defer skaffold.Delete().InDir("testdata/deploy-hello-tail").Run(t) 132 WaitForLogs(t, out, "Hello world!") 133 } 134 135 func TestDeployWithInCorrectConfig(t *testing.T) { 136 MarkIntegrationTest(t, CanRunWithoutGcp) 137 138 ns, _ := SetupNamespace(t) 139 140 // We're not providing a tag for the getting-started image 141 output, err := skaffold.Deploy().InDir("examples/getting-started").InNs(ns.Name).RunWithCombinedOutput(t) 142 if err == nil { 143 t.Errorf("expected to see an error since not every image tag is provided: %s", output) 144 } else if !strings.Contains(string(output), "no tag provided for image [skaffold-example]") { 145 t.Errorf("failed without saying the reason: %s", output) 146 } 147 } 148 149 // Verify that we can deploy without artifact details (https://github.com/GoogleContainerTools/skaffold/issues/4616) 150 func TestDeployWithoutWorkspaces(t *testing.T) { 151 MarkIntegrationTest(t, NeedsGcp) 152 153 ns, _ := SetupNamespace(t) 154 155 outputBytes := skaffold.Build("--quiet").InDir("examples/nodejs").InNs(ns.Name).RunOrFailOutput(t) 156 // Parse the Build Output 157 buildArtifacts, err := flags.ParseBuildOutput(outputBytes) 158 failNowIfError(t, err) 159 if len(buildArtifacts.Builds) != 1 { 160 t.Fatalf("expected 1 artifact to be built, but found %d", len(buildArtifacts.Builds)) 161 } 162 163 tmpDir := testutil.NewTempDir(t) 164 buildOutputFile := tmpDir.Path("build.out") 165 tmpDir.Write("build.out", string(outputBytes)) 166 copyFiles(tmpDir.Root(), "examples/nodejs/skaffold.yaml") 167 copyFiles(tmpDir.Root(), "examples/nodejs/k8s") 168 169 // Run Deploy using the build output 170 // See https://github.com/GoogleContainerTools/skaffold/issues/2372 on why status-check=false 171 skaffold.Deploy("--build-artifacts", buildOutputFile, "--status-check=false").InDir(tmpDir.Root()).InNs(ns.Name).RunOrFail(t) 172 } 173 174 func TestDeployDependenciesOrder(t *testing.T) { 175 tests := []struct { 176 description string 177 dir string 178 moduleToDeploy string 179 expectedDeployOrder []string 180 }{ 181 { 182 description: "Deploy order of entire project", 183 dir: "testdata/multi-config-dependencies-order", 184 expectedDeployOrder: []string{ 185 "module4", 186 "module3", 187 "module2", 188 "module1", 189 }, 190 }, 191 { 192 description: "Deploy order for just one part of the project", 193 dir: "testdata/multi-config-dependencies-order", 194 moduleToDeploy: "module2", 195 expectedDeployOrder: []string{ 196 "module4", 197 "module3", 198 "module2", 199 }, 200 }, 201 } 202 203 for _, test := range tests { 204 t.Run(test.description, func(t *testing.T) { 205 MarkIntegrationTest(t, CanRunWithoutGcp) 206 targetModule := []string{} 207 if test.moduleToDeploy != "" { 208 targetModule = []string{"--module", test.moduleToDeploy} 209 } 210 211 expectedFormatedDeployOrder := []string{} 212 for _, module := range test.expectedDeployOrder { 213 expectedFormatedDeployOrder = append(expectedFormatedDeployOrder, fmt.Sprintf(" - pod/%v created", module)) 214 expectedFormatedDeployOrder = append(expectedFormatedDeployOrder, "Waiting for deployments to stabilize...") 215 expectedFormatedDeployOrder = append(expectedFormatedDeployOrder, "Deployments stabilized in \\d*\\.?\\d+ms") 216 } 217 expectedFormatedDeployOrder = append([]string{"Starting deploy..."}, expectedFormatedDeployOrder...) 218 expectedOutput := strings.Join(expectedFormatedDeployOrder, "\n") 219 220 ns, _ := SetupNamespace(t) 221 outputBytes := skaffold.Run(targetModule...).InDir(test.dir).InNs(ns.Name).RunOrFailOutput(t) 222 defer skaffold.Delete().InDir(test.dir).InNs(ns.Name).RunOrFail(t) 223 224 output := string(outputBytes) 225 testutil.CheckRegex(t, expectedOutput, output) 226 }) 227 } 228 } 229 230 // Copies a file or directory tree. There are 2x3 cases: 231 // 1. If _src_ is a file, 232 // 1. and _dst_ exists and is a file then _src_ is copied into _dst_ 233 // 2. and _dst_ exists and is a directory, then _src_ is copied as _dst/$(basename src)_ 234 // 3. and _dst_ does not exist, then _src_ is copied as _dst_. 235 // 2. If _src_ is a directory, 236 // 1. and _dst_ exists and is a file, then return an error 237 // 2. and _dst_ exists and is a directory, then src is copied as _dst/$(basename src)_ 238 // 3. and _dst_ does not exist, then src is copied as _dst/src[1:]_. 239 240 func copyFiles(dst, src string) error { 241 if util.IsFile(src) { 242 switch { 243 case util.IsFile(dst): // copy _src_ to _dst_ 244 case util.IsDir(dst): // copy _src_ to _dst/src[-1] 245 dst = filepath.Join(dst, filepath.Base(src)) 246 default: // copy _src_ to _dst_ 247 if err := os.MkdirAll(filepath.Dir(dst), os.ModePerm); err != nil { 248 return err 249 } 250 } 251 in, err := os.Open(src) 252 if err != nil { 253 return err 254 } 255 out, err := os.Create(dst) 256 if err != nil { 257 return err 258 } 259 _, err = io.Copy(out, in) 260 return err 261 } else if !util.IsDir(src) { 262 return errors.New("src does not exist") 263 } 264 // so src is a directory 265 if util.IsFile(dst) { 266 return errors.New("cannot copy directory into file") 267 } 268 srcPrefix := src 269 if util.IsDir(dst) { // src is copied to _dst/$(basename src) 270 srcPrefix = filepath.Dir(src) 271 } else if err := os.MkdirAll(filepath.Dir(dst), os.ModePerm); err != nil { 272 return err 273 } 274 return walk.From(src).Unsorted().WhenIsFile().Do(func(path string, _ walk.Dirent) error { 275 rel, err := filepath.Rel(srcPrefix, path) 276 if err != nil { 277 return err 278 } 279 in, err := os.Open(path) 280 if err != nil { 281 return err 282 } 283 defer in.Close() 284 285 destFile := filepath.Join(dst, rel) 286 if err := os.MkdirAll(filepath.Dir(destFile), os.ModePerm); err != nil { 287 return err 288 } 289 290 out, err := os.Create(destFile) 291 if err != nil { 292 return err 293 } 294 295 _, err = io.Copy(out, in) 296 return err 297 }) 298 }