diff --git a/internal/cli/wire_gen.go b/internal/cli/wire_gen.go index a1e266cbf7..a5c9a3c90a 100644 --- a/internal/cli/wire_gen.go +++ b/internal/cli/wire_gen.go @@ -125,7 +125,7 @@ func wireDemo(ctx context.Context, branch demo.RepoBranch, analytics2 *analytics imageReaper := build.NewImageReaper(switchCli) imageController := engine.NewImageController(imageReaper) defaults := _wireDefaultsValue - tiltfileLoader := tiltfile.ProvideTiltfileLoader(analytics2, k8sClient, dockerComposeClient, kubeContext, defaults) + tiltfileLoader := tiltfile.ProvideTiltfileLoader(analytics2, k8sClient, dockerComposeClient, kubeContext, env, defaults) configsController := engine.NewConfigsController(tiltfileLoader, switchCli) dockerComposeEventWatcher := engine.NewDockerComposeEventWatcher(dockerComposeClient) dockerComposeLogManager := engine.NewDockerComposeLogManager(dockerComposeClient) @@ -261,7 +261,7 @@ func wireThreads(ctx context.Context, analytics2 *analytics.TiltAnalytics) (Thre imageReaper := build.NewImageReaper(switchCli) imageController := engine.NewImageController(imageReaper) defaults := _wireDefaultsValue - tiltfileLoader := tiltfile.ProvideTiltfileLoader(analytics2, k8sClient, dockerComposeClient, kubeContext, defaults) + tiltfileLoader := tiltfile.ProvideTiltfileLoader(analytics2, k8sClient, dockerComposeClient, kubeContext, env, defaults) configsController := engine.NewConfigsController(tiltfileLoader, switchCli) dockerComposeEventWatcher := engine.NewDockerComposeEventWatcher(dockerComposeClient) dockerComposeLogManager := engine.NewDockerComposeLogManager(dockerComposeClient) @@ -477,7 +477,7 @@ func wireDownDeps(ctx context.Context, tiltAnalytics *analytics.TiltAnalytics) ( } dockerComposeClient := dockercompose.NewDockerComposeClient(localEnv) defaults := _wireDefaultsValue - tiltfileLoader := tiltfile.ProvideTiltfileLoader(tiltAnalytics, k8sClient, dockerComposeClient, kubeContext, defaults) + tiltfileLoader := tiltfile.ProvideTiltfileLoader(tiltAnalytics, k8sClient, dockerComposeClient, kubeContext, env, defaults) downDeps := ProvideDownDeps(tiltfileLoader, dockerComposeClient, k8sClient) return downDeps, nil } diff --git a/internal/demo/script.go b/internal/demo/script.go index f8ef0c5f15..d795c7fadd 100644 --- a/internal/demo/script.go +++ b/internal/demo/script.go @@ -148,7 +148,7 @@ func (m *podMonitor) waitUntilCond(ctx context.Context, f func() bool) error { } func (s Script) Run(ctx context.Context) error { - if !s.env.IsLocalCluster() { + if !s.env.UsesLocalDockerRegistry() { _, _ = fmt.Fprintf(os.Stderr, "tilt demo mode only supports Docker For Mac, Minikube, and MicroK8s\n") _, _ = fmt.Fprintf(os.Stderr, "check your current cluster with:\n") _, _ = fmt.Fprintf(os.Stderr, "\nkubectl config get-contexts\n\n") diff --git a/internal/engine/build_and_deployer.go b/internal/engine/build_and_deployer.go index a1a9b3bad3..cb6104919b 100644 --- a/internal/engine/build_and_deployer.go +++ b/internal/engine/build_and_deployer.go @@ -101,5 +101,5 @@ func DefaultBuildOrder(lubad *LiveUpdateBuildAndDeployer, ibad *ImageBuildAndDep } func shouldUseSynclet(updMode UpdateMode, env k8s.Env, runtime container.Runtime) bool { - return updMode == UpdateModeAuto && !env.IsLocalCluster() && runtime == container.RuntimeDocker + return updMode == UpdateModeAuto && !env.UsesLocalDockerRegistry() && runtime == container.RuntimeDocker } diff --git a/internal/engine/image_build_and_deployer.go b/internal/engine/image_build_and_deployer.go index 49c6eff522..11ac864755 100644 --- a/internal/engine/image_build_and_deployer.go +++ b/internal/engine/image_build_and_deployer.go @@ -322,7 +322,7 @@ func (ibd *ImageBuildAndDeployer) deploy(ctx context.Context, st store.RStore, p // The k8s will use the image already available // in the local docker daemon. func (ibd *ImageBuildAndDeployer) canAlwaysSkipPush() bool { - return ibd.env.IsLocalCluster() && ibd.runtime == container.RuntimeDocker + return ibd.env.UsesLocalDockerRegistry() && ibd.runtime == container.RuntimeDocker } // Create a new ImageTarget with the dockerfiles rewritten diff --git a/internal/engine/live_update_build_and_deployer.go b/internal/engine/live_update_build_and_deployer.go index 0a6886420b..7129fa7a88 100644 --- a/internal/engine/live_update_build_and_deployer.go +++ b/internal/engine/live_update_build_and_deployer.go @@ -262,7 +262,7 @@ func (lubad *LiveUpdateBuildAndDeployer) containerUpdaterForSpecs(specs []model. return lubad.scu } - if lubad.runtime == container.RuntimeDocker && lubad.env.IsLocalCluster() { + if lubad.runtime == container.RuntimeDocker && lubad.env.UsesLocalDockerRegistry() { return lubad.dcu } diff --git a/internal/engine/update_mode.go b/internal/engine/update_mode.go index b6cdd9515c..943da0b08a 100644 --- a/internal/engine/update_mode.go +++ b/internal/engine/update_mode.go @@ -56,7 +56,7 @@ func ProvideUpdateMode(flag UpdateModeFlag, env k8s.Env, runtime container.Runti mode := UpdateMode(flag) if mode == UpdateModeContainer { - if !env.IsLocalCluster() || runtime != container.RuntimeDocker { + if !env.UsesLocalDockerRegistry() || runtime != container.RuntimeDocker { return "", fmt.Errorf("update mode %q is only valid with local Docker clusters like Docker For Mac, Minikube, and MicroK8s", flag) } } diff --git a/internal/engine/upper_test.go b/internal/engine/upper_test.go index 77ea00f5db..0e799d8a26 100644 --- a/internal/engine/upper_test.go +++ b/internal/engine/upper_test.go @@ -2660,9 +2660,9 @@ func newTestFixture(t *testing.T) *testFixture { dockerClient := docker.NewFakeClient() reaper := build.NewImageReaper(dockerClient) - k8s := k8s.NewFakeK8sClient() - pw := NewPodWatcher(k8s) - sw := NewServiceWatcher(k8s, "") + kCli := k8s.NewFakeK8sClient() + pw := NewPodWatcher(kCli) + sw := NewServiceWatcher(kCli, "") fakeHud := hud.NewFakeHud() @@ -2670,7 +2670,7 @@ func newTestFixture(t *testing.T) *testFixture { st := store.NewStore(UpperReducer, store.LogActionsFlag(false)) st.AddSubscriber(ctx, fSub) - plm := NewPodLogManager(k8s) + plm := NewPodLogManager(kCli) bc := NewBuildController(b) err := os.Mkdir(f.JoinPath(".git"), os.FileMode(0777)) @@ -2679,14 +2679,14 @@ func newTestFixture(t *testing.T) *testFixture { } fwm := NewWatchManager(watcher.newSub, timerMaker.maker()) - pfc := NewPortForwardController(k8s) + pfc := NewPortForwardController(kCli) ic := NewImageController(reaper) tas := NewTiltAnalyticsSubscriber(ta) ar := ProvideAnalyticsReporter(ta, st) fakeDcc := dockercompose.NewFakeDockerComposeClient(t, ctx) - tfl := tiltfile.ProvideTiltfileLoader(ta, k8s, fakeDcc, "fake-context", feature.MainDefaults) + tfl := tiltfile.ProvideTiltfileLoader(ta, kCli, fakeDcc, "fake-context", k8s.EnvDockerDesktop, feature.MainDefaults) cc := NewConfigsController(tfl, dockerClient) dcw := NewDockerComposeEventWatcher(fakeDcc) dclm := NewDockerComposeLogManager(fakeDcc) @@ -2694,11 +2694,11 @@ func newTestFixture(t *testing.T) *testFixture { sCli := synclet.NewTestSyncletClient(dockerClient) sGRPCCli, err := synclet.FakeGRPCWrapper(ctx, sCli) assert.NoError(t, err) - sm := containerupdate.NewSyncletManagerForTests(k8s, sGRPCCli, sCli) + sm := containerupdate.NewSyncletManagerForTests(kCli, sGRPCCli, sCli) hudsc := server.ProvideHeadsUpServerController(0, &server.HeadsUpServer{}, assets.NewFakeServer(), model.WebURL{}, false) ghc := &github.FakeClient{} sc := &client.FakeSailClient{} - ewm := NewEventWatchManager(k8s, clockwork.NewRealClock()) + ewm := NewEventWatchManager(kCli, clockwork.NewRealClock()) ret := &testFixture{ TempDirFixture: f, @@ -2708,7 +2708,7 @@ func newTestFixture(t *testing.T) *testFixture { fsWatcher: watcher, timerMaker: &timerMaker, docker: dockerClient, - kClient: k8s, + kClient: kCli, hud: fakeHud, log: log, store: st, diff --git a/internal/k8s/env.go b/internal/k8s/env.go index 2d1f66ab48..c31c557ed3 100644 --- a/internal/k8s/env.go +++ b/internal/k8s/env.go @@ -20,10 +20,14 @@ const ( EnvNone Env = "none" // k8s not running (not neces. a problem, e.g. if using Tilt x Docker Compose) ) -func (e Env) IsLocalCluster() bool { +func (e Env) UsesLocalDockerRegistry() bool { return e == EnvMinikube || e == EnvDockerDesktop || e == EnvMicroK8s } +func (e Env) IsLocalCluster() bool { + return e == EnvMinikube || e == EnvDockerDesktop || e == EnvMicroK8s || e == EnvKIND +} + func ProvideEnv(kubeConfig *api.Config) Env { return EnvFromConfig(kubeConfig) } @@ -42,25 +46,6 @@ func ProvideKubeConfig(clientLoader clientcmd.ClientConfig) (*api.Config, error) return config, nil } -func EnvFromString(s string) Env { - if Env(s) == EnvMinikube { - return EnvMinikube - } else if Env(s) == EnvDockerDesktop || s == "docker-desktop" { - return EnvDockerDesktop - } else if Env(s) == EnvMicroK8s { - return EnvMicroK8s - } else if strings.HasPrefix(s, "kubernetes-admin@kind") { - return EnvKIND - } else if Env(s) == EnvNone { - return EnvNone - } else if strings.HasPrefix(s, string(EnvGKE)) { - // GKE context strings look like: - // gke_blorg-dev_us-central1-b_blorg - return EnvGKE - } - return EnvUnknown -} - func EnvFromConfig(config *api.Config) Env { n := config.CurrentContext diff --git a/internal/k8s/env_test.go b/internal/k8s/env_test.go index e9bfb65de1..714efabfac 100644 --- a/internal/k8s/env_test.go +++ b/internal/k8s/env_test.go @@ -11,28 +11,6 @@ type expectedEnv struct { string } -func TestEnvFromString(t *testing.T) { - table := []expectedEnv{ - {EnvMinikube, "minikube"}, - {EnvDockerDesktop, "docker-for-desktop"}, - {EnvDockerDesktop, "docker-desktop"}, - {EnvGKE, "gke_blorg-dev_us-central1-b_blorg"}, - {EnvMicroK8s, "microk8s"}, - {EnvUnknown, "aws"}, - {EnvKIND, "kubernetes-admin@kind"}, - {EnvKIND, "kubernetes-admin@kind-1"}, - } - - for _, tt := range table { - t.Run(tt.string, func(t *testing.T) { - actual := EnvFromString(tt.string) - if actual != tt.expected { - t.Errorf("Expected %s, actual %s", tt.expected, actual) - } - }) - } -} - type expectedConfig struct { expected Env input *api.Config diff --git a/internal/tiltfile/k8s.go b/internal/tiltfile/k8s.go index 36150a813e..0c1629c6b6 100644 --- a/internal/tiltfile/k8s.go +++ b/internal/tiltfile/k8s.go @@ -6,18 +6,16 @@ import ( "strconv" "strings" - "github.com/windmilleng/tilt/internal/sliceutils" - - "go.starlark.net/syntax" - "github.com/docker/distribution/reference" "github.com/pkg/errors" "go.starlark.net/starlark" + "go.starlark.net/syntax" "k8s.io/apimachinery/pkg/labels" "github.com/windmilleng/tilt/internal/container" "github.com/windmilleng/tilt/internal/k8s" "github.com/windmilleng/tilt/internal/model" + "github.com/windmilleng/tilt/internal/sliceutils" ) type referenceList []reference.Named @@ -887,3 +885,44 @@ func newK8sObjectID(e k8s.K8sEntity) k8sObjectID { func (s *tiltfileState) k8sContext(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { return starlark.String(s.kubeContext), nil } + +func (s *tiltfileState) allowK8SContexts(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + var contexts starlark.Value + if err := starlark.UnpackArgs(fn.Name(), args, kwargs, + "contexts", &contexts, + ); err != nil { + return nil, err + } + + for _, c := range starlarkValueOrSequenceToSlice(contexts) { + switch val := c.(type) { + case starlark.String: + s.allowedK8SContexts = append(s.allowedK8SContexts, k8s.KubeContext(val)) + default: + return nil, fmt.Errorf("allow_k8s_contexts contexts must be a string or a sequence of strings; found a %T", val) + + } + } + + return starlark.None, nil +} + +func (s *tiltfileState) validateK8SContext(context k8s.KubeContext, env k8s.Env) error { + if env.IsLocalCluster() { + return nil + } + + for _, c := range s.allowedK8SContexts { + if c == context { + return nil + } + } + + return fmt.Errorf( + `Stop! '%s' might be production. +If you're sure you want to deploy there, add: +allow_k8s_contexts('%s') +to your Tiltfile. Otherwise, switch k8s contexts and restart Tilt.`, + context, + context) +} diff --git a/internal/tiltfile/tiltfile.go b/internal/tiltfile/tiltfile.go index de769f7c9d..2f0888d05c 100644 --- a/internal/tiltfile/tiltfile.go +++ b/internal/tiltfile/tiltfile.go @@ -72,12 +72,14 @@ func ProvideTiltfileLoader( kCli k8s.Client, dcCli dockercompose.DockerComposeClient, kubeContext k8s.KubeContext, + kubeEnv k8s.Env, fDefaults feature.Defaults) TiltfileLoader { return tiltfileLoader{ analytics: analytics, kCli: kCli, dcCli: dcCli, kubeContext: kubeContext, + kubeEnv: kubeEnv, fDefaults: fDefaults, } } @@ -87,6 +89,7 @@ type tiltfileLoader struct { kCli k8s.Client dcCli dockercompose.DockerComposeClient kubeContext k8s.KubeContext + kubeEnv k8s.Env fDefaults feature.Defaults } @@ -110,7 +113,7 @@ func (tfl tiltfileLoader) Load(ctx context.Context, filename string, matching ma } privateRegistry := tfl.kCli.PrivateRegistry(ctx) - s := newTiltfileState(ctx, tfl.dcCli, tfl.kubeContext, privateRegistry, feature.FromDefaults(tfl.fDefaults)) + s := newTiltfileState(ctx, tfl.dcCli, tfl.kubeContext, tfl.kubeEnv, privateRegistry, feature.FromDefaults(tfl.fDefaults)) printedWarnings := false defer func() { tlr.ConfigFiles = s.configFiles @@ -141,6 +144,11 @@ func (tfl tiltfileLoader) Load(ctx context.Context, filename string, matching ma if err != nil { return TiltfileLoadResult{}, err } + + err = s.validateK8SContext(s.kubeContext, s.kubeEnv) + if err != nil { + return TiltfileLoadResult{}, err + } } else { manifests, err = s.translateDC(resources.dc) if err != nil { diff --git a/internal/tiltfile/tiltfile_state.go b/internal/tiltfile/tiltfile_state.go index 2d5b337055..352886a889 100644 --- a/internal/tiltfile/tiltfile_state.go +++ b/internal/tiltfile/tiltfile_state.go @@ -33,6 +33,7 @@ type tiltfileState struct { ctx context.Context dcCli dockercompose.DockerComposeClient kubeContext k8s.KubeContext + kubeEnv k8s.Env privateRegistry container.Registry features feature.FeatureSet @@ -57,6 +58,7 @@ type tiltfileState struct { k8sResourceAssemblyVersion int k8sResourceAssemblyVersionReason k8sResourceAssemblyVersionReason workloadToResourceFunction workloadToResourceFunction + allowedK8SContexts []k8s.KubeContext // for assembly usedImages map[string]bool @@ -94,11 +96,18 @@ const ( k8sResourceAssemblyVersionReasonExplicit ) -func newTiltfileState(ctx context.Context, dcCli dockercompose.DockerComposeClient, kubeContext k8s.KubeContext, privateRegistry container.Registry, features feature.FeatureSet) *tiltfileState { +func newTiltfileState( + ctx context.Context, + dcCli dockercompose.DockerComposeClient, + kubeContext k8s.KubeContext, + kubeEnv k8s.Env, + privateRegistry container.Registry, + features feature.FeatureSet) *tiltfileState { return &tiltfileState{ ctx: ctx, dcCli: dcCli, kubeContext: kubeContext, + kubeEnv: kubeEnv, privateRegistry: privateRegistry, buildIndex: newBuildIndex(), k8sByName: make(map[string]*k8sResource), @@ -165,6 +174,7 @@ const ( k8sImageJSONPathN = "k8s_image_json_path" workloadToResourceFunctionN = "workload_to_resource_function" k8sContextN = "k8s_context" + allowK8SContexts = "allow_k8s_contexts" // file functions localGitRepoN = "local_git_repo" @@ -294,6 +304,7 @@ func (s *tiltfileState) predeclared() starlark.StringDict { addBuiltin(r, k8sImageJSONPathN, s.k8sImageJsonPath) addBuiltin(r, workloadToResourceFunctionN, s.workloadToResourceFunctionFn) addBuiltin(r, k8sContextN, s.k8sContext) + addBuiltin(r, allowK8SContexts, s.allowK8SContexts) addBuiltin(r, localGitRepoN, s.localGitRepo) addBuiltin(r, kustomizeN, s.kustomize) addBuiltin(r, helmN, s.helm) diff --git a/internal/tiltfile/tiltfile_test.go b/internal/tiltfile/tiltfile_test.go index afcd8315da..27ddd12c2f 100644 --- a/internal/tiltfile/tiltfile_test.go +++ b/internal/tiltfile/tiltfile_test.go @@ -10,17 +10,13 @@ import ( "strings" "testing" - appsv1 "k8s.io/api/apps/v1" - - "github.com/windmilleng/tilt/internal/testutils" - - "github.com/windmilleng/tilt/internal/sliceutils" - "github.com/stretchr/testify/assert" "github.com/windmilleng/wmclient/pkg/analytics" + appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" + tiltanalytics "github.com/windmilleng/tilt/internal/analytics" "github.com/windmilleng/tilt/internal/container" "github.com/windmilleng/tilt/internal/docker" "github.com/windmilleng/tilt/internal/dockercompose" @@ -30,6 +26,8 @@ import ( "github.com/windmilleng/tilt/internal/k8s/testyaml" "github.com/windmilleng/tilt/internal/model" "github.com/windmilleng/tilt/internal/ospath" + "github.com/windmilleng/tilt/internal/sliceutils" + "github.com/windmilleng/tilt/internal/testutils" "github.com/windmilleng/tilt/internal/testutils/tempdir" "github.com/windmilleng/tilt/internal/tiltfile/testdata" "github.com/windmilleng/tilt/internal/yaml" @@ -936,7 +934,7 @@ docker_build('gcr.io/bar', 'bar') k8s_yaml('bar.yaml') `) - _, err := f.tfl.Load(f.ctx, f.JoinPath("Tiltfile"), matchMap("baz")) + _, err := f.newTiltfileLoader().Load(f.ctx, f.JoinPath("Tiltfile"), matchMap("baz")) if assert.Error(t, err) { assert.Equal(t, `You specified some resources that could not be found: "baz" Is this a typo? Existing resources in Tiltfile: "foo", "bar"`, err.Error()) @@ -3744,32 +3742,72 @@ set_team('jets') f.loadErrString("team_name set multiple times", "'sharks'", "'jets'") } +func TestK8SContextAcceptance(t *testing.T) { + for _, test := range []struct { + name string + contextName k8s.KubeContext + env k8s.Env + expectError bool + expectedErrorSubstrings []string + }{ + {"minikube", "minikube", k8s.EnvMinikube, false, nil}, + {"docker-for-desktop", "docker-for-desktop", k8s.EnvDockerDesktop, false, nil}, + {"kind", "KIND", k8s.EnvKIND, false, nil}, + {"gke", "gke", k8s.EnvGKE, true, []string{"'gke'", "If you're sure", "switch k8s contexts", "allow_k8s_contexts"}}, + {"allowed", "allowed-context", k8s.EnvGKE, false, nil}, + } { + t.Run(test.name, func(t *testing.T) { + f := newFixture(t) + defer f.TearDown() + + f.file("Tiltfile", ` +k8s_yaml("foo.yaml") +allow_k8s_contexts("allowed-context") +`) + f.setupFoo() + + f.k8sContext = test.contextName + f.k8sEnv = test.env + if !test.expectError { + f.load() + } else { + f.loadErrString(test.expectedErrorSubstrings...) + } + }) + } +} + type fixture struct { ctx context.Context out *bytes.Buffer t *testing.T *tempdir.TempDirFixture - kCli *k8s.FakeK8sClient + kCli *k8s.FakeK8sClient + k8sContext k8s.KubeContext + k8sEnv k8s.Env - tfl TiltfileLoader - an *analytics.MemoryAnalytics + ta *tiltanalytics.TiltAnalytics + an *analytics.MemoryAnalytics loadResult TiltfileLoadResult } -func newFixture(t *testing.T) *fixture { - out := new(bytes.Buffer) - ctx, ma, ta := testutils.ForkedCtxAndAnalyticsForTest(out) - f := tempdir.NewTempDirFixture(t) +func (f *fixture) newTiltfileLoader() TiltfileLoader { dcc := dockercompose.NewDockerComposeClient(docker.LocalEnv{}) - kCli := k8s.NewFakeK8sClient() features := feature.Defaults{ "testflag_disabled": feature.Value{Enabled: false}, "testflag_enabled": feature.Value{Enabled: true}, "obsoleteflag": feature.Value{Status: feature.Obsolete, Enabled: true}, feature.MultipleContainersPerPod: feature.Value{Enabled: false}, } - tfl := ProvideTiltfileLoader(ta, kCli, dcc, "fake-context", features) + return ProvideTiltfileLoader(f.ta, f.kCli, dcc, f.k8sContext, f.k8sEnv, features) +} + +func newFixture(t *testing.T) *fixture { + out := new(bytes.Buffer) + ctx, ma, ta := testutils.ForkedCtxAndAnalyticsForTest(out) + f := tempdir.NewTempDirFixture(t) + kCli := k8s.NewFakeK8sClient() r := &fixture{ ctx: ctx, @@ -3777,8 +3815,10 @@ func newFixture(t *testing.T) *fixture { t: t, TempDirFixture: f, an: ma, - tfl: tfl, + ta: ta, kCli: kCli, + k8sContext: "fake-context", + k8sEnv: k8s.EnvDockerDesktop, } return r } @@ -3895,7 +3935,7 @@ func (f *fixture) load(names ...string) { } func (f *fixture) loadResourceAssemblyV1(names ...string) { - tlr, err := f.tfl.Load(f.ctx, f.JoinPath("Tiltfile"), matchMap(names...)) + tlr, err := f.newTiltfileLoader().Load(f.ctx, f.JoinPath("Tiltfile"), matchMap(names...)) if err != nil { f.t.Fatal(err) } @@ -3906,7 +3946,7 @@ func (f *fixture) loadResourceAssemblyV1(names ...string) { // Load the manifests, expecting warnings. // Warnings should be asserted later with assertWarnings func (f *fixture) loadAllowWarnings(names ...string) { - tlr, err := f.tfl.Load(f.ctx, f.JoinPath("Tiltfile"), matchMap(names...)) + tlr, err := f.newTiltfileLoader().Load(f.ctx, f.JoinPath("Tiltfile"), matchMap(names...)) if err != nil { f.t.Fatal(err) } @@ -3931,7 +3971,7 @@ func (f *fixture) loadAssertWarnings(warnings ...string) { } func (f *fixture) loadErrString(msgs ...string) { - tlr, err := f.tfl.Load(f.ctx, f.JoinPath("Tiltfile"), nil) + tlr, err := f.newTiltfileLoader().Load(f.ctx, f.JoinPath("Tiltfile"), nil) if err == nil { f.t.Fatalf("expected error but got nil") }