github.com/grahambrereton-form3/tilt@v0.10.18/internal/k8s/image_test.go (about) 1 package k8s 2 3 import ( 4 "fmt" 5 "strings" 6 "testing" 7 8 "github.com/docker/distribution/reference" 9 "github.com/opencontainers/go-digest" 10 "github.com/stretchr/testify/assert" 11 appsv1 "k8s.io/api/apps/v1" 12 v1 "k8s.io/api/core/v1" 13 14 "github.com/windmilleng/tilt/internal/container" 15 "github.com/windmilleng/tilt/internal/k8s/testyaml" 16 ) 17 18 func TestInjectDigestSanchoYAML(t *testing.T) { 19 entities, err := ParseYAMLFromString(testyaml.SanchoYAML) 20 if err != nil { 21 t.Fatal(err) 22 } 23 24 if len(entities) != 1 { 25 t.Fatalf("Unexpected entities: %+v", entities) 26 } 27 28 entity := entities[0] 29 name := "gcr.io/some-project-162817/sancho" 30 digest := "sha256:2baf1f40105d9501fe319a8ec463fdf4325a2a5df445adf3f572f626253678c9" 31 newEntity, replaced, err := InjectImageDigestWithStrings(entity, name, digest, v1.PullIfNotPresent) 32 if err != nil { 33 t.Fatal(err) 34 } 35 36 if !replaced { 37 t.Errorf("Expected replaced: true. Actual: %v", replaced) 38 } 39 40 result, err := SerializeSpecYAML([]K8sEntity{newEntity}) 41 if err != nil { 42 t.Fatal(err) 43 } 44 45 if !strings.Contains(result, fmt.Sprintf("image: %s@%s", name, digest)) { 46 t.Errorf("image name did not appear in serialized yaml: %s", result) 47 } 48 } 49 50 func TestInjectDigestDoesNotMutateOriginal(t *testing.T) { 51 entities, err := ParseYAMLFromString(testyaml.SanchoYAML) 52 if err != nil { 53 t.Fatal(err) 54 } 55 56 if len(entities) != 1 { 57 t.Fatalf("Unexpected entities: %+v", entities) 58 } 59 60 entity := entities[0] 61 name := "gcr.io/some-project-162817/sancho" 62 digest := "sha256:2baf1f40105d9501fe319a8ec463fdf4325a2a5df445adf3f572f626253678c9" 63 _, replaced, err := InjectImageDigestWithStrings(entity, name, digest, v1.PullIfNotPresent) 64 if err != nil { 65 t.Fatal(err) 66 } 67 68 if !replaced { 69 t.Errorf("Expected replaced: true. Actual: %v", replaced) 70 } 71 72 result, err := SerializeSpecYAML([]K8sEntity{entity}) 73 if err != nil { 74 t.Fatal(err) 75 } 76 77 if strings.Contains(result, fmt.Sprintf("image: %s@%s", name, digest)) { 78 t.Errorf("oops! accidentally mutated original entity: %s", result) 79 } 80 } 81 82 func TestInjectImagePullPolicy(t *testing.T) { 83 entities, err := ParseYAMLFromString(testyaml.BlorgBackendYAML) 84 if err != nil { 85 t.Fatal(err) 86 } 87 88 entity := entities[1] 89 newEntity, err := InjectImagePullPolicy(entity, v1.PullNever) 90 if err != nil { 91 t.Fatal(err) 92 } 93 94 result, err := SerializeSpecYAML([]K8sEntity{newEntity}) 95 if err != nil { 96 t.Fatal(err) 97 } 98 99 if !strings.Contains(result, "imagePullPolicy: Never") { 100 t.Errorf("image does not have correct pull policy: %s", result) 101 } 102 103 serializedOrigEntity, err := SerializeSpecYAML([]K8sEntity{entity}) 104 if err != nil { 105 t.Fatal(err) 106 } 107 108 if strings.Contains(serializedOrigEntity, "imagePullPolicy: Never") { 109 t.Errorf("oops! accidentally mutated original entity: %+v", entity) 110 } 111 } 112 113 func TestInjectImagePullPolicyDoesNotMutateOriginal(t *testing.T) { 114 entities, err := ParseYAMLFromString(testyaml.BlorgBackendYAML) 115 if err != nil { 116 t.Fatal(err) 117 } 118 119 entity := entities[1] 120 _, err = InjectImagePullPolicy(entity, v1.PullNever) 121 if err != nil { 122 t.Fatal(err) 123 } 124 125 result, err := SerializeSpecYAML([]K8sEntity{entity}) 126 if err != nil { 127 t.Fatal(err) 128 } 129 130 if strings.Contains(result, "imagePullPolicy: Never") { 131 t.Errorf("oops! accidentally mutated original entity: %+v", entity) 132 } 133 } 134 135 func TestErrorInjectDigestBlorgBackendYAML(t *testing.T) { 136 entities, err := ParseYAMLFromString(testyaml.BlorgBackendYAML) 137 if err != nil { 138 t.Fatal(err) 139 } 140 141 if len(entities) != 2 { 142 t.Fatalf("Unexpected entities: %+v", entities) 143 } 144 145 entity := entities[1] 146 name := "gcr.io/blorg-dev/blorg-backend" 147 digest := "sha256:2baf1f40105d9501fe319a8ec463fdf4325a2a5df445adf3f572f626253678c9" 148 _, _, err = InjectImageDigestWithStrings(entity, name, digest, v1.PullNever) 149 if err == nil || !strings.Contains(err.Error(), "INTERNAL TILT ERROR") { 150 t.Errorf("Expected internal tilt error, actual: %v", err) 151 } 152 } 153 154 func TestInjectDigestBlorgBackendYAML(t *testing.T) { 155 entities, err := ParseYAMLFromString(testyaml.BlorgBackendYAML) 156 if err != nil { 157 t.Fatal(err) 158 } 159 160 if len(entities) != 2 { 161 t.Fatalf("Unexpected entities: %+v", entities) 162 } 163 164 entity := entities[1] 165 name := "gcr.io/blorg-dev/blorg-backend" 166 namedTagged, _ := reference.ParseNamed(fmt.Sprintf("%s:wm-tilt", name)) 167 newEntity, replaced, err := InjectImageDigest(entity, container.NameSelector(namedTagged), namedTagged, false, v1.PullNever) 168 if err != nil { 169 t.Fatal(err) 170 } 171 172 if !replaced { 173 t.Errorf("Expected replaced: true. Actual: %v", replaced) 174 } 175 176 result, err := SerializeSpecYAML([]K8sEntity{newEntity}) 177 if err != nil { 178 t.Fatal(err) 179 } 180 181 if !strings.Contains(result, fmt.Sprintf("image: %s", namedTagged)) { 182 t.Errorf("image name did not appear in serialized yaml: %s", result) 183 } 184 185 if !strings.Contains(result, "imagePullPolicy: Never") { 186 t.Errorf("image does not have correct pull policy: %s", result) 187 } 188 } 189 190 // Returns: the new entity, whether anything was replaced, and an error. 191 func InjectImageDigestWithStrings(entity K8sEntity, original string, newDigest string, policy v1.PullPolicy) (K8sEntity, bool, error) { 192 originalRef, err := reference.ParseNamed(original) 193 if err != nil { 194 return K8sEntity{}, false, err 195 } 196 197 d, err := digest.Parse(newDigest) 198 if err != nil { 199 return K8sEntity{}, false, err 200 } 201 202 canonicalRef, err := reference.WithDigest(originalRef, d) 203 if err != nil { 204 return K8sEntity{}, false, err 205 } 206 207 return InjectImageDigest(entity, container.NameSelector(originalRef), canonicalRef, false, policy) 208 } 209 210 func TestInjectSyncletImage(t *testing.T) { 211 entities, err := ParseYAMLFromString(testyaml.SyncletYAML) 212 if err != nil { 213 t.Fatal(err) 214 } 215 216 assert.Equal(t, 1, len(entities)) 217 entity := entities[0] 218 name := "gcr.io/windmill-public-containers/synclet" 219 namedTagged, _ := container.ParseNamedTagged(fmt.Sprintf("%s:tilt-deadbeef", name)) 220 newEntity, replaced, err := InjectImageDigest(entity, container.NameSelector(namedTagged), namedTagged, false, v1.PullNever) 221 if err != nil { 222 t.Fatal(err) 223 } else if !replaced { 224 t.Errorf("Expected replacement in:\n%s", testyaml.SyncletYAML) 225 } 226 227 result, err := SerializeSpecYAML([]K8sEntity{newEntity}) 228 if err != nil { 229 t.Fatal(err) 230 } 231 232 if !strings.Contains(result, namedTagged.String()) { 233 t.Errorf("could not find image in yaml (%s):\n%s", namedTagged, result) 234 } 235 } 236 237 func TestEntityHasImage(t *testing.T) { 238 entities, err := ParseYAMLFromString(testyaml.BlorgBackendYAML) 239 if err != nil { 240 t.Fatal(err) 241 } 242 243 img := container.MustParseSelector("gcr.io/blorg-dev/blorg-backend:devel-nick") 244 wrongImg := container.MustParseSelector("gcr.io/blorg-dev/wrong-app-whoops:devel-nick") 245 246 match, err := entities[0].HasImage(img, nil, false) 247 if err != nil { 248 t.Fatal(err) 249 } 250 assert.False(t, match, "service yaml should not match (does not contain image)") 251 252 match, err = entities[1].HasImage(img, nil, false) 253 if err != nil { 254 t.Fatal(err) 255 } 256 assert.True(t, match, "deployment yaml should match image %s", img.String()) 257 258 match, err = entities[1].HasImage(wrongImg, nil, false) 259 if err != nil { 260 t.Fatal(err) 261 } 262 assert.False(t, match, "deployment yaml should not match image %s", img.String()) 263 264 entities, err = ParseYAMLFromString(testyaml.CRDYAML) 265 if err != nil { 266 t.Fatal(err) 267 } 268 269 img = container.MustParseTaggedSelector("docker.io/bitnami/minideb:latest") 270 e := entities[0] 271 jp, err := NewJSONPath("{.spec.validation.openAPIV3Schema.properties.spec.properties.image}") 272 if err != nil { 273 t.Fatal(err) 274 } 275 imageJSONPaths := []JSONPath{jp} 276 match, err = e.HasImage(img, imageJSONPaths, false) 277 if err != nil { 278 t.Fatal(err) 279 } 280 assert.True(t, match, "CRD yaml should match image %s", img.String()) 281 282 entities, err = ParseYAMLFromString(testyaml.SanchoImageInEnvYAML) 283 if err != nil { 284 t.Fatal(err) 285 } 286 img = container.MustParseSelector("gcr.io/some-project-162817/sancho") 287 e = entities[0] 288 match, err = e.HasImage(img, nil, false) 289 if err != nil { 290 t.Fatal(err) 291 } 292 assert.True(t, match, "deployment yaml should match image %s", img.String()) 293 img2 := container.MustParseSelector("gcr.io/some-project-162817/sancho2") 294 e = entities[0] 295 match, err = e.HasImage(img2, nil, true) 296 if err != nil { 297 t.Fatal(err) 298 } 299 assert.True(t, match, "CRD yaml should match image %s", img2.String()) 300 301 } 302 303 func testInjectDigestCRD(t *testing.T, yaml string, expectedDigestPrefix string) { 304 entities, err := ParseYAMLFromString(yaml) 305 if err != nil { 306 t.Fatal(err) 307 } 308 309 if len(entities) != 1 { 310 t.Fatalf("Unexpected entities: %+v", entities) 311 } 312 313 entity := entities[0] 314 name := "gcr.io/foo" 315 digest := "sha256:2baf1f40105d9501fe319a8ec463fdf4325a2a5df445adf3f572f626253678c9" 316 newEntity, replaced, err := InjectImageDigestWithStrings(entity, name, digest, v1.PullIfNotPresent) 317 if err != nil { 318 t.Fatal(err) 319 } 320 321 if !replaced { 322 t.Errorf("Expected replaced: true. Actual: %v", replaced) 323 } 324 325 result, err := SerializeSpecYAML([]K8sEntity{newEntity}) 326 if err != nil { 327 t.Fatal(err) 328 } 329 330 if !strings.Contains(result, fmt.Sprintf("%s%s@%s", expectedDigestPrefix, name, digest)) { 331 t.Errorf("image name did not appear in serialized yaml: %s", result) 332 } 333 } 334 335 func TestInjectDigestCRDMapValue(t *testing.T) { 336 testInjectDigestCRD(t, ` 337 apiversion: foo/v1 338 kind: Foo 339 spec: 340 image: gcr.io/foo:stable 341 `, "image: ") 342 } 343 344 func TestInjectDigestCRDListElement(t *testing.T) { 345 testInjectDigestCRD(t, ` 346 apiversion: foo/v1 347 kind: Foo 348 spec: 349 images: 350 - gcr.io/foo:stable 351 `, "- ") 352 } 353 354 func TestInjectDigestCRDListOfMaps(t *testing.T) { 355 testInjectDigestCRD(t, ` 356 apiversion: foo/v1 357 kind: Foo 358 spec: 359 args: 360 image: gcr.io/foo:stable 361 `, "image: ") 362 } 363 364 func TestMatchInEnvVarsFalse(t *testing.T) { 365 entity := parseOneEntity(t, testyaml.SanchoImageInEnvYAML) 366 name := "gcr.io/some-project-162817/sancho" 367 digest := "2baf1f40105d9501fe319a8ec463fdf4325a2a5df445adf3f572f626253678c9" 368 namedTagged, err := reference.ParseNamed(fmt.Sprintf("%s:%s", name, digest)) 369 if err != nil { 370 t.Fatal(err) 371 } 372 newEntity, replaced, err := InjectImageDigest(entity, container.NameSelector(namedTagged), namedTagged, false, v1.PullNever) 373 if err != nil { 374 t.Fatal(err) 375 } 376 assert.True(t, replaced) 377 d := newEntity.Obj.(*appsv1.Deployment) 378 if !assert.Equal(t, 1, len(d.Spec.Template.Spec.Containers)) { 379 return 380 } 381 c := d.Spec.Template.Spec.Containers[0] 382 // make sure we didn't inject to the env var 383 assert.Equal(t, namedTagged.String(), c.Image) 384 assert.Contains(t, c.Env, v1.EnvVar{Name: "bar", Value: name}) 385 } 386 387 func TestMatchInEnvVarsTrue(t *testing.T) { 388 entity := parseOneEntity(t, testyaml.SanchoImageInEnvYAML) 389 name := "gcr.io/some-project-162817/sancho" 390 digest := "2baf1f40105d9501fe319a8ec463fdf4325a2a5df445adf3f572f626253678c9" 391 namedTagged, err := reference.ParseNamed(fmt.Sprintf("%s:%s", name, digest)) 392 if err != nil { 393 t.Fatal(err) 394 } 395 newEntity, replaced, err := InjectImageDigest(entity, container.NameSelector(namedTagged), namedTagged, true, v1.PullNever) 396 if err != nil { 397 t.Fatal(err) 398 } 399 assert.True(t, replaced) 400 d := newEntity.Obj.(*appsv1.Deployment) 401 if !assert.Equal(t, 1, len(d.Spec.Template.Spec.Containers)) { 402 return 403 } 404 c := d.Spec.Template.Spec.Containers[0] 405 // make sure we didn't inject to the env var 406 assert.Equal(t, namedTagged.String(), c.Image) 407 assert.Contains(t, c.Env, v1.EnvVar{Name: "bar", Value: namedTagged.String()}) 408 }