github.com/GoogleContainerTools/skaffold/v2@v2.13.2/integration/run_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 "fmt" 21 "os" 22 "path/filepath" 23 "strings" 24 "testing" 25 "time" 26 27 "github.com/google/uuid" 28 "k8s.io/apimachinery/pkg/util/wait" 29 30 "github.com/GoogleContainerTools/skaffold/v2/integration/skaffold" 31 "github.com/GoogleContainerTools/skaffold/v2/testutil" 32 ) 33 34 const ( 35 emptydir = "testdata/empty-dir" 36 ) 37 38 // Note: `custom-buildx` is not included as it depends on having a 39 // `skaffold-builder` builder configured and a registry to push to. 40 // TODO: remove nolint once we've reenabled integration tests 41 // 42 //nolint:golint,unused 43 var tests = []struct { 44 description string 45 dir string 46 args []string 47 deployments []string 48 pods []string 49 env []string 50 targetLog string 51 }{ 52 { 53 description: "copying-empty-directory", 54 dir: emptydir, 55 pods: []string{"empty-dir"}, 56 targetLog: "Hello world!", 57 }, 58 { 59 description: "getting-started", 60 dir: "examples/getting-started", 61 pods: []string{"getting-started"}, 62 targetLog: "Hello world!", 63 }, 64 { 65 description: "ko", 66 dir: "examples/ko", 67 deployments: []string{"ko"}, 68 }, 69 { 70 description: "nodejs", 71 dir: "examples/nodejs", 72 deployments: []string{"node"}, 73 }, 74 { 75 description: "structure-tests", 76 dir: "examples/structure-tests", 77 pods: []string{"getting-started"}, 78 }, 79 { 80 description: "custom-tests", 81 dir: "examples/custom-tests", 82 pods: []string{"custom-test"}, 83 }, 84 { 85 description: "microservices", 86 dir: "examples/microservices", 87 // See https://github.com/GoogleContainerTools/skaffold/issues/2372 88 args: []string{"--status-check=false"}, 89 deployments: []string{"leeroy-app", "leeroy-web"}, 90 }, 91 { 92 description: "multi-config-microservices", 93 dir: "examples/multi-config-microservices", 94 deployments: []string{"leeroy-app", "leeroy-web"}, 95 }, 96 { 97 description: "remote-multi-config-microservices", 98 dir: "examples/remote-multi-config-microservices", 99 deployments: []string{"leeroy-app", "leeroy-web"}, 100 }, 101 { 102 description: "envTagger", 103 dir: "examples/tagging-with-environment-variables", 104 pods: []string{"getting-started"}, 105 env: []string{"FOO=foo"}, 106 }, 107 { 108 description: "bazel", 109 dir: "examples/bazel", 110 pods: []string{"bazel"}, 111 }, 112 { 113 description: "bazel oci", 114 dir: "testdata/bazel-rules-oci", 115 deployments: []string{"helloweb"}, 116 }, 117 { 118 description: "bazel oci sub-directory", 119 dir: "testdata/bazel-rules-oci", 120 args: []string{"-p", "target-with-package"}, 121 deployments: []string{"helloweb"}, 122 }, 123 { 124 description: "jib", 125 dir: "testdata/jib", 126 deployments: []string{"web"}, 127 }, 128 { 129 description: "jib gradle", 130 dir: "examples/jib-gradle", 131 deployments: []string{"web"}, 132 }, 133 { 134 description: "profiles", 135 dir: "examples/profiles", 136 args: []string{"-p", "minikube-profile"}, 137 pods: []string{"hello-service"}, 138 }, 139 { 140 description: "multiple deployers", 141 dir: "testdata/deploy-multiple", 142 pods: []string{"deploy-kubectl", "deploy-kustomize"}, 143 }, 144 { 145 description: "custom builder", 146 dir: "examples/custom", 147 pods: []string{"getting-started-custom"}, 148 }, 149 { 150 description: "helm templating charPath", 151 dir: "testdata/helm", 152 args: []string{"-p", "helm-templating-charPath"}, 153 deployments: []string{"skaffold-helm"}, 154 env: []string{"FOO=skaffold-helm"}, 155 }, 156 // TODO(#8811): Enable this test when issue is solve. 157 // { 158 // description: "buildpacks Go", 159 // dir: "examples/buildpacks", 160 // deployments: []string{"web"}, 161 // }, 162 // TODO(#8811): Enable this test when issue is solve. 163 // { 164 // description: "buildpacks NodeJS", 165 // dir: "examples/buildpacks-node", 166 // deployments: []string{"web"}, 167 // }, 168 // TODO(#8811): Enable this test when issue is solve. 169 // { 170 // description: "buildpacks Python", 171 // dir: "examples/buildpacks-python", 172 // deployments: []string{"web"}, 173 // }, 174 // TODO(#8811): Enable this test when issue is solve. 175 // { 176 // description: "buildpacks Java", 177 // dir: "examples/buildpacks-java", 178 // deployments: []string{"web"}, 179 // }, 180 { 181 description: "kustomize", 182 dir: "examples/getting-started-kustomize", 183 deployments: []string{"skaffold-kustomize-dev"}, 184 targetLog: "Hello world!", 185 }, 186 { 187 description: "helm", 188 dir: "examples/helm-deployment", 189 deployments: []string{"skaffold-helm"}, 190 targetLog: "Hello world!", 191 }, 192 { 193 description: "multiple renderers mixed in", 194 dir: "examples/multiple-renderers", 195 deployments: []string{"frontend", "backend", "go-guestbook-mongodb"}, 196 }, 197 { 198 description: "multiple renderers mixed in", 199 dir: "examples/multiple-renderers", 200 args: []string{"-p", "mix-deploy"}, 201 deployments: []string{"frontend", "backend", "go-guestbook-mongodb"}, 202 }, 203 } 204 205 func TestRun(t *testing.T) { 206 for _, test := range tests { 207 t.Run(test.description, func(t *testing.T) { 208 MarkIntegrationTest(t, CanRunWithoutGcp) 209 210 ns, client := SetupNamespace(t) 211 args := append(test.args, "--cache-artifacts=false") 212 if test.dir == emptydir { 213 err := os.MkdirAll(filepath.Join(test.dir, "emptydir"), 0755) 214 t.Log("Creating empty directory") 215 if err != nil { 216 t.Errorf("Error creating empty dir: %s", err) 217 } 218 } 219 skaffold.Run(args...).InDir(test.dir).InNs(ns.Name).WithEnv(test.env).RunOrFail(t) 220 221 client.WaitForPodsReady(test.pods...) 222 client.WaitForDeploymentsToStabilize(test.deployments...) 223 224 skaffold.Delete().InDir(test.dir).InNs(ns.Name).WithEnv(test.env).RunOrFail(t) 225 }) 226 } 227 } 228 229 func TestRunTail(t *testing.T) { 230 for _, test := range tests { 231 t.Run(test.description, func(t *testing.T) { 232 MarkIntegrationTest(t, CanRunWithoutGcp) 233 234 if test.targetLog == "" { 235 t.SkipNow() 236 } 237 if test.dir == emptydir { 238 err := os.MkdirAll(filepath.Join(test.dir, "emptydir"), 0755) 239 t.Log("Creating empty directory") 240 if err != nil { 241 t.Errorf("Error creating empty dir: %s", err) 242 } 243 } 244 ns, _ := SetupNamespace(t) 245 246 args := append(test.args, "--tail") 247 out := skaffold.Run(args...).InDir(test.dir).InNs(ns.Name).WithEnv(test.env).RunLive(t) 248 249 WaitForLogs(t, out, test.targetLog) 250 251 skaffold.Delete().InDir(test.dir).InNs(ns.Name).WithEnv(test.env).RunOrFail(t) 252 }) 253 } 254 } 255 256 func TestRunTailDefaultNamespace(t *testing.T) { 257 for _, test := range tests { 258 t.Run(test.description, func(t *testing.T) { 259 MarkIntegrationTest(t, CanRunWithoutGcp) 260 261 if test.targetLog == "" { 262 t.SkipNow() 263 } 264 if test.dir == emptydir { 265 err := os.MkdirAll(filepath.Join(test.dir, "emptydir"), 0755) 266 t.Log("Creating empty directory") 267 if err != nil { 268 t.Errorf("Error creating empty dir: %s", err) 269 } 270 } 271 272 args := append(test.args, "--tail") 273 out := skaffold.Run(args...).InDir(test.dir).WithEnv(test.env).RunLive(t) 274 defer skaffold.Delete().InDir(test.dir).WithEnv(test.env).RunOrFail(t) 275 WaitForLogs(t, out, test.targetLog) 276 }) 277 } 278 } 279 280 func TestRunTailTolerateFailuresUntilDeadline(t *testing.T) { 281 var tsts = []struct { 282 description string 283 dir string 284 args []string 285 deployments []string 286 env []string 287 targetLogOne string 288 targetLogTwo string 289 }{ 290 { 291 description: "status-check-tolerance", 292 dir: "testdata/status-check-tolerance", 293 args: []string{"--tolerate-failures-until-deadline"}, 294 deployments: []string{"tolerance-check"}, 295 targetLogOne: "container will exit with error", 296 targetLogTwo: "Hello world!", 297 env: []string{fmt.Sprintf("STOP_FAILING_TIME=%d", time.Now().Unix()+10)}, 298 }, 299 } 300 301 for _, test := range tsts { 302 t.Run(test.description, func(t *testing.T) { 303 MarkIntegrationTest(t, CanRunWithoutGcp) 304 if test.targetLogOne == "" || test.targetLogTwo == "" { 305 t.SkipNow() 306 } 307 ns, _ := SetupNamespace(t) 308 309 args := append(test.args, "--tail") 310 out := skaffold.Run(args...).InDir(test.dir).InNs(ns.Name).WithEnv(test.env).RunLive(t) 311 defer skaffold.Delete().InDir(test.dir).InNs(ns.Name).WithEnv(test.env).Run(t) 312 WaitForLogs(t, out, test.targetLogOne) 313 WaitForLogs(t, out, test.targetLogTwo) 314 }) 315 } 316 } 317 318 func TestRunRenderOnly(t *testing.T) { 319 MarkIntegrationTest(t, CanRunWithoutGcp) 320 321 testutil.Run(t, "write rendered manifest to provided filepath", func(tu *testutil.T) { 322 tmpDir := tu.NewTempDir() 323 renderPath := tmpDir.Path("output.yaml") 324 325 test := struct { 326 description string 327 renderPath string 328 args []string 329 dir string 330 pods []string 331 }{ 332 args: []string{"--digest-source=local", "--render-only", "--render-output", renderPath}, 333 dir: "examples/getting-started", 334 pods: []string{"getting-started"}, 335 } 336 337 skaffold.Run(test.args...).InDir(test.dir).RunOrFail(t) 338 339 dat, err := os.ReadFile(renderPath) 340 tu.CheckNoError(err) 341 342 tu.CheckMatches("name: getting-started", string(dat)) 343 }) 344 } 345 346 func TestRunGCPOnly(t *testing.T) { 347 tests := []struct { 348 description string 349 dir string 350 args []string 351 deployments []string 352 pods []string 353 skipCrossPlatform bool 354 }{ 355 { 356 description: "Google Cloud Build", 357 dir: "examples/google-cloud-build", 358 pods: []string{"getting-started"}, 359 }, 360 { 361 description: "Google Cloud Build with sub folder", 362 dir: "testdata/gcb-sub-folder", 363 pods: []string{"getting-started"}, 364 }, 365 { 366 description: "Google Cloud Build with location", 367 dir: "testdata/gcb-with-location", 368 pods: []string{"getting-started"}, 369 }, 370 { 371 description: "Google Cloud Build with source artifact dependencies", 372 dir: "testdata/multi-config-pods", 373 args: []string{"-p", "gcb"}, 374 pods: []string{"module1", "module2"}, 375 }, 376 { 377 description: "Google Cloud Build with Kaniko", 378 dir: "examples/gcb-kaniko", 379 pods: []string{"getting-started-kaniko"}, 380 // building machines on gcb are linux/amd64, kaniko doesn't support cross-platform builds. 381 skipCrossPlatform: true, 382 }, 383 { 384 description: "kaniko", 385 dir: "examples/kaniko", 386 pods: []string{"getting-started-kaniko"}, 387 }, 388 { 389 description: "kaniko with target", 390 dir: "testdata/kaniko-target", 391 pods: []string{"getting-started-kaniko"}, 392 }, 393 { 394 description: "kaniko with sub folder", 395 dir: "testdata/kaniko-sub-folder", 396 pods: []string{"getting-started-kaniko"}, 397 }, 398 { 399 description: "kaniko microservices", 400 dir: "testdata/kaniko-microservices", 401 deployments: []string{"leeroy-app", "leeroy-web"}, 402 }, 403 { 404 description: "jib in googlecloudbuild", 405 dir: "testdata/jib", 406 args: []string{"-p", "gcb"}, 407 deployments: []string{"web"}, 408 }, 409 { 410 description: "jib gradle in googlecloudbuild", 411 dir: "examples/jib-gradle", 412 args: []string{"-p", "gcb"}, 413 deployments: []string{"web"}, 414 }, 415 { 416 description: "buildpacks on Cloud Build", 417 dir: "examples/buildpacks", 418 args: []string{"-p", "gcb"}, 419 deployments: []string{"web"}, 420 // buildpacks doesn't support arm64 builds. 421 skipCrossPlatform: true, 422 }, 423 } 424 for _, test := range tests { 425 if (os.Getenv("GKE_CLUSTER_NAME") == "integration-tests-arm" || os.Getenv("GKE_CLUSTER_NAME") == "integration-tests-hybrid") && test.skipCrossPlatform { 426 continue 427 } 428 t.Run(test.description, func(t *testing.T) { 429 MarkIntegrationTest(t, NeedsGcp) 430 ns, client := SetupNamespace(t) 431 432 test.args = append(test.args, "--tag", uuid.New().String()) 433 434 skaffold.Run(test.args...).InDir(test.dir).InNs(ns.Name).RunOrFail(t) 435 436 client.WaitForPodsReady(test.pods...) 437 client.WaitForDeploymentsToStabilize(test.deployments...) 438 439 skaffold.Delete().InDir(test.dir).InNs(ns.Name).RunOrFail(t) 440 }) 441 } 442 } 443 444 func TestRunIdempotent(t *testing.T) { 445 MarkIntegrationTest(t, CanRunWithoutGcp) 446 447 ns, _ := SetupNamespace(t) 448 449 // The first `skaffold run` creates resources (deployment.apps/leeroy-web, service/leeroy-app, deployment.apps/leeroy-app) 450 out := skaffold.Run("-l", "skaffold.dev/run-id=notunique").InDir("examples/microservices").InNs(ns.Name).RunOrFailOutput(t) 451 firstOut := string(out) 452 if strings.Count(firstOut, "created") == 0 { 453 t.Errorf("resources should have been created: %s", firstOut) 454 } 455 456 // Because we use the same custom `run-id`, the second `skaffold run` is idempotent: 457 // + It has nothing to rebuild 458 // + It leaves all resources unchanged 459 out = skaffold.Run("-l", "skaffold.dev/run-id=notunique").InDir("examples/microservices").InNs(ns.Name).RunOrFailOutput(t) 460 secondOut := string(out) 461 if strings.Count(secondOut, "created") != 0 { 462 t.Errorf("no resource should have been created: %s", secondOut) 463 } 464 if !strings.Contains(secondOut, "leeroy-web: Found") || !strings.Contains(secondOut, "leeroy-app: Found") { 465 t.Errorf("both artifacts should be in cache: %s", secondOut) 466 } 467 } 468 469 func TestRunUnstableChecked(t *testing.T) { 470 MarkIntegrationTest(t, CanRunWithoutGcp) 471 472 ns, _ := SetupNamespace(t) 473 474 output, err := skaffold.Run().InDir("testdata/unstable-deployment").InNs(ns.Name).RunWithCombinedOutput(t) 475 if err == nil { 476 t.Errorf("expected to see an error since the deployment is not stable: %s", output) 477 } else if !strings.Contains(string(output), "unstable-deployment failed") { 478 t.Errorf("failed without saying the reason: %s", output) 479 } 480 } 481 482 func TestRunUnstableNotChecked(t *testing.T) { 483 MarkIntegrationTest(t, CanRunWithoutGcp) 484 485 ns, _ := SetupNamespace(t) 486 487 skaffold.Run("--status-check=false").InDir("testdata/unstable-deployment").InNs(ns.Name).RunOrFail(t) 488 } 489 490 func TestRunTailPod(t *testing.T) { 491 MarkIntegrationTest(t, CanRunWithoutGcp) 492 493 ns, _ := SetupNamespace(t) 494 495 out := skaffold.Run("--tail", "-p", "pod").InDir("testdata/hello").InNs(ns.Name).RunLive(t) 496 497 WaitForLogs(t, out, 498 "Hello world! 0", 499 "Hello world! 1", 500 "Hello world! 2", 501 ) 502 } 503 504 func TestRunTailDeployment(t *testing.T) { 505 MarkIntegrationTest(t, CanRunWithoutGcp) 506 507 ns, _ := SetupNamespace(t) 508 509 out := skaffold.Run("--tail", "-p", "deployment").InDir("testdata/hello").InNs(ns.Name).RunLive(t) 510 511 WaitForLogs(t, out, 512 "Hello world! 0", 513 "Hello world! 1", 514 "Hello world! 2", 515 ) 516 } 517 518 func TestRunTest(t *testing.T) { 519 tests := []struct { 520 description string 521 testDir string 522 testFile string 523 args []string 524 skipTests bool 525 expectedText string 526 }{ 527 { 528 description: "Run test", 529 testDir: "testdata/custom-test", 530 testFile: "testdata/custom-test/runtest", 531 args: []string{"--profile", "custom"}, 532 skipTests: false, 533 expectedText: "foo\n", 534 }, 535 { 536 description: "Run test with skip test false", 537 testDir: "testdata/custom-test", 538 testFile: "testdata/custom-test/runtest", 539 args: []string{"--profile", "custom", "--skip-tests=false"}, 540 skipTests: false, 541 expectedText: "foo\n", 542 }, 543 { 544 description: "Run test with skip test true", 545 testDir: "testdata/custom-test", 546 testFile: "testdata/custom-test/runtest", 547 args: []string{"--profile", "custom", "--skip-tests=True"}, 548 skipTests: true, 549 }, 550 } 551 for _, test := range tests { 552 t.Run(test.description, func(t *testing.T) { 553 MarkIntegrationTest(t, CanRunWithoutGcp) 554 555 defer os.Remove(test.testFile) 556 557 // Run skaffold build first to fail quickly on a build failure 558 skaffold.Build().InDir(test.testDir).RunOrFail(t) 559 560 ns, client := SetupNamespace(t) 561 skaffold.Run(test.args...).InDir(test.testDir).InNs(ns.Name).RunBackground(t) 562 563 client.WaitForPodsReady("custom-test-example") 564 565 err := wait.PollImmediate(time.Millisecond*500, 1*time.Minute, func() (bool, error) { 566 _, e := os.Stat(test.testFile) 567 if test.skipTests { 568 if !os.IsNotExist(e) { 569 t.Fatalf("Tests are not skipped.") 570 } 571 return true, nil 572 } 573 out, e := os.ReadFile(test.testFile) 574 failNowIfError(t, e) 575 return string(out) == test.expectedText, nil 576 }) 577 failNowIfError(t, err) 578 }) 579 } 580 } 581 582 // TestRunNoOptFlags tests to ensure that flags that don't require a value to be passed work when no value is passed 583 func TestRunNoOptFlags(t *testing.T) { 584 test := struct { 585 description string 586 dir string 587 targetLog string 588 pods []string 589 args []string 590 }{ 591 description: "getting-started", 592 dir: "testdata/getting-started", 593 pods: []string{"getting-started"}, 594 targetLog: "Hello world!", 595 args: []string{ 596 "--port-forward", 597 "--status-check", 598 }, 599 } 600 601 t.Run(test.description, func(t *testing.T) { 602 MarkIntegrationTest(t, CanRunWithoutGcp) 603 ns, _ := SetupNamespace(t) 604 605 args := append(test.args, "--tail") 606 out := skaffold.Run(args...).InDir(test.dir).InNs(ns.Name).RunLive(t) 607 defer skaffold.Delete().InDir(test.dir).InNs(ns.Name).RunOrFail(t) 608 609 WaitForLogs(t, out, test.targetLog) 610 }) 611 } 612 613 func TestRunKubectlDefaultNamespace(t *testing.T) { 614 tests := []struct { 615 description string 616 namespaceToCreate string 617 projectDir string 618 podName string 619 envVariable string 620 }{ 621 { 622 description: "run with defaultNamespace when namespace exists in cluster", 623 namespaceToCreate: "namespace-test", 624 projectDir: "testdata/kubectl-with-default-namespace", 625 podName: "getting-started", 626 envVariable: "ENV1", 627 }, 628 } 629 630 for _, test := range tests { 631 testutil.Run(t, test.description, func(t *testutil.T) { 632 MarkIntegrationTest(t.T, CanRunWithoutGcp) 633 ns, client := SetupNamespace(t.T) 634 t.Setenv(test.envVariable, ns.Name) 635 skaffold.Run().InDir(test.projectDir).RunOrFail(t.T) 636 pod := client.GetPod(test.podName) 637 t.CheckNotNil(pod) 638 }) 639 } 640 }