github.com/tilt-dev/tilt@v0.36.0/internal/controllers/core/extension/reconciler_test.go (about) 1 package extension 2 3 import ( 4 "fmt" 5 "testing" 6 7 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 "k8s.io/apimachinery/pkg/types" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 13 "github.com/tilt-dev/wmclient/pkg/analytics" 14 15 tiltanalytics "github.com/tilt-dev/tilt/internal/analytics" 16 "github.com/tilt-dev/tilt/internal/controllers/fake" 17 "github.com/tilt-dev/tilt/internal/testutils/tempdir" 18 "github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1" 19 ) 20 21 func TestDefault(t *testing.T) { 22 f := newFixture(t) 23 repo := f.setupRepo() 24 25 ext := v1alpha1.Extension{ 26 ObjectMeta: metav1.ObjectMeta{ 27 Name: "my-repo:my-ext", 28 }, 29 Spec: v1alpha1.ExtensionSpec{ 30 RepoName: "my-repo", 31 RepoPath: "my-ext", 32 Args: []string{"--namespaces=foo"}, 33 }, 34 } 35 f.Create(&ext) 36 37 f.MustGet(types.NamespacedName{Name: "my-repo:my-ext"}, &ext) 38 39 p := f.JoinPath("my-repo", "my-ext", "Tiltfile") 40 require.Equal(t, p, ext.Status.Path) 41 42 var tf v1alpha1.Tiltfile 43 f.MustGet(types.NamespacedName{Name: "my-repo:my-ext"}, &tf) 44 require.Equal(t, tf.Spec, v1alpha1.TiltfileSpec{ 45 Path: p, 46 Labels: map[string]string{"extension.my-repo_my-ext": "extension.my-repo_my-ext"}, 47 RestartOn: &v1alpha1.RestartOnSpec{FileWatches: []string{"configs:my-repo:my-ext"}}, 48 Args: []string{"--namespaces=foo"}, 49 }) 50 51 assert.Equal(t, f.ma.Counts, []analytics.CountEvent{ 52 { 53 Name: "api.extension.load", 54 Tags: map[string]string{ 55 "ext_path": "my-ext", 56 "repo_type": "file", 57 "repo_url_hash": tiltanalytics.HashSHA1(repo.Spec.URL), 58 }, 59 N: 1, 60 }, 61 }) 62 } 63 64 // Assert that the repo extension path, if specified, is used for extension location 65 func TestRepoSubpath(t *testing.T) { 66 f := newFixture(t) 67 repo := f.setupRepoSubpath("subpath") 68 69 ext := v1alpha1.Extension{ 70 ObjectMeta: metav1.ObjectMeta{ 71 Name: "my-repo:my-ext", 72 }, 73 Spec: v1alpha1.ExtensionSpec{ 74 RepoName: "my-repo", 75 RepoPath: "my-ext", 76 Args: []string{"--namespaces=foo"}, 77 }, 78 } 79 f.Create(&ext) 80 81 f.MustGet(types.NamespacedName{Name: "my-repo:my-ext"}, &ext) 82 83 p := f.JoinPath("my-repo", "subpath", "my-ext", "Tiltfile") 84 require.Equal(t, p, ext.Status.Path) 85 86 var tf v1alpha1.Tiltfile 87 f.MustGet(types.NamespacedName{Name: "my-repo:my-ext"}, &tf) 88 require.Equal(t, tf.Spec, v1alpha1.TiltfileSpec{ 89 Path: p, 90 Labels: map[string]string{"extension.my-repo_my-ext": "extension.my-repo_my-ext"}, 91 RestartOn: &v1alpha1.RestartOnSpec{FileWatches: []string{"configs:my-repo:my-ext"}}, 92 Args: []string{"--namespaces=foo"}, 93 }) 94 95 assert.Equal(t, f.ma.Counts, []analytics.CountEvent{ 96 { 97 Name: "api.extension.load", 98 Tags: map[string]string{ 99 "ext_path": "my-ext", 100 "repo_type": "file", 101 "repo_url_hash": tiltanalytics.HashSHA1(repo.Spec.URL), 102 }, 103 N: 1, 104 }, 105 }) 106 } 107 108 func TestCleanupTiltfile(t *testing.T) { 109 f := newFixture(t) 110 f.setupRepo() 111 112 ext := v1alpha1.Extension{ 113 ObjectMeta: metav1.ObjectMeta{ 114 Name: "my-repo:my-ext", 115 }, 116 Spec: v1alpha1.ExtensionSpec{ 117 RepoName: "my-repo", 118 RepoPath: "my-ext", 119 }, 120 } 121 f.Create(&ext) 122 123 f.MustGet(types.NamespacedName{Name: "my-repo:my-ext"}, &ext) 124 125 p := f.JoinPath("my-repo", "my-ext", "Tiltfile") 126 127 var tf v1alpha1.Tiltfile 128 f.MustGet(types.NamespacedName{Name: "my-repo:my-ext"}, &tf) 129 require.Equal(t, p, tf.Spec.Path) 130 131 f.Delete(&ext) 132 assert.False(t, f.Get(types.NamespacedName{Name: "my-repo:my-ext"}, &tf)) 133 } 134 135 func TestMissing(t *testing.T) { 136 f := newFixture(t) 137 f.setupRepo() 138 139 ext := v1alpha1.Extension{ 140 ObjectMeta: metav1.ObjectMeta{ 141 Name: "my-repo:my-ext", 142 }, 143 Spec: v1alpha1.ExtensionSpec{ 144 RepoName: "my-repo", 145 RepoPath: "my-ext2", 146 }, 147 } 148 f.Create(&ext) 149 150 f.MustGet(types.NamespacedName{Name: "my-repo:my-ext"}, &ext) 151 p := f.JoinPath("my-repo", "my-ext2", "Tiltfile") 152 require.Equal(t, ext.Status.Error, 153 fmt.Sprintf("no extension tiltfile found at %s", p), ext.Status.Error) 154 } 155 156 // Verify that args are propagated to the tiltfile when changed. 157 func TestChangeArgs(t *testing.T) { 158 f := newFixture(t) 159 f.setupRepo() 160 161 ext := v1alpha1.Extension{ 162 ObjectMeta: metav1.ObjectMeta{ 163 Name: "ext", 164 }, 165 Spec: v1alpha1.ExtensionSpec{ 166 RepoName: "my-repo", 167 RepoPath: "my-ext", 168 Args: []string{"--namespace=bar"}, 169 }, 170 } 171 f.Create(&ext) 172 173 f.MustGet(types.NamespacedName{Name: "ext"}, &ext) 174 175 ext.Spec.Args = []string{"--namespace=foo"} 176 f.Update(&ext) 177 178 var tf v1alpha1.Tiltfile 179 f.MustGet(types.NamespacedName{Name: "ext"}, &tf) 180 require.Equal(t, []string{"--namespace=foo"}, tf.Spec.Args) 181 } 182 183 // Verify that no errors get printed if the extension 184 // appears in the apiserver before the repo appears. 185 func TestExtensionBeforeRepo(t *testing.T) { 186 f := newFixture(t) 187 188 nn := types.NamespacedName{Name: "ext"} 189 ext := v1alpha1.Extension{ 190 ObjectMeta: metav1.ObjectMeta{ 191 Name: "ext", 192 }, 193 Spec: v1alpha1.ExtensionSpec{ 194 RepoName: "my-repo", 195 RepoPath: "my-ext", 196 }, 197 } 198 f.Create(&ext) 199 200 f.MustGet(nn, &ext) 201 assert.Equal(t, "extension repo not found: my-repo", ext.Status.Error) 202 assert.Equal(t, "", f.Stdout()) 203 204 f.setupRepo() 205 f.MustReconcile(nn) 206 f.MustGet(nn, &ext) 207 assert.Equal(t, "", ext.Status.Error) 208 assert.Equal(t, f.JoinPath("my-repo", "my-ext", "Tiltfile"), ext.Status.Path) 209 assert.Equal(t, "", f.Stdout()) 210 } 211 212 type fixture struct { 213 *fake.ControllerFixture 214 *tempdir.TempDirFixture 215 r *Reconciler 216 ma *analytics.MemoryAnalytics 217 } 218 219 func newFixture(t *testing.T) *fixture { 220 cfb := fake.NewControllerFixtureBuilder(t) 221 tf := tempdir.NewTempDirFixture(t) 222 223 o := tiltanalytics.NewFakeOpter(analytics.OptIn) 224 ma, ta := tiltanalytics.NewMemoryTiltAnalyticsForTest(o) 225 226 r := NewReconciler(cfb.Client, v1alpha1.NewScheme(), ta) 227 return &fixture{ 228 ControllerFixture: cfb.Build(r), 229 TempDirFixture: tf, 230 r: r, 231 ma: ma, 232 } 233 } 234 235 func (f *fixture) setupRepo() *v1alpha1.ExtensionRepo { 236 p := f.JoinPath("my-repo", "my-ext", "Tiltfile") 237 f.WriteFile(p, "print('hello-world')") 238 239 repo := v1alpha1.ExtensionRepo{ 240 ObjectMeta: metav1.ObjectMeta{ 241 Name: "my-repo", 242 }, 243 Spec: v1alpha1.ExtensionRepoSpec{ 244 URL: fmt.Sprintf("file://%s", f.JoinPath("my-repo")), 245 }, 246 Status: v1alpha1.ExtensionRepoStatus{ 247 Path: f.JoinPath("my-repo"), 248 }, 249 } 250 f.Create(&repo) 251 return &repo 252 } 253 254 func (f *fixture) setupRepoSubpath(subpath string) *v1alpha1.ExtensionRepo { 255 p := f.JoinPath("my-repo", subpath, "my-ext", "Tiltfile") 256 f.WriteFile(p, "print('hello-world')") 257 258 repo := v1alpha1.ExtensionRepo{ 259 ObjectMeta: metav1.ObjectMeta{ 260 Name: "my-repo", 261 }, 262 Spec: v1alpha1.ExtensionRepoSpec{ 263 URL: fmt.Sprintf("file://%s", f.JoinPath("my-repo")), 264 GitSubpath: subpath, 265 }, 266 Status: v1alpha1.ExtensionRepoStatus{ 267 Path: f.JoinPath("my-repo"), 268 }, 269 } 270 f.Create(&repo) 271 return &repo 272 }