github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/cmd/generic-autobumper/imagebumper/imagebumper_test.go (about) 1 /* 2 Copyright 2019 The Kubernetes 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 imagebumper 18 19 import ( 20 "fmt" 21 "regexp" 22 "testing" 23 ) 24 25 func TestDeconstructCommit(t *testing.T) { 26 cases := []struct { 27 name string 28 commit string 29 tag string 30 num int 31 expectedCommit string 32 }{ 33 { 34 name: "basically works", 35 }, 36 { 37 name: "just commit works", 38 commit: "deadbeef", 39 expectedCommit: "deadbeef", 40 }, 41 { 42 name: "commit drops leading g", 43 commit: "gdeadbeef", 44 expectedCommit: "deadbeef", 45 }, 46 { 47 name: "just tag works", 48 commit: "v0.0.30", 49 tag: "v0.0.30", 50 }, 51 { 52 name: "commits past tags work", 53 commit: "v0.0.30-14-gdeadbeef", 54 tag: "v0.0.30", 55 num: 14, 56 expectedCommit: "deadbeef", 57 }, 58 } 59 60 for _, tc := range cases { 61 t.Run(tc.name, func(t *testing.T) { 62 tag, num, commit := DeconstructCommit(tc.commit) 63 if tag != tc.tag { 64 t.Errorf("DeconstructCommit(%s) got tag %q, want %q", tc.commit, tag, tc.tag) 65 } 66 if num != tc.num { 67 t.Errorf("DeconstructCommit(%s) got tag %d, want %d", tc.commit, num, tc.num) 68 } 69 if commit != tc.expectedCommit { 70 t.Errorf("DeconstructCommit(%s) got commit %q, want %q", tc.commit, commit, tc.expectedCommit) 71 } 72 73 }) 74 } 75 } 76 77 func TestDeconstructTag(t *testing.T) { 78 cases := []struct { 79 tag string 80 date string 81 commit string 82 variant string 83 }{ 84 { 85 tag: "deadbeef", 86 // TODO(fejta): commit: "deadbeef", 87 }, 88 { 89 tag: "v0.0.30", 90 // TODO(fejta): commit: "v0.0.30", 91 }, 92 { 93 tag: "v20190404-65af07d", 94 date: "20190404", 95 commit: "65af07d", 96 }, 97 { 98 tag: "v20190330-811f79999-experimental", 99 date: "20190330", 100 commit: "811f79999", 101 variant: "-experimental", 102 }, 103 { 104 tag: "latest", 105 date: "atest", // TODO(fejta): empty 106 commit: "latest", 107 }, 108 { 109 tag: "latest-experimental", 110 date: "atest", // TODO(fejta): empty 111 commit: "latest", 112 variant: "-experimental", // TODO(fejta): no - 113 }, 114 { 115 tag: "v20210125-v0.0.41-8-gcb960c8", 116 date: "20210125", 117 commit: "gcb960c8", // TODO(fejta): "v0.0.41-8-gcb960c8", 118 }, 119 { 120 tag: "v20210125-v0.0.41-8-gcb960c8-fancy", 121 date: "20210125", 122 commit: "gcb960c8", // TODO(fejta): "v0.0.41-8-gcb960c8", 123 variant: "-fancy", // TODO(fejta): no - 124 }, 125 } 126 127 for _, tc := range cases { 128 t.Run(tc.tag, func(t *testing.T) { 129 date, commit, variant := DeconstructTag(tc.tag) 130 if date != tc.date { 131 t.Errorf("DeconstructTag(%q) got date %s, want %s", tc.tag, date, tc.date) 132 } 133 if commit != tc.commit { 134 t.Errorf("DeconstructTag(%q) got commit %s, want %s", tc.tag, commit, tc.commit) 135 } 136 if variant != tc.variant { 137 t.Errorf("DeconstructTag(%q) got variant %s, want %s", tc.tag, variant, tc.variant) 138 } 139 }) 140 } 141 } 142 143 func TestPickBestTag(t *testing.T) { 144 tests := []struct { 145 name string 146 tag string 147 manifest manifest 148 bestTag string 149 expectErr bool 150 }{ 151 { 152 name: "simple lookup", 153 tag: "v20190329-811f7954b", 154 manifest: manifest{ 155 "image1": { 156 TimeCreatedMs: "2000", 157 Tags: []string{"v20190404-65af07d"}, 158 }, 159 "image2": { 160 TimeCreatedMs: "1000", 161 Tags: []string{"v20190329-811f7954b"}, 162 }, 163 }, 164 bestTag: "v20190404-65af07d", 165 }, 166 { 167 name: "'latest' overrides date", 168 tag: "v20190329-811f7954b", 169 manifest: manifest{ 170 "image1": { 171 TimeCreatedMs: "2000", 172 Tags: []string{"v20190404-65af07d"}, 173 }, 174 "image2": { 175 TimeCreatedMs: "1000", 176 Tags: []string{"v20190330-811f79999", "latest"}, 177 }, 178 }, 179 bestTag: "v20190330-811f79999", 180 }, 181 { 182 name: "tags with suffixes only match other tags with the same suffix", 183 tag: "v20190329-811f7954b-experimental", 184 manifest: manifest{ 185 "image1": { 186 TimeCreatedMs: "2000", 187 Tags: []string{"v20190404-65af07d"}, 188 }, 189 "image2": { 190 TimeCreatedMs: "1000", 191 Tags: []string{"v20190330-811f79999-experimental"}, 192 }, 193 }, 194 bestTag: "v20190330-811f79999-experimental", 195 }, 196 { 197 name: "unsuffixed 'latest' has no effect on suffixed tags", 198 tag: "v20190329-811f7954b-experimental", 199 manifest: manifest{ 200 "image1": { 201 TimeCreatedMs: "2000", 202 Tags: []string{"v20190404-65af07d", "latest"}, 203 }, 204 "image2": { 205 TimeCreatedMs: "1000", 206 Tags: []string{"v20190330-811f79999-experimental"}, 207 }, 208 }, 209 bestTag: "v20190330-811f79999-experimental", 210 }, 211 { 212 name: "suffixed 'latest' has no effect on unsuffixed tags", 213 tag: "v20190329-811f7954b", 214 manifest: manifest{ 215 "image1": { 216 TimeCreatedMs: "2000", 217 Tags: []string{"v20190404-65af07d"}, 218 }, 219 "image2": { 220 TimeCreatedMs: "1000", 221 Tags: []string{"v20190330-811f79999-experimental", "latest-experimental"}, 222 }, 223 }, 224 bestTag: "v20190404-65af07d", 225 }, 226 { 227 name: "'latest' with the correct suffix overrides date", 228 tag: "v20190329-811f7954b-experimental", 229 manifest: manifest{ 230 "image1": { 231 TimeCreatedMs: "2000", 232 Tags: []string{"v20190404-65af07d-experimental"}, 233 }, 234 "image2": { 235 TimeCreatedMs: "1000", 236 Tags: []string{"v20190330-811f79999-experimental", "latest-experimental"}, 237 }, 238 }, 239 bestTag: "v20190330-811f79999-experimental", 240 }, 241 { 242 name: "it is an error when no tags are found", 243 tag: "v20190329-811f7954b-master", 244 manifest: manifest{ 245 "image1": { 246 TimeCreatedMs: "2000", 247 Tags: []string{"v20190404-65af07d-experimental"}, 248 }, 249 "image2": { 250 TimeCreatedMs: "1000", 251 Tags: []string{"v20190330-811f79999-experimental", "latest-experimental"}, 252 }, 253 }, 254 expectErr: true, 255 }, 256 } 257 258 for _, test := range tests { 259 t.Run(test.name, func(t *testing.T) { 260 tagParts := tagRegexp.FindStringSubmatch(test.tag) 261 bestTag, err := pickBestTag(tagParts, test.manifest) 262 if err != nil { 263 if !test.expectErr { 264 t.Fatalf("Unexpected error: %v", err) 265 } 266 return 267 } 268 if test.expectErr { 269 t.Fatalf("Expected an error, but got result %q", bestTag) 270 } 271 if bestTag != test.bestTag { 272 t.Fatalf("Expected tag %q, but got %q instead", test.bestTag, bestTag) 273 } 274 }) 275 } 276 } 277 278 func TestUpdateAllTags(t *testing.T) { 279 tests := []struct { 280 name string 281 content string 282 expectedResult string 283 imageFilter *regexp.Regexp 284 newTags map[string]string 285 }{ 286 { 287 name: "file with no images does nothing", 288 content: "this is just a normal file", 289 expectedResult: "this is just a normal file", 290 }, 291 { 292 name: "file that has only an image replaces the image", 293 content: "gcr.io/k8s-testimages/some-image:v20190404-12345678", 294 expectedResult: "gcr.io/k8s-testimages/some-image:v20190405-123456789", 295 newTags: map[string]string{ 296 "gcr.io/k8s-testimages/some-image:v20190404-12345678": "v20190405-123456789", 297 }, 298 }, 299 { 300 name: "file that has content before and after an image still has it later", 301 content: `{"image": "gcr.io/k8s-testimages/some-image:v20190404-12345678"}`, 302 expectedResult: `{"image": "gcr.io/k8s-testimages/some-image:v20190405-123456789"}`, 303 newTags: map[string]string{ 304 "gcr.io/k8s-testimages/some-image:v20190404-12345678": "v20190405-123456789", 305 }, 306 }, 307 { 308 name: "file that has multiple different images replaces both of them", 309 content: `{"images": ["gcr.io/k8s-testimages/some-image:v20190404-12345678-master", "gcr.io/k8s-testimages/some-image:v20190404-12345678-experimental"]}`, 310 expectedResult: `{"images": ["gcr.io/k8s-testimages/some-image:v20190405-123456789-master", "gcr.io/k8s-testimages/some-image:v20190405-123456789-experimental"]}`, 311 newTags: map[string]string{ 312 "gcr.io/k8s-testimages/some-image:v20190404-12345678-master": "v20190405-123456789-master", 313 "gcr.io/k8s-testimages/some-image:v20190404-12345678-experimental": "v20190405-123456789-experimental", 314 }, 315 }, 316 { 317 name: "file with an error image is still otherwise updated", 318 content: `{"images": ["gcr.io/k8s-testimages/some-image:0.2", "gcr.io/k8s-testimages/some-image:v20190404-12345678"]}`, 319 expectedResult: `{"images": ["gcr.io/k8s-testimages/some-image:0.2", "gcr.io/k8s-testimages/some-image:v20190405-123456789"]}`, 320 newTags: map[string]string{ 321 "gcr.io/k8s-testimages/some-image:v20190404-12345678": "v20190405-123456789", 322 }, 323 }, 324 { 325 name: "gcr subdomains are supported", 326 content: `{"images": ["eu.gcr.io/k8s-testimages/some-image:v20190404-12345678"]}`, 327 expectedResult: `{"images": ["eu.gcr.io/k8s-testimages/some-image:v20190405-123456789"]}`, 328 newTags: map[string]string{ 329 "eu.gcr.io/k8s-testimages/some-image:v20190404-12345678": "v20190405-123456789", 330 }, 331 }, 332 { 333 name: "AR multi-regional subdomains are supported", 334 content: `{"images": ["us-docker.pkg.dev/k8s-testimages/some-image:v20190404-12345678"]}`, 335 expectedResult: `{"images": ["us-docker.pkg.dev/k8s-testimages/some-image:v20190405-123456789"]}`, 336 newTags: map[string]string{ 337 "us-docker.pkg.dev/k8s-testimages/some-image:v20190404-12345678": "v20190405-123456789", 338 }, 339 }, 340 { 341 name: "AR regional subdomains are supported", 342 content: `{"images": ["us-central1-docker.pkg.dev/k8s-testimages/some-image:v20190404-12345678"]}`, 343 expectedResult: `{"images": ["us-central1-docker.pkg.dev/k8s-testimages/some-image:v20190405-123456789"]}`, 344 newTags: map[string]string{ 345 "us-central1-docker.pkg.dev/k8s-testimages/some-image:v20190404-12345678": "v20190405-123456789", 346 }, 347 }, 348 { 349 name: "images not matching the filter regex are not updated", 350 content: `{"images": ["gcr.io/k8s-prow/pkg-thing:v20190404-12345678", "gcr.io/k8s-testimages/some-image:v20190404-12345678"]}`, 351 expectedResult: `{"images": ["gcr.io/k8s-prow/pkg-thing:v20190404-12345678", "gcr.io/k8s-testimages/some-image:v20190405-123456789"]}`, 352 newTags: map[string]string{ 353 "gcr.io/k8s-prow/pkg-thing:v20190404-12345678": "v20190405-123456789", 354 "gcr.io/k8s-testimages/some-image:v20190404-12345678": "v20190405-123456789", 355 }, 356 imageFilter: regexp.MustCompile("gcr.io/k8s-testimages"), 357 }, 358 } 359 360 for _, test := range tests { 361 t.Run(test.name, func(t *testing.T) { 362 tagPicker := func(imageHost string, imageName string, imageTag string) (string, error) { 363 result, ok := test.newTags[imageHost+"/"+imageName+":"+imageTag] 364 if !ok { 365 return "", fmt.Errorf("unknown image %s/%s:%s", imageHost, imageName, imageTag) 366 } 367 return result, nil 368 } 369 370 newContent := updateAllTags(tagPicker, []byte(test.content), test.imageFilter) 371 if test.expectedResult != string(newContent) { 372 t.Fatalf("Expected content:\n%s\n\nActual content:\n%s\n\n", test.expectedResult, string(newContent)) 373 } 374 }) 375 } 376 }