github.com/grahambrereton-form3/tilt@v0.10.18/internal/engine/image_build_and_deployer_test.go (about) 1 package engine 2 3 import ( 4 "archive/tar" 5 "context" 6 "fmt" 7 "io" 8 "strings" 9 "testing" 10 "time" 11 12 "github.com/docker/distribution/reference" 13 "github.com/docker/docker/api/types" 14 "github.com/opencontainers/go-digest" 15 "github.com/stretchr/testify/assert" 16 "github.com/windmilleng/wmclient/pkg/dirs" 17 v1 "k8s.io/api/apps/v1" 18 corev1 "k8s.io/api/core/v1" 19 20 "github.com/windmilleng/tilt/internal/container" 21 "github.com/windmilleng/tilt/internal/docker" 22 "github.com/windmilleng/tilt/internal/k8s" 23 "github.com/windmilleng/tilt/internal/k8s/testyaml" 24 "github.com/windmilleng/tilt/internal/store" 25 "github.com/windmilleng/tilt/internal/testutils" 26 "github.com/windmilleng/tilt/internal/testutils/manifestbuilder" 27 "github.com/windmilleng/tilt/internal/testutils/tempdir" 28 "github.com/windmilleng/tilt/pkg/model" 29 ) 30 31 func TestDockerBuildWithCache(t *testing.T) { 32 f := newIBDFixture(t, k8s.EnvGKE) 33 defer f.TearDown() 34 35 manifest := NewSanchoDockerBuildManifestWithCache(f, []string{"/root/.cache"}) 36 cache := "gcr.io/some-project-162817/sancho:tilt-cache-3de427a264f80719a58a9abd456487b3" 37 f.docker.Images[cache] = types.ImageInspect{} 38 39 _, err := f.ibd.BuildAndDeploy(f.ctx, f.st, buildTargets(manifest), store.BuildStateSet{}) 40 if err != nil { 41 t.Fatal(err) 42 } 43 44 expected := expectedFile{ 45 Path: "Dockerfile", 46 Contents: `FROM gcr.io/some-project-162817/sancho:tilt-cache-3de427a264f80719a58a9abd456487b3 47 LABEL "tilt.cache"="0" 48 ADD . . 49 RUN go install github.com/windmilleng/sancho 50 ENTRYPOINT /go/bin/sancho 51 `, 52 } 53 testutils.AssertFileInTar(t, tar.NewReader(f.docker.BuildOptions.Context), expected) 54 } 55 56 func TestBaseDockerfileWithCache(t *testing.T) { 57 f := newIBDFixture(t, k8s.EnvGKE) 58 defer f.TearDown() 59 60 manifest := NewSanchoFastBuildManifestWithCache(f, []string{"/root/.cache"}) 61 cache := "gcr.io/some-project-162817/sancho:tilt-cache-3de427a264f80719a58a9abd456487b3" 62 f.docker.Images[cache] = types.ImageInspect{} 63 64 _, err := f.ibd.BuildAndDeploy(f.ctx, f.st, buildTargets(manifest), store.BuildStateSet{}) 65 if err != nil { 66 t.Fatal(err) 67 } 68 69 expected := expectedFile{ 70 Path: "Dockerfile", 71 Contents: `FROM gcr.io/some-project-162817/sancho:tilt-cache-3de427a264f80719a58a9abd456487b3 72 LABEL "tilt.cache"="0" 73 ADD . / 74 RUN ["go", "install", "github.com/windmilleng/sancho"] 75 ENTRYPOINT ["/go/bin/sancho"] 76 LABEL "tilt.buildMode"="scratch"`, 77 } 78 testutils.AssertFileInTar(t, tar.NewReader(f.docker.BuildOptions.Context), expected) 79 } 80 81 func TestDeployTwinImages(t *testing.T) { 82 f := newIBDFixture(t, k8s.EnvGKE) 83 defer f.TearDown() 84 85 sancho := NewSanchoFastBuildManifest(f) 86 manifest := sancho.WithDeployTarget(sancho.K8sTarget().AppendYAML(SanchoTwinYAML)) 87 result, err := f.ibd.BuildAndDeploy(f.ctx, f.st, buildTargets(manifest), store.BuildStateSet{}) 88 if err != nil { 89 t.Fatal(err) 90 } 91 92 id := manifest.ImageTargetAt(0).ID() 93 expectedImage := "gcr.io/some-project-162817/sancho:tilt-11cd0b38bc3ceb95" 94 image := store.ImageFromBuildResult(result[id]) 95 assert.Equal(t, expectedImage, image.String()) 96 assert.Equalf(t, 2, strings.Count(f.k8s.Yaml, expectedImage), 97 "Expected image to update twice in YAML: %s", f.k8s.Yaml) 98 } 99 100 func TestDeployPodWithMultipleImages(t *testing.T) { 101 f := newIBDFixture(t, k8s.EnvGKE) 102 defer f.TearDown() 103 104 iTarget1 := NewSanchoDockerBuildImageTarget(f) 105 iTarget2 := NewSanchoSidecarDockerBuildImageTarget(f) 106 kTarget := model.K8sTarget{Name: "sancho", YAML: testyaml.SanchoSidecarYAML}. 107 WithDependencyIDs([]model.TargetID{iTarget1.ID(), iTarget2.ID()}) 108 targets := []model.TargetSpec{iTarget1, iTarget2, kTarget} 109 110 result, err := f.ibd.BuildAndDeploy(f.ctx, f.st, targets, store.BuildStateSet{}) 111 if err != nil { 112 t.Fatal(err) 113 } 114 115 assert.Equal(t, 2, f.docker.BuildCount) 116 117 expectedSanchoRef := "gcr.io/some-project-162817/sancho:tilt-11cd0b38bc3ceb95" 118 image := store.ImageFromBuildResult(result[iTarget1.ID()]) 119 assert.Equal(t, expectedSanchoRef, image.String()) 120 assert.Equalf(t, 1, strings.Count(f.k8s.Yaml, expectedSanchoRef), 121 "Expected image to appear once in YAML: %s", f.k8s.Yaml) 122 123 expectedSidecarRef := "gcr.io/some-project-162817/sancho-sidecar:tilt-11cd0b38bc3ceb95" 124 image = store.ImageFromBuildResult(result[iTarget2.ID()]) 125 assert.Equal(t, expectedSidecarRef, image.String()) 126 assert.Equalf(t, 1, strings.Count(f.k8s.Yaml, expectedSidecarRef), 127 "Expected image to appear once in YAML: %s", f.k8s.Yaml) 128 } 129 130 func TestDeployPodWithMultipleLiveUpdateImages(t *testing.T) { 131 f := newIBDFixture(t, k8s.EnvGKE) 132 defer f.TearDown() 133 f.ibd.injectSynclet = true 134 135 iTarget1 := NewSanchoLiveUpdateImageTarget(f) 136 iTarget2 := NewSanchoSidecarLiveUpdateImageTarget(f) 137 138 kTarget := model.K8sTarget{Name: "sancho", YAML: testyaml.SanchoSidecarYAML}. 139 WithDependencyIDs([]model.TargetID{iTarget1.ID(), iTarget2.ID()}) 140 targets := []model.TargetSpec{iTarget1, iTarget2, kTarget} 141 142 result, err := f.ibd.BuildAndDeploy(f.ctx, f.st, targets, store.BuildStateSet{}) 143 if err != nil { 144 t.Fatal(err) 145 } 146 147 assert.Equal(t, 2, f.docker.BuildCount) 148 149 expectedSanchoRef := "gcr.io/some-project-162817/sancho:tilt-11cd0b38bc3ceb95" 150 image := store.ImageFromBuildResult(result[iTarget1.ID()]) 151 assert.Equal(t, expectedSanchoRef, image.String()) 152 assert.Equalf(t, 1, strings.Count(f.k8s.Yaml, expectedSanchoRef), 153 "Expected image to appear once in YAML: %s", f.k8s.Yaml) 154 155 expectedSidecarRef := "gcr.io/some-project-162817/sancho-sidecar:tilt-11cd0b38bc3ceb95" 156 image = store.ImageFromBuildResult(result[iTarget2.ID()]) 157 assert.Equal(t, expectedSidecarRef, image.String()) 158 assert.Equalf(t, 1, strings.Count(f.k8s.Yaml, expectedSidecarRef), 159 "Expected image to appear once in YAML: %s", f.k8s.Yaml) 160 161 assert.Equalf(t, 1, strings.Count(f.k8s.Yaml, "gcr.io/windmill-public-containers/tilt-synclet:"), 162 "Expected synclet to be injected once in YAML: %s", f.k8s.Yaml) 163 } 164 165 func TestNoImageTargets(t *testing.T) { 166 f := newIBDFixture(t, k8s.EnvGKE) 167 defer f.TearDown() 168 169 targName := "some-k8s-manifest" 170 specs := []model.TargetSpec{ 171 model.K8sTarget{ 172 Name: model.TargetName(targName), 173 YAML: testyaml.LonelyPodYAML, 174 }, 175 } 176 177 _, err := f.ibd.BuildAndDeploy(f.ctx, f.st, specs, store.BuildStateSet{}) 178 if err != nil { 179 t.Fatal(err) 180 } 181 182 assert.Equal(t, 0, f.docker.BuildCount, "expect no docker builds") 183 assert.Equalf(t, 1, strings.Count(f.k8s.Yaml, "image: gcr.io/windmill-public-containers/lonely-pod"), 184 "Expected lonely-pod image to appear once in YAML: %s", f.k8s.Yaml) 185 186 expectedLabelStr := fmt.Sprintf("%s: %s", k8s.ManagedByLabel, k8s.ManagedByValue) 187 assert.Equalf(t, 1, strings.Count(f.k8s.Yaml, expectedLabelStr), 188 "Expected \"%s\" label to appear once in YAML: %s", expectedLabelStr, f.k8s.Yaml) 189 } 190 191 func TestImageIsClean(t *testing.T) { 192 f := newIBDFixture(t, k8s.EnvGKE) 193 defer f.TearDown() 194 195 manifest := NewSanchoDockerBuildManifest(f) 196 iTargetID1 := manifest.ImageTargets[0].ID() 197 result1 := store.NewImageBuildResult(iTargetID1, container.MustParseNamedTagged("sancho-base:tilt-prebuilt1")) 198 199 f.docker.ImageListCount = 1 200 201 stateSet := store.BuildStateSet{ 202 iTargetID1: store.NewBuildState(result1, []string{}), 203 } 204 _, err := f.ibd.BuildAndDeploy(f.ctx, f.st, buildTargets(manifest), stateSet) 205 if err != nil { 206 t.Fatal(err) 207 } 208 209 // Expect no build or push, b/c image is clean (i.e. last build was an image build and 210 // no file changes since). 211 assert.Equal(t, 0, f.docker.BuildCount) 212 assert.Equal(t, 0, f.docker.PushCount) 213 } 214 215 func TestImageIsDirtyAfterContainerBuild(t *testing.T) { 216 f := newIBDFixture(t, k8s.EnvGKE) 217 defer f.TearDown() 218 219 manifest := NewSanchoDockerBuildManifest(f) 220 iTargetID1 := manifest.ImageTargets[0].ID() 221 result1 := store.NewLiveUpdateBuildResult( 222 iTargetID1, 223 container.MustParseNamedTagged("sancho-base:tilt-prebuilt1"), 224 []container.ID{container.ID("12345")}) 225 226 stateSet := store.BuildStateSet{ 227 iTargetID1: store.NewBuildState(result1, []string{}), 228 } 229 _, err := f.ibd.BuildAndDeploy(f.ctx, f.st, buildTargets(manifest), stateSet) 230 if err != nil { 231 t.Fatal(err) 232 } 233 234 // Expect build + push; last result has a container ID, which implies that it was an in-place 235 // update, so the current state of this manifest is NOT reflected in an existing image. 236 assert.Equal(t, 1, f.docker.BuildCount) 237 assert.Equal(t, 1, f.docker.PushCount) 238 } 239 240 func TestMultiStageDockerBuild(t *testing.T) { 241 f := newIBDFixture(t, k8s.EnvGKE) 242 defer f.TearDown() 243 244 manifest := NewSanchoDockerBuildMultiStageManifest(f) 245 _, err := f.ibd.BuildAndDeploy(f.ctx, f.st, buildTargets(manifest), store.BuildStateSet{}) 246 if err != nil { 247 t.Fatal(err) 248 } 249 250 assert.Equal(t, 2, f.docker.BuildCount) 251 assert.Equal(t, 1, f.docker.PushCount) 252 assert.Equal(t, 0, f.kp.pushCount) 253 254 expected := expectedFile{ 255 Path: "Dockerfile", 256 Contents: ` 257 FROM sancho-base:tilt-11cd0b38bc3ceb95 258 ADD . . 259 RUN go install github.com/windmilleng/sancho 260 ENTRYPOINT /go/bin/sancho 261 `, 262 } 263 testutils.AssertFileInTar(t, tar.NewReader(f.docker.BuildOptions.Context), expected) 264 } 265 266 func TestMultiStageDockerBuildPreservesSyntaxDirective(t *testing.T) { 267 f := newIBDFixture(t, k8s.EnvGKE) 268 defer f.TearDown() 269 270 baseImage := model.NewImageTarget(SanchoBaseRef).WithBuildDetails(model.DockerBuild{ 271 Dockerfile: `FROM golang:1.10`, 272 BuildPath: f.JoinPath("sancho-base"), 273 }) 274 275 srcImage := model.NewImageTarget(SanchoRef).WithBuildDetails(model.DockerBuild{ 276 Dockerfile: `# syntax = docker/dockerfile:experimental 277 278 FROM sancho-base 279 ADD . . 280 RUN go install github.com/windmilleng/sancho 281 ENTRYPOINT /go/bin/sancho 282 `, 283 BuildPath: f.JoinPath("sancho"), 284 }).WithDependencyIDs([]model.TargetID{baseImage.ID()}) 285 286 m := manifestbuilder.New(f, "sancho"). 287 WithK8sYAML(SanchoYAML). 288 WithImageTargets(baseImage, srcImage). 289 Build() 290 291 _, err := f.ibd.BuildAndDeploy(f.ctx, f.st, buildTargets(m), store.BuildStateSet{}) 292 if err != nil { 293 t.Fatal(err) 294 } 295 296 assert.Equal(t, 2, f.docker.BuildCount) 297 assert.Equal(t, 1, f.docker.PushCount) 298 assert.Equal(t, 0, f.kp.pushCount) 299 300 expected := expectedFile{ 301 Path: "Dockerfile", 302 Contents: `# syntax = docker/dockerfile:experimental 303 304 FROM sancho-base:tilt-11cd0b38bc3ceb95 305 ADD . . 306 RUN go install github.com/windmilleng/sancho 307 ENTRYPOINT /go/bin/sancho 308 `, 309 } 310 testutils.AssertFileInTar(t, tar.NewReader(f.docker.BuildOptions.Context), expected) 311 } 312 313 func TestMultiStageDockerBuildWithFirstImageDirty(t *testing.T) { 314 f := newIBDFixture(t, k8s.EnvGKE) 315 defer f.TearDown() 316 317 manifest := NewSanchoDockerBuildMultiStageManifest(f) 318 iTargetID1 := manifest.ImageTargets[0].ID() 319 iTargetID2 := manifest.ImageTargets[1].ID() 320 result1 := store.NewImageBuildResult(iTargetID1, container.MustParseNamedTagged("sancho-base:tilt-prebuilt1")) 321 result2 := store.NewImageBuildResult(iTargetID2, container.MustParseNamedTagged("sancho:tilt-prebuilt2")) 322 323 newFile := f.WriteFile("sancho-base/message.txt", "message") 324 325 stateSet := store.BuildStateSet{ 326 iTargetID1: store.NewBuildState(result1, []string{newFile}), 327 iTargetID2: store.NewBuildState(result2, nil), 328 } 329 _, err := f.ibd.BuildAndDeploy(f.ctx, f.st, buildTargets(manifest), stateSet) 330 if err != nil { 331 t.Fatal(err) 332 } 333 334 assert.Equal(t, 2, f.docker.BuildCount) 335 assert.Equal(t, 1, f.docker.PushCount) 336 337 expected := expectedFile{ 338 Path: "Dockerfile", 339 Contents: ` 340 FROM sancho-base:tilt-11cd0b38bc3ceb95 341 ADD . . 342 RUN go install github.com/windmilleng/sancho 343 ENTRYPOINT /go/bin/sancho 344 `, 345 } 346 testutils.AssertFileInTar(t, tar.NewReader(f.docker.BuildOptions.Context), expected) 347 } 348 349 func TestMultiStageDockerBuildWithSecondImageDirty(t *testing.T) { 350 f := newIBDFixture(t, k8s.EnvGKE) 351 defer f.TearDown() 352 353 manifest := NewSanchoDockerBuildMultiStageManifest(f) 354 iTargetID1 := manifest.ImageTargets[0].ID() 355 iTargetID2 := manifest.ImageTargets[1].ID() 356 result1 := store.NewImageBuildResult(iTargetID1, container.MustParseNamedTagged("sancho-base:tilt-prebuilt1")) 357 result2 := store.NewImageBuildResult(iTargetID2, container.MustParseNamedTagged("sancho:tilt-prebuilt2")) 358 359 newFile := f.WriteFile("sancho/message.txt", "message") 360 361 f.docker.ImageListCount = 1 362 363 stateSet := store.BuildStateSet{ 364 iTargetID1: store.NewBuildState(result1, nil), 365 iTargetID2: store.NewBuildState(result2, []string{newFile}), 366 } 367 _, err := f.ibd.BuildAndDeploy(f.ctx, f.st, buildTargets(manifest), stateSet) 368 if err != nil { 369 t.Fatal(err) 370 } 371 372 assert.Equal(t, 1, f.docker.BuildCount) 373 374 expected := expectedFile{ 375 Path: "Dockerfile", 376 Contents: ` 377 FROM sancho-base:tilt-prebuilt1 378 ADD . . 379 RUN go install github.com/windmilleng/sancho 380 ENTRYPOINT /go/bin/sancho 381 `, 382 } 383 testutils.AssertFileInTar(t, tar.NewReader(f.docker.BuildOptions.Context), expected) 384 } 385 386 func TestKINDPush(t *testing.T) { 387 f := newIBDFixture(t, k8s.EnvKIND) 388 defer f.TearDown() 389 390 manifest := NewSanchoDockerBuildManifest(f) 391 _, err := f.ibd.BuildAndDeploy(f.ctx, f.st, buildTargets(manifest), store.BuildStateSet{}) 392 if err != nil { 393 t.Fatal(err) 394 } 395 396 assert.Equal(t, 1, f.docker.BuildCount) 397 assert.Equal(t, 1, f.kp.pushCount) 398 assert.Equal(t, 0, f.docker.PushCount) 399 } 400 401 func TestCustomBuildDisablePush(t *testing.T) { 402 f := newIBDFixture(t, k8s.EnvKIND) 403 defer f.TearDown() 404 sha := digest.Digest("sha256:11cd0eb38bc3ceb958ffb2f9bd70be3fb317ce7d255c8a4c3f4af30e298aa1aab") 405 f.docker.Images["gcr.io/some-project-162817/sancho:tilt-build"] = types.ImageInspect{ID: string(sha)} 406 407 manifest := NewSanchoCustomBuildManifestWithPushDisabled(f) 408 _, err := f.ibd.BuildAndDeploy(f.ctx, f.st, buildTargets(manifest), store.BuildStateSet{}) 409 assert.NoError(t, err) 410 411 // but we also didn't try to build or push an image 412 assert.Equal(t, 0, f.docker.BuildCount) 413 assert.Equal(t, 0, f.kp.pushCount) 414 assert.Equal(t, 0, f.docker.PushCount) 415 } 416 417 func TestDeployUsesInjectRef(t *testing.T) { 418 expectedImages := []string{"foo.com/gcr.io_some-project-162817_sancho"} 419 tests := []struct { 420 name string 421 manifest func(f Fixture) model.Manifest 422 expectedImages []string 423 }{ 424 {"docker build", func(f Fixture) model.Manifest { return NewSanchoDockerBuildManifest(f) }, expectedImages}, 425 {"fast build", NewSanchoFastBuildManifest, expectedImages}, 426 {"custom build", NewSanchoCustomBuildManifest, expectedImages}, 427 {"live multi stage", NewSanchoLiveUpdateMultiStageManifest, append(expectedImages, "foo.com/sancho-base")}, 428 } 429 430 for _, test := range tests { 431 t.Run(test.name, func(t *testing.T) { 432 f := newIBDFixture(t, k8s.EnvGKE) 433 defer f.TearDown() 434 435 if test.name == "custom build" { 436 sha := digest.Digest("sha256:11cd0eb38bc3ceb958ffb2f9bd70be3fb317ce7d255c8a4c3f4af30e298aa1aab") 437 f.docker.Images["foo.com/gcr.io_some-project-162817_sancho:tilt-build-1546304461"] = types.ImageInspect{ID: string(sha)} 438 } 439 440 manifest := test.manifest(f) 441 var err error 442 for i := range manifest.ImageTargets { 443 manifest.ImageTargets[i].DeploymentRef, err = container.ReplaceRegistry("foo.com", manifest.ImageTargets[i].ConfigurationRef) 444 if err != nil { 445 t.Fatal(err) 446 } 447 } 448 449 result, err := f.ibd.BuildAndDeploy(f.ctx, f.st, buildTargets(manifest), store.BuildStateSet{}) 450 if err != nil { 451 t.Fatal(err) 452 } 453 454 var observedImages []string 455 for i := range manifest.ImageTargets { 456 id := manifest.ImageTargets[i].ID() 457 image := store.ImageFromBuildResult(result[id]) 458 observedImages = append(observedImages, image.Name()) 459 } 460 461 assert.ElementsMatch(t, test.expectedImages, observedImages) 462 }) 463 } 464 } 465 466 func TestDeployInjectImageEnvVar(t *testing.T) { 467 f := newIBDFixture(t, k8s.EnvGKE) 468 defer f.TearDown() 469 470 manifest := NewSanchoManifestWithImageInEnvVar(f) 471 _, err := f.ibd.BuildAndDeploy(f.ctx, f.st, buildTargets(manifest), store.BuildStateSet{}) 472 if err != nil { 473 t.Fatal(err) 474 } 475 476 entities, err := k8s.ParseYAMLFromString(f.k8s.Yaml) 477 if err != nil { 478 t.Fatal(err) 479 } 480 481 if !assert.Equal(t, 1, len(entities)) { 482 return 483 } 484 485 d := entities[0].Obj.(*v1.Deployment) 486 if !assert.Equal(t, 1, len(d.Spec.Template.Spec.Containers)) { 487 return 488 } 489 490 c := d.Spec.Template.Spec.Containers[0] 491 // container image always gets injected 492 assert.Equal(t, "gcr.io/some-project-162817/sancho:tilt-11cd0b38bc3ceb95", c.Image) 493 expectedEnv := []corev1.EnvVar{ 494 // sancho2 gets injected here because it sets match_in_env_vars in docker_build 495 {Name: "foo", Value: "gcr.io/some-project-162817/sancho2:tilt-11cd0b38bc3ceb95"}, 496 // sancho does not because it doesn't 497 {Name: "bar", Value: "gcr.io/some-project-162817/sancho"}, 498 } 499 assert.Equal(t, expectedEnv, c.Env) 500 } 501 502 func TestDeployInjectsOverrideCommand(t *testing.T) { 503 f := newIBDFixture(t, k8s.EnvGKE) 504 defer f.TearDown() 505 506 cmd := model.ToShellCmd("./foo.sh bar") 507 manifest := NewSanchoDockerBuildManifest(f) 508 iTarg := manifest.ImageTargetAt(0).WithOverrideCommand(cmd) 509 manifest = manifest.WithImageTarget(iTarg) 510 511 _, err := f.ibd.BuildAndDeploy(f.ctx, f.st, buildTargets(manifest), store.BuildStateSet{}) 512 if err != nil { 513 t.Fatal(err) 514 } 515 516 entities, err := k8s.ParseYAMLFromString(f.k8s.Yaml) 517 if err != nil { 518 t.Fatal(err) 519 } 520 521 if !assert.Equal(t, 1, len(entities)) { 522 return 523 } 524 525 d := entities[0].Obj.(*v1.Deployment) 526 if !assert.Equal(t, 1, len(d.Spec.Template.Spec.Containers)) { 527 return 528 } 529 530 c := d.Spec.Template.Spec.Containers[0] 531 532 // Make sure container ref injection worked as expected 533 assert.Equal(t, "gcr.io/some-project-162817/sancho:tilt-11cd0b38bc3ceb95", c.Image) 534 535 assert.Equal(t, cmd.Argv, c.Command) 536 assert.Empty(t, c.Args) 537 } 538 539 func TestDeployInjectOverrideCommandClearsOldCommandAndArgs(t *testing.T) { 540 f := newIBDFixture(t, k8s.EnvGKE) 541 defer f.TearDown() 542 543 cmd := model.ToShellCmd("./foo.sh bar") 544 manifest := NewSanchoDockerBuildManifestWithYaml(f, testyaml.SanchoYAMLWithCommand) 545 iTarg := manifest.ImageTargetAt(0).WithOverrideCommand(cmd) 546 manifest = manifest.WithImageTarget(iTarg) 547 548 _, err := f.ibd.BuildAndDeploy(f.ctx, f.st, buildTargets(manifest), store.BuildStateSet{}) 549 if err != nil { 550 t.Fatal(err) 551 } 552 553 entities, err := k8s.ParseYAMLFromString(f.k8s.Yaml) 554 if err != nil { 555 t.Fatal(err) 556 } 557 558 if !assert.Equal(t, 1, len(entities)) { 559 return 560 } 561 562 d := entities[0].Obj.(*v1.Deployment) 563 if !assert.Equal(t, 1, len(d.Spec.Template.Spec.Containers)) { 564 return 565 } 566 567 c := d.Spec.Template.Spec.Containers[0] 568 assert.Equal(t, cmd.Argv, c.Command) 569 assert.Empty(t, c.Args) 570 } 571 572 func TestCantInjectOverrideCommandWithoutContainer(t *testing.T) { 573 f := newIBDFixture(t, k8s.EnvGKE) 574 defer f.TearDown() 575 576 // CRD YAML: we WILL successfully inject the new image ref, but can't inject 577 // an override command for that image because it's not in a "container" block: 578 // expect an error when we try 579 crdYamlWithSanchoImage := strings.ReplaceAll(testyaml.CRDYAML, testyaml.CRDImage, testyaml.SanchoImage) 580 581 cmd := model.ToShellCmd("./foo.sh bar") 582 manifest := NewSanchoDockerBuildManifestWithYaml(f, crdYamlWithSanchoImage) 583 iTarg := manifest.ImageTargetAt(0).WithOverrideCommand(cmd) 584 manifest = manifest.WithImageTarget(iTarg) 585 586 _, err := f.ibd.BuildAndDeploy(f.ctx, f.st, buildTargets(manifest), store.BuildStateSet{}) 587 if assert.Error(t, err) { 588 assert.Contains(t, err.Error(), "could not inject command") 589 } 590 } 591 592 func TestInjectOverrideCommandsMultipleImages(t *testing.T) { 593 f := newIBDFixture(t, k8s.EnvGKE) 594 defer f.TearDown() 595 596 cmd1 := model.ToShellCmd("./command1.sh foo") 597 cmd2 := model.ToShellCmd("./command2.sh bar baz") 598 599 iTarget1 := NewSanchoDockerBuildImageTarget(f).WithOverrideCommand(cmd1) 600 iTarget2 := NewSanchoSidecarDockerBuildImageTarget(f).WithOverrideCommand(cmd2) 601 kTarget := model.K8sTarget{Name: "sancho", YAML: testyaml.SanchoSidecarYAML}. 602 WithDependencyIDs([]model.TargetID{iTarget1.ID(), iTarget2.ID()}) 603 targets := []model.TargetSpec{iTarget1, iTarget2, kTarget} 604 605 _, err := f.ibd.BuildAndDeploy(f.ctx, f.st, targets, store.BuildStateSet{}) 606 if err != nil { 607 t.Fatal(err) 608 } 609 610 entities, err := k8s.ParseYAMLFromString(f.k8s.Yaml) 611 if err != nil { 612 t.Fatal(err) 613 } 614 615 if !assert.Equal(t, 1, len(entities)) { 616 return 617 } 618 619 d := entities[0].Obj.(*v1.Deployment) 620 if !assert.Equal(t, 2, len(d.Spec.Template.Spec.Containers)) { 621 return 622 } 623 624 sanchoContainer := d.Spec.Template.Spec.Containers[0] 625 sidecarContainer := d.Spec.Template.Spec.Containers[1] 626 627 // Make sure container ref injection worked as expected 628 assert.Equal(t, "gcr.io/some-project-162817/sancho:tilt-11cd0b38bc3ceb95", sanchoContainer.Image) 629 assert.Equal(t, "gcr.io/some-project-162817/sancho-sidecar:tilt-11cd0b38bc3ceb95", sidecarContainer.Image) 630 631 assert.Equal(t, cmd1.Argv, sanchoContainer.Command) 632 assert.Equal(t, cmd2.Argv, sidecarContainer.Command) 633 634 } 635 636 func TestIBDDeployUIDs(t *testing.T) { 637 f := newIBDFixture(t, k8s.EnvGKE) 638 defer f.TearDown() 639 640 manifest := NewSanchoFastBuildManifest(f) 641 result, err := f.ibd.BuildAndDeploy(f.ctx, f.st, buildTargets(manifest), store.BuildStateSet{}) 642 if err != nil { 643 t.Fatal(err) 644 } 645 646 assert.Equal(t, 1, len(result.DeployedUIDSet())) 647 assert.True(t, result.DeployedUIDSet().Contains(f.k8s.LastUpsertResult[0].UID())) 648 } 649 650 func TestDockerBuildTargetStage(t *testing.T) { 651 f := newIBDFixture(t, k8s.EnvGKE) 652 defer f.TearDown() 653 654 iTarget := NewSanchoDockerBuildImageTarget(f) 655 db := iTarget.BuildDetails.(model.DockerBuild) 656 db.TargetStage = "stage" 657 iTarget.BuildDetails = db 658 659 manifest := manifestbuilder.New(f, "sancho"). 660 WithK8sYAML(testyaml.SanchoYAML). 661 WithImageTargets(iTarget). 662 Build() 663 _, err := f.ibd.BuildAndDeploy(f.ctx, f.st, buildTargets(manifest), store.BuildStateSet{}) 664 if err != nil { 665 t.Fatal(err) 666 } 667 assert.Equal(t, "stage", f.docker.BuildOptions.Target) 668 } 669 670 type ibdFixture struct { 671 *tempdir.TempDirFixture 672 ctx context.Context 673 docker *docker.FakeClient 674 k8s *k8s.FakeK8sClient 675 ibd *ImageBuildAndDeployer 676 st *store.TestingStore 677 kp *fakeKINDPusher 678 } 679 680 func newIBDFixture(t *testing.T, env k8s.Env) *ibdFixture { 681 f := tempdir.NewTempDirFixture(t) 682 dir := dirs.NewWindmillDirAt(f.Path()) 683 docker := docker.NewFakeClient() 684 ctx, _, ta := testutils.CtxAndAnalyticsForTest() 685 kClient := k8s.NewFakeK8sClient() 686 kp := &fakeKINDPusher{} 687 clock := fakeClock{time.Date(2019, 1, 1, 1, 1, 1, 1, time.UTC)} 688 ibd, err := provideImageBuildAndDeployer(ctx, docker, kClient, env, dir, clock, kp, ta) 689 if err != nil { 690 t.Fatal(err) 691 } 692 return &ibdFixture{ 693 TempDirFixture: f, 694 ctx: ctx, 695 docker: docker, 696 k8s: kClient, 697 ibd: ibd, 698 st: store.NewTestingStore(), 699 kp: kp, 700 } 701 } 702 703 func (f *ibdFixture) TearDown() { 704 f.k8s.TearDown() 705 f.TempDirFixture.TearDown() 706 } 707 708 type fakeKINDPusher struct { 709 pushCount int 710 } 711 712 func (kp *fakeKINDPusher) PushToKIND(ctx context.Context, ref reference.NamedTagged, w io.Writer) error { 713 kp.pushCount++ 714 return nil 715 }