github.com/tilt-dev/tilt@v0.36.0/internal/controllers/core/tiltfile/reconciler_test.go (about) 1 package tiltfile 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "path/filepath" 8 "reflect" 9 "sort" 10 "strconv" 11 "testing" 12 "time" 13 14 "github.com/pkg/errors" 15 "github.com/stretchr/testify/assert" 16 "github.com/stretchr/testify/require" 17 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 18 "k8s.io/apimachinery/pkg/types" 19 "k8s.io/client-go/util/workqueue" 20 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 21 "sigs.k8s.io/controller-runtime/pkg/reconcile" 22 23 "github.com/tilt-dev/tilt/internal/container" 24 configmap2 "github.com/tilt-dev/tilt/internal/controllers/apis/configmap" 25 "github.com/tilt-dev/tilt/internal/controllers/apis/uibutton" 26 "github.com/tilt-dev/tilt/internal/controllers/fake" 27 "github.com/tilt-dev/tilt/internal/docker" 28 "github.com/tilt-dev/tilt/internal/k8s/testyaml" 29 "github.com/tilt-dev/tilt/internal/store" 30 "github.com/tilt-dev/tilt/internal/testutils/configmap" 31 "github.com/tilt-dev/tilt/internal/testutils/manifestbuilder" 32 "github.com/tilt-dev/tilt/internal/testutils/tempdir" 33 "github.com/tilt-dev/tilt/internal/tiltfile" 34 "github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1" 35 "github.com/tilt-dev/tilt/pkg/model" 36 "github.com/tilt-dev/wmclient/pkg/analytics" 37 ) 38 39 func TestDefault(t *testing.T) { 40 f := newFixture(t) 41 p := f.tempdir.JoinPath("Tiltfile") 42 f.tempdir.WriteFile(p, "print('hello-world')") 43 44 tf := v1alpha1.Tiltfile{ 45 ObjectMeta: metav1.ObjectMeta{ 46 Name: "my-tf", 47 }, 48 Spec: v1alpha1.TiltfileSpec{ 49 Path: p, 50 }, 51 } 52 ts := time.Now() 53 f.Create(&tf) 54 55 // Make sure the FileWatch was created 56 var fw v1alpha1.FileWatch 57 fwKey := types.NamespacedName{Name: "configs:my-tf"} 58 f.MustGet(fwKey, &fw) 59 assert.Equal(t, tf.Spec.Path, fw.Spec.WatchedPaths[0]) 60 61 f.waitForRunning(tf.Name) 62 63 f.popQueue() 64 65 f.waitForTerminatedAfter(tf.Name, ts) 66 67 f.Delete(&tf) 68 69 // Ensure the FileWatch was deleted. 70 assert.False(t, f.Get(fwKey, &fw)) 71 } 72 73 func TestSteadyState(t *testing.T) { 74 f := newFixture(t) 75 p := f.tempdir.JoinPath("Tiltfile") 76 f.tempdir.WriteFile(p, "print('hello-world')") 77 78 tf := v1alpha1.Tiltfile{ 79 ObjectMeta: metav1.ObjectMeta{ 80 Name: "my-tf", 81 }, 82 Spec: v1alpha1.TiltfileSpec{ 83 Path: p, 84 }, 85 } 86 f.createAndWaitForLoaded(&tf) 87 88 // Make sure a second reconcile doesn't update the status again. 89 var tf2 = v1alpha1.Tiltfile{} 90 f.MustReconcile(types.NamespacedName{Name: "my-tf"}) 91 f.MustGet(types.NamespacedName{Name: "my-tf"}, &tf2) 92 assert.Equal(t, tf.ResourceVersion, tf2.ResourceVersion) 93 } 94 95 func TestLiveUpdate(t *testing.T) { 96 f := newFixture(t) 97 p := f.tempdir.JoinPath("Tiltfile") 98 99 luSpec := v1alpha1.LiveUpdateSpec{ 100 BasePath: f.tempdir.Path(), 101 StopPaths: []string{filepath.Join("src", "package.json")}, 102 Syncs: []v1alpha1.LiveUpdateSync{{LocalPath: "src", ContainerPath: "/src"}}, 103 } 104 expectedSpec := *(luSpec.DeepCopy()) 105 expectedSpec.Sources = []v1alpha1.LiveUpdateSource{{ 106 FileWatch: "image:sancho-image", 107 ImageMap: "sancho-image", 108 }} 109 expectedSpec.Selector.Kubernetes = &v1alpha1.LiveUpdateKubernetesSelector{ 110 ImageMapName: "sancho-image", 111 DiscoveryName: "sancho", 112 ApplyName: "sancho", 113 } 114 115 sanchoImage := model.MustNewImageTarget(container.MustParseSelector("sancho-image")). 116 WithLiveUpdateSpec("sancho:sancho-image", luSpec). 117 WithDockerImage(v1alpha1.DockerImageSpec{Context: f.tempdir.Path()}) 118 sancho := manifestbuilder.New(f.tempdir, "sancho"). 119 WithImageTargets(sanchoImage). 120 WithK8sYAML(testyaml.SanchoYAML). 121 Build() 122 f.tfl.Result = tiltfile.TiltfileLoadResult{ 123 Manifests: []model.Manifest{sancho}, 124 } 125 126 tf := v1alpha1.Tiltfile{ 127 ObjectMeta: metav1.ObjectMeta{ 128 Name: "my-tf", 129 }, 130 Spec: v1alpha1.TiltfileSpec{ 131 Path: p, 132 }, 133 } 134 f.createAndWaitForLoaded(&tf) 135 136 assert.Equal(t, "", tf.Status.Terminated.Error) 137 138 var luList = v1alpha1.LiveUpdateList{} 139 f.List(&luList) 140 if assert.Equal(t, 1, len(luList.Items)) { 141 assert.Equal(t, "sancho:sancho-image", luList.Items[0].Name) 142 assert.Equal(t, expectedSpec, luList.Items[0].Spec) 143 } 144 } 145 146 func TestCluster(t *testing.T) { 147 f := newFixture(t) 148 p := f.tempdir.JoinPath("Tiltfile") 149 f.r.k8sContextOverride = "context-override" 150 f.r.k8sNamespaceOverride = "namespace-override" 151 152 expected := &v1alpha1.ClusterConnection{ 153 Kubernetes: &v1alpha1.KubernetesClusterConnection{ 154 Context: string(f.r.k8sContextOverride), 155 Namespace: string(f.r.k8sNamespaceOverride), 156 }, 157 } 158 159 sancho := manifestbuilder.New(f.tempdir, "sancho"). 160 WithK8sYAML(testyaml.SanchoYAML). 161 Build() 162 f.tfl.Result = tiltfile.TiltfileLoadResult{ 163 Manifests: []model.Manifest{sancho}, 164 } 165 166 name := model.MainTiltfileManifestName.String() 167 tf := v1alpha1.Tiltfile{ 168 ObjectMeta: metav1.ObjectMeta{ 169 Name: name, 170 }, 171 Spec: v1alpha1.TiltfileSpec{ 172 Path: p, 173 }, 174 } 175 f.createAndWaitForLoaded(&tf) 176 177 assert.Equal(t, "", tf.Status.Terminated.Error) 178 179 var clList = v1alpha1.ClusterList{} 180 f.List(&clList) 181 if assert.Equal(t, 1, len(clList.Items)) { 182 assert.Equal(t, "default", clList.Items[0].Name) 183 assert.Equal(t, expected, clList.Items[0].Spec.Connection) 184 } 185 } 186 187 func TestLocalServe(t *testing.T) { 188 f := newFixture(t) 189 p := f.tempdir.JoinPath("Tiltfile") 190 191 m := manifestbuilder.New(f.tempdir, "foo").WithLocalServeCmd(".").Build() 192 f.tfl.Result = tiltfile.TiltfileLoadResult{ 193 Manifests: []model.Manifest{m}, 194 } 195 196 tf := v1alpha1.Tiltfile{ 197 ObjectMeta: metav1.ObjectMeta{ 198 Name: "my-tf", 199 }, 200 Spec: v1alpha1.TiltfileSpec{ 201 Path: p, 202 }, 203 } 204 f.createAndWaitForLoaded(&tf) 205 206 assert.Equal(t, "", tf.Status.Terminated.Error) 207 208 a := f.st.WaitForAction(t, reflect.TypeOf(ConfigsReloadedAction{})).(ConfigsReloadedAction) 209 require.Equal(t, 1, len(a.Manifests)) 210 m = a.Manifests[0] 211 require.Equal(t, model.ManifestName("foo"), m.Name) 212 require.IsType(t, model.LocalTarget{}, m.DeployTarget) 213 lt := m.DeployTarget.(model.LocalTarget) 214 require.NotNil(t, lt.ServeCmdDisableSource, "ServeCmdDisableSource is nil") 215 require.NotNil(t, lt.ServeCmdDisableSource.ConfigMap, "ServeCmdDisableSource.ConfigMap is nil") 216 require.Equal(t, "foo-disable", lt.ServeCmdDisableSource.ConfigMap.Name) 217 } 218 219 func TestDockerMetrics(t *testing.T) { 220 f := newFixture(t) 221 p := f.tempdir.JoinPath("Tiltfile") 222 223 sanchoImage := model.MustNewImageTarget(container.MustParseSelector("sancho-image")). 224 WithDockerImage(v1alpha1.DockerImageSpec{Context: f.tempdir.Path()}) 225 sancho := manifestbuilder.New(f.tempdir, "sancho"). 226 WithImageTargets(sanchoImage). 227 WithK8sYAML(testyaml.SanchoYAML). 228 Build() 229 230 f.tfl.Result = tiltfile.TiltfileLoadResult{ 231 Manifests: []model.Manifest{sancho}, 232 } 233 234 tf := v1alpha1.Tiltfile{ 235 ObjectMeta: metav1.ObjectMeta{ 236 Name: "my-tf", 237 }, 238 Spec: v1alpha1.TiltfileSpec{ 239 Path: p, 240 }, 241 } 242 f.createAndWaitForLoaded(&tf) 243 244 connectEvt := analytics.CountEvent{ 245 Name: "api.tiltfile.docker.connect", 246 Tags: map[string]string{ 247 "server.arch": "amd64", 248 "server.version": "20.10.11", 249 "status": "connected", 250 }, 251 N: 1, 252 } 253 assert.ElementsMatch(t, []analytics.CountEvent{connectEvt}, f.ma.Counts) 254 } 255 256 func TestArgsChangeResetsEnabledResources(t *testing.T) { 257 f := newFixture(t) 258 p := f.tempdir.JoinPath("Tiltfile") 259 260 m1 := manifestbuilder.New(f.tempdir, "m1").WithLocalServeCmd("hi").Build() 261 m2 := manifestbuilder.New(f.tempdir, "m2").WithLocalServeCmd("hi").Build() 262 f.tfl.Result = tiltfile.TiltfileLoadResult{ 263 Manifests: []model.Manifest{m1, m2}, 264 EnabledManifests: []model.ManifestName{"m1", "m2"}, 265 } 266 267 tf := v1alpha1.Tiltfile{ 268 ObjectMeta: metav1.ObjectMeta{ 269 Name: "my-tf", 270 }, 271 Spec: v1alpha1.TiltfileSpec{ 272 Path: p, 273 Args: []string{"m1", "m2"}, 274 }, 275 } 276 f.createAndWaitForLoaded(&tf) 277 278 ts := time.Now() 279 280 f.setArgs("my-tf", []string{"m2"}) 281 f.tfl.Result.EnabledManifests = []model.ManifestName{"m2"} 282 283 f.MustReconcile(types.NamespacedName{Name: "my-tf"}) 284 f.waitForRunning("my-tf") 285 f.popQueue() 286 f.waitForTerminatedAfter("my-tf", ts) 287 288 f.requireEnabled(m1, false) 289 f.requireEnabled(m2, true) 290 } 291 292 func TestRunWithoutArgsChangePreservesEnabledResources(t *testing.T) { 293 f := newFixture(t) 294 p := f.tempdir.JoinPath("Tiltfile") 295 296 m1 := manifestbuilder.New(f.tempdir, "m1").WithLocalServeCmd("hi").Build() 297 m2 := manifestbuilder.New(f.tempdir, "m2").WithLocalServeCmd("hi").Build() 298 f.tfl.Result = tiltfile.TiltfileLoadResult{ 299 Manifests: []model.Manifest{m1, m2}, 300 EnabledManifests: []model.ManifestName{"m1", "m2"}, 301 } 302 303 tf := v1alpha1.Tiltfile{ 304 ObjectMeta: metav1.ObjectMeta{ 305 Name: "my-tf", 306 }, 307 Spec: v1alpha1.TiltfileSpec{ 308 Path: p, 309 Args: []string{"m1"}, 310 }, 311 } 312 f.createAndWaitForLoaded(&tf) 313 314 err := configmap.UpsertDisableConfigMap(f.Context(), f.Client, "m2-disable", "isDisabled", false) 315 require.NoError(t, err) 316 317 f.setArgs("my-tf", tf.Spec.Args) 318 319 f.triggerRun("my-tf") 320 321 ts := time.Now() 322 f.MustReconcile(types.NamespacedName{Name: "my-tf"}) 323 f.waitForRunning("my-tf") 324 f.popQueue() 325 f.waitForTerminatedAfter("my-tf", ts) 326 327 f.requireEnabled(m1, true) 328 f.requireEnabled(m2, true) 329 } 330 331 func TestTiltfileFailurePreservesEnabledResources(t *testing.T) { 332 f := newFixture(t) 333 p := f.tempdir.JoinPath("Tiltfile") 334 335 m1 := manifestbuilder.New(f.tempdir, "m1").WithLocalServeCmd("hi").Build() 336 m2 := manifestbuilder.New(f.tempdir, "m2").WithLocalServeCmd("hi").Build() 337 f.tfl.Result = tiltfile.TiltfileLoadResult{ 338 Manifests: []model.Manifest{m1, m2}, 339 EnabledManifests: []model.ManifestName{"m1"}, 340 } 341 342 tf := v1alpha1.Tiltfile{ 343 ObjectMeta: metav1.ObjectMeta{ 344 Name: "my-tf", 345 }, 346 Spec: v1alpha1.TiltfileSpec{ 347 Path: p, 348 Args: []string{"m1"}, 349 }, 350 } 351 f.createAndWaitForLoaded(&tf) 352 353 f.tfl.Result = tiltfile.TiltfileLoadResult{ 354 Manifests: []model.Manifest{m1, m2}, 355 EnabledManifests: []model.ManifestName{}, 356 Error: errors.New("unknown manifest: m3"), 357 } 358 359 f.triggerRun("my-tf") 360 361 ts := time.Now() 362 f.MustReconcile(types.NamespacedName{Name: "my-tf"}) 363 f.waitForRunning("my-tf") 364 f.popQueue() 365 f.waitForTerminatedAfter("my-tf", ts) 366 367 f.requireEnabled(m1, true) 368 f.requireEnabled(m2, false) 369 } 370 371 func TestCancel(t *testing.T) { 372 f := newFixture(t) 373 p := f.tempdir.JoinPath("Tiltfile") 374 f.tempdir.WriteFile(p, "print('hello-world')") 375 376 f.tfl.Delegate = newBlockingTiltfileLoader() 377 378 tf := v1alpha1.Tiltfile{ 379 ObjectMeta: metav1.ObjectMeta{ 380 Name: "my-tf", 381 }, 382 Spec: v1alpha1.TiltfileSpec{ 383 Path: p, 384 StopOn: &v1alpha1.StopOnSpec{UIButtons: []string{uibutton.StopBuildButtonName("my-tf")}}, 385 }, 386 } 387 388 cancelButton := uibutton.StopBuildButton(tf.Name) 389 err := f.Client.Create(f.Context(), cancelButton) 390 require.NoError(t, err) 391 392 ts := time.Now() 393 f.Create(&tf) 394 395 f.waitForRunning(tf.Name) 396 397 cancelButton.Status.LastClickedAt = metav1.NowMicro() 398 f.UpdateStatus(cancelButton) 399 require.NoError(t, err) 400 401 f.MustReconcile(types.NamespacedName{Name: tf.Name}) 402 403 f.popQueue() 404 405 f.waitForTerminatedAfter(tf.Name, ts) 406 407 f.Get(types.NamespacedName{Name: tf.Name}, &tf) 408 require.NotNil(t, tf.Status.Terminated) 409 require.Equal(t, "build canceled", tf.Status.Terminated.Error) 410 } 411 412 func TestCancelClickedBeforeLoad(t *testing.T) { 413 f := newFixture(t) 414 p := f.tempdir.JoinPath("Tiltfile") 415 f.tempdir.WriteFile(p, "print('hello-world')") 416 417 tfl := newBlockingTiltfileLoader() 418 f.tfl.Delegate = tfl 419 420 tf := v1alpha1.Tiltfile{ 421 ObjectMeta: metav1.ObjectMeta{ 422 Name: "my-tf", 423 }, 424 Spec: v1alpha1.TiltfileSpec{ 425 Path: p, 426 StopOn: &v1alpha1.StopOnSpec{UIButtons: []string{uibutton.StopBuildButtonName("my-tf")}}, 427 }, 428 } 429 430 cancelButton := uibutton.StopBuildButton(tf.Name) 431 cancelButton.Status.LastClickedAt = metav1.NewMicroTime(time.Now().Add(-time.Second)) 432 err := f.Client.Create(f.Context(), cancelButton) 433 require.NoError(t, err) 434 435 nn := types.NamespacedName{Name: tf.Name} 436 437 ts := time.Now() 438 f.Create(&tf) 439 440 f.waitForRunning(tf.Name) 441 442 // give the reconciler a chance to observe the cancel button click 443 f.MustReconcile(nn) 444 445 // finish the build 446 tfl.Complete() 447 448 f.MustReconcile(nn) 449 450 f.popQueue() 451 452 f.waitForTerminatedAfter(tf.Name, ts) 453 454 f.Get(nn, &tf) 455 require.NotNil(t, tf.Status.Terminated) 456 require.Equal(t, "", tf.Status.Terminated.Error) 457 } 458 459 func TestPushBaseImageIssue6486(t *testing.T) { 460 f := newFixture(t) 461 p := f.tempdir.JoinPath("Tiltfile") 462 463 image1 := model.MustNewImageTarget(container.MustParseSelector("image-1")). 464 WithDockerImage(v1alpha1.DockerImageSpec{Context: f.tempdir.Path()}) 465 image2 := model.MustNewImageTarget(container.MustParseSelector("image-2")). 466 WithDockerImage(v1alpha1.DockerImageSpec{Context: f.tempdir.Path()}) 467 image3 := model.MustNewImageTarget(container.MustParseSelector("image-3")). 468 WithDockerImage(v1alpha1.DockerImageSpec{Context: f.tempdir.Path()}). 469 WithImageMapDeps([]string{"image-1", "image-2"}) 470 471 service1 := manifestbuilder.New(f.tempdir, "service-1"). 472 WithImageTargets(image1). 473 WithK8sYAML(testyaml.Deployment("service-1", "image-1")). 474 Build() 475 service3 := manifestbuilder.New(f.tempdir, "service-3"). 476 WithImageTargets(image1, image2, image3). 477 WithK8sYAML(testyaml.Deployment("service-3", "image-3")). 478 Build() 479 480 f.tfl.Result = tiltfile.TiltfileLoadResult{ 481 Manifests: []model.Manifest{service1, service3}, 482 } 483 484 name := model.MainTiltfileManifestName.String() 485 tf := v1alpha1.Tiltfile{ 486 ObjectMeta: metav1.ObjectMeta{ 487 Name: name, 488 }, 489 Spec: v1alpha1.TiltfileSpec{ 490 Path: p, 491 }, 492 } 493 f.createAndWaitForLoaded(&tf) 494 495 assert.Equal(t, "", tf.Status.Terminated.Error) 496 497 var imageList = v1alpha1.DockerImageList{} 498 f.List(&imageList) 499 500 sort.Slice(imageList.Items, func(i, j int) bool { 501 return imageList.Items[i].Name < imageList.Items[j].Name 502 }) 503 504 if assert.Equal(t, 4, len(imageList.Items)) { 505 assert.Equal(t, "service-1:image-1", imageList.Items[0].Name) 506 assert.Equal(t, v1alpha1.ClusterImageNeedsPush, imageList.Items[0].Spec.ClusterNeeds) 507 assert.Equal(t, "service-3:image-1", imageList.Items[1].Name) 508 assert.Equal(t, v1alpha1.ClusterImageNeedsPush, imageList.Items[1].Spec.ClusterNeeds) 509 assert.Equal(t, "service-3:image-2", imageList.Items[2].Name) 510 assert.Equal(t, v1alpha1.ClusterImageNeedsBase, imageList.Items[2].Spec.ClusterNeeds) 511 assert.Equal(t, "service-3:image-3", imageList.Items[3].Name) 512 assert.Equal(t, v1alpha1.ClusterImageNeedsPush, imageList.Items[3].Spec.ClusterNeeds) 513 } 514 } 515 516 type testStore struct { 517 *store.TestingStore 518 out *bytes.Buffer 519 } 520 521 func NewTestingStore() *testStore { 522 return &testStore{ 523 TestingStore: store.NewTestingStore(), 524 out: bytes.NewBuffer(nil), 525 } 526 } 527 528 func (s *testStore) Dispatch(action store.Action) { 529 s.TestingStore.Dispatch(action) 530 531 logAction, ok := action.(store.LogAction) 532 if ok { 533 _, _ = fmt.Fprintf(s.out, "%s", logAction.Message()) 534 } 535 } 536 537 type fixture struct { 538 *fake.ControllerFixture 539 tempdir *tempdir.TempDirFixture 540 st *testStore 541 r *Reconciler 542 q workqueue.TypedRateLimitingInterface[reconcile.Request] 543 tfl *tiltfile.FakeTiltfileLoader 544 ma *analytics.MemoryAnalytics 545 } 546 547 func newFixture(t *testing.T) *fixture { 548 cfb := fake.NewControllerFixtureBuilder(t) 549 tf := tempdir.NewTempDirFixture(t) 550 551 st := NewTestingStore() 552 tfl := tiltfile.NewFakeTiltfileLoader() 553 d := docker.NewFakeClient() 554 r := NewReconciler(st, tfl, d, cfb.Client, v1alpha1.NewScheme(), store.EngineModeUp, "", "", 0) 555 q := workqueue.NewTypedRateLimitingQueue[reconcile.Request]( 556 workqueue.NewTypedItemExponentialFailureRateLimiter[reconcile.Request](time.Millisecond, time.Millisecond)) 557 _ = r.requeuer.Start(context.Background(), q) 558 559 return &fixture{ 560 ControllerFixture: cfb.Build(r), 561 tempdir: tf, 562 st: st, 563 r: r, 564 q: q, 565 tfl: tfl, 566 ma: cfb.Analytics(), 567 } 568 } 569 570 // Wait for the next item on the workqueue, then run reconcile on it. 571 func (f *fixture) popQueue() { 572 f.T().Helper() 573 574 done := make(chan error) 575 go func() { 576 item, _ := f.q.Get() 577 _, err := f.r.Reconcile(f.Context(), item) 578 f.q.Done(item) 579 done <- err 580 }() 581 582 select { 583 case <-time.After(time.Second): 584 f.T().Fatal("timeout waiting for workqueue") 585 case err := <-done: 586 assert.NoError(f.T(), err) 587 } 588 } 589 590 func (f *fixture) waitForTerminatedAfter(name string, ts time.Time) { 591 require.Eventually(f.T(), func() bool { 592 var tf v1alpha1.Tiltfile 593 f.MustGet(types.NamespacedName{Name: name}, &tf) 594 return tf.Status.Terminated != nil && tf.Status.Terminated.FinishedAt.After(ts) 595 }, time.Second, time.Millisecond, "waiting for tiltfile to finish running") 596 } 597 598 func (f *fixture) waitForRunning(name string) { 599 require.Eventually(f.T(), func() bool { 600 var tf v1alpha1.Tiltfile 601 f.MustGet(types.NamespacedName{Name: name}, &tf) 602 return tf.Status.Running != nil 603 }, time.Second, time.Millisecond, "waiting for tiltfile to start running") 604 } 605 606 func (f *fixture) createAndWaitForLoaded(tf *v1alpha1.Tiltfile) { 607 ts := time.Now() 608 f.Create(tf) 609 610 f.waitForRunning(tf.Name) 611 612 f.popQueue() 613 614 f.waitForTerminatedAfter(tf.Name, ts) 615 616 f.MustGet(types.NamespacedName{Name: tf.Name}, tf) 617 } 618 619 func (f *fixture) triggerRun(name string) { 620 queue := configmap2.TriggerQueueCreate([]configmap2.TriggerQueueEntry{{Name: model.ManifestName(name)}}) 621 f.Create(&queue) 622 } 623 624 func (f *fixture) setArgs(name string, args []string) { 625 tf := v1alpha1.Tiltfile{ 626 ObjectMeta: metav1.ObjectMeta{ 627 Name: name, 628 }, 629 } 630 _, err := controllerutil.CreateOrUpdate(f.Context(), f.Client, &tf, func() error { 631 tf.Spec.Args = args 632 return nil 633 }) 634 require.NoError(f.T(), err) 635 } 636 637 func (f *fixture) requireEnabled(m model.Manifest, isEnabled bool) { 638 var cm v1alpha1.ConfigMap 639 f.MustGet(types.NamespacedName{Name: disableConfigMapName(m)}, &cm) 640 isDisabled, err := strconv.ParseBool(cm.Data["isDisabled"]) 641 require.NoError(f.T(), err) 642 actualIsEnabled := !isDisabled 643 require.Equal(f.T(), isEnabled, actualIsEnabled, "is %s enabled", m.Name) 644 } 645 646 // builds block until canceled or manually completed 647 type blockingTiltfileLoader struct { 648 completionChan chan struct{} 649 } 650 651 func newBlockingTiltfileLoader() blockingTiltfileLoader { 652 return blockingTiltfileLoader{completionChan: make(chan struct{})} 653 } 654 655 func (b blockingTiltfileLoader) Load(ctx context.Context, tf *v1alpha1.Tiltfile, prevResult *tiltfile.TiltfileLoadResult) tiltfile.TiltfileLoadResult { 656 select { 657 case <-ctx.Done(): 658 case <-b.completionChan: 659 } 660 return tiltfile.TiltfileLoadResult{} 661 } 662 663 func (b blockingTiltfileLoader) Complete() { 664 close(b.completionChan) 665 }