github.com/GoogleContainerTools/skaffold/v2@v2.13.2/integration/build_dependencies_test.go (about) 1 /* 2 Copyright 2020 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 "strings" 22 "testing" 23 24 "github.com/google/uuid" 25 26 "github.com/GoogleContainerTools/skaffold/v2/integration/skaffold" 27 ) 28 29 func TestBuildDependenciesOrder(t *testing.T) { 30 tests := []struct { 31 description string 32 args []string 33 cacheEnabled bool 34 failure string 35 }{ 36 { 37 description: "default concurrency=1", 38 }, 39 { 40 description: "concurrency=0", 41 args: []string{"-p", "concurrency-0"}, 42 }, 43 { 44 description: "concurrency=3", 45 args: []string{"-p", "concurrency-3"}, 46 }, 47 { 48 description: "invalid dependency", 49 args: []string{"-p", "invalid-dependency"}, 50 failure: `unknown build dependency "image5" for artifact "image1"`, 51 }, 52 { 53 description: "circular dependency", 54 args: []string{"-p", "circular-dependency"}, 55 failure: `cycle detected in build dependencies involving "image1"`, 56 }, 57 { 58 description: "build failure with concurrency=1", 59 args: []string{"-p", "failed-dependency"}, 60 failure: `docker build failure: The command '/bin/sh -c [ "${FAIL}" == "0" ] || false' returned a non-zero code: 1`, 61 }, 62 { 63 description: "build failure with concurrency=0", 64 args: []string{"-p", "failed-dependency", "-p", "concurrency-0"}, 65 failure: `docker build failure: The command '/bin/sh -c [ "${FAIL}" == "0" ] || false' returned a non-zero code: 1`, 66 }, 67 } 68 69 for _, test := range tests { 70 t.Run(test.description, func(t *testing.T) { 71 MarkIntegrationTest(t, CanRunWithoutGcp) 72 if test.cacheEnabled { 73 test.args = append(test.args, "--cache-artifacts=true") 74 } else { 75 test.args = append(test.args, "--cache-artifacts=false") 76 } 77 78 if test.failure == "" { 79 // Run without artifact caching 80 skaffold.Build(test.args...).InDir("testdata/build-dependencies").RunOrFail(t) 81 checkImagesExist(t) 82 } else { 83 if out, err := skaffold.Build(test.args...).InDir("testdata/build-dependencies").RunWithCombinedOutput(t); err == nil { 84 t.Fatal("expected build to fail") 85 } else if !strings.Contains(string(out), test.failure) { 86 t.Log("build output: ", string(out)) 87 t.Fatalf("build failed but for wrong reason") 88 } 89 } 90 }) 91 } 92 } 93 94 func TestBuildDependenciesCache(t *testing.T) { 95 // These tests build 4 images and then make a file change to the images in `change`. 96 // The test then triggers another build and verifies that the images in `rebuilt` were built 97 // (e.g., the changed images and their dependents), and that the other images were found in the artifact cache. 98 // It runs the profile `concurrency-0` which builds with maximum concurrency. 99 tests := []struct { 100 description string 101 change []int 102 rebuilt []int 103 }{ 104 { 105 description: "no change", 106 }, 107 { 108 description: "change 1", 109 change: []int{1}, 110 rebuilt: []int{1}, 111 }, 112 { 113 description: "change 2", 114 change: []int{2}, 115 rebuilt: []int{1, 2}, 116 }, 117 { 118 description: "change 3", 119 change: []int{3}, 120 rebuilt: []int{1, 2, 3}, 121 }, 122 { 123 description: "change 4", 124 change: []int{4}, 125 rebuilt: []int{4}, 126 }, 127 { 128 description: "change all", 129 change: []int{1, 2, 3, 4}, 130 rebuilt: []int{1, 2, 3, 4}, 131 }, 132 } 133 134 skaffold.Build("--cache-artifacts=true", "-p", "concurrency-0").InDir("testdata/build-dependencies").RunOrFail(t) 135 checkImagesExist(t) 136 137 for _, test := range tests { 138 t.Run(test.description, func(t *testing.T) { 139 MarkIntegrationTest(t, CanRunWithoutGcp) 140 // modify file `foo` to invalidate cache for target artifacts 141 for _, i := range test.change { 142 Run(t, fmt.Sprintf("testdata/build-dependencies/app%d", i), "sh", "-c", fmt.Sprintf("echo %s > foo", uuid.New().String())) 143 } 144 out, err := skaffold.Build("--cache-artifacts=true", "-p", "concurrency-0").InDir("testdata/build-dependencies").RunWithCombinedOutput(t) 145 if err != nil { 146 t.Fatal("expected build to succeed") 147 } 148 log := string(out) 149 150 for i := 1; i <= 4; i++ { 151 if !contains(test.rebuilt, i) && !strings.Contains(log, fmt.Sprintf("image%d: Found Locally", i)) { 152 t.Log("build output: ", string(out)) 153 t.Fatalf("expected image%d to be cached", i) 154 } 155 156 if contains(test.rebuilt, i) && !strings.Contains(log, fmt.Sprintf("image%d: Not found. Building", i)) { 157 t.Log("build output: ", string(out)) 158 t.Fatalf("expected image%d to be rebuilt", i) 159 } 160 } 161 checkImagesExist(t) 162 }) 163 } 164 165 // revert file changes 166 for i := 1; i <= 4; i++ { 167 Run(t, fmt.Sprintf("testdata/build-dependencies/app%d", i), "sh", "-c", "> foo") 168 } 169 } 170 171 func checkImagesExist(t *testing.T) { 172 checkImageExists(t, "us-central1-docker.pkg.dev/k8s-skaffold/testing/image1:latest") 173 checkImageExists(t, "us-central1-docker.pkg.dev/k8s-skaffold/testing/image2:latest") 174 checkImageExists(t, "us-central1-docker.pkg.dev/k8s-skaffold/testing/image3:latest") 175 checkImageExists(t, "us-central1-docker.pkg.dev/k8s-skaffold/testing/image4:latest") 176 } 177 178 func contains(sl []int, t int) bool { 179 for _, i := range sl { 180 if i == t { 181 return true 182 } 183 } 184 return false 185 }