github.com/tilt-dev/tilt@v0.36.0/internal/k8s/label_test.go (about) 1 package k8s 2 3 import ( 4 "testing" 5 6 extbeta1 "k8s.io/api/extensions/v1beta1" 7 "k8s.io/apimachinery/pkg/api/meta" 8 9 "k8s.io/api/apps/v1beta1" 10 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 appsv1 "k8s.io/api/apps/v1" 14 "k8s.io/api/apps/v1beta2" 15 v1 "k8s.io/api/core/v1" 16 17 "github.com/tilt-dev/tilt/internal/k8s/testyaml" 18 "github.com/tilt-dev/tilt/pkg/model" 19 ) 20 21 type field struct { 22 name string 23 m map[string]string 24 } 25 26 func verifyFields(t *testing.T, expected []model.LabelPair, fields []field) { 27 em := make(map[string]string) 28 for _, l := range expected { 29 em[l.Key] = l.Value 30 } 31 32 for _, f := range fields { 33 require.Equal(t, em, f.m, f.name) 34 } 35 } 36 37 func TestInjectLabelPod(t *testing.T) { 38 entity := parseOneEntity(t, testyaml.LonelyPodYAML) 39 lps := []model.LabelPair{ 40 { 41 Key: "tier", 42 Value: "test", 43 }, 44 } 45 newEntity, err := InjectLabels(entity, lps) 46 if err != nil { 47 t.Fatal(err) 48 } 49 50 p, ok := newEntity.Obj.(*v1.Pod) 51 require.True(t, ok) 52 53 verifyFields(t, lps, []field{{"pod.Labels", p.Labels}}) 54 } 55 56 func TestInjectLabelDeployment(t *testing.T) { 57 entity := parseOneEntity(t, testyaml.SanchoYAML) 58 lps := []model.LabelPair{ 59 {Key: "tier", Value: "test"}, 60 {Key: "owner", Value: "me"}, 61 } 62 newEntity, err := InjectLabels(entity, lps) 63 if err != nil { 64 t.Fatal(err) 65 } 66 67 d, ok := newEntity.Obj.(*appsv1.Deployment) 68 require.True(t, ok) 69 70 appLP := model.LabelPair{Key: "app", Value: "sancho"} 71 expectedLPs := append(lps, appLP) 72 73 verifyFields(t, expectedLPs, []field{ 74 {"d.Labels", d.Labels}, 75 {"d.Spec.Template.Labels", d.Spec.Template.Labels}, 76 }) 77 // matchlabels is not updated 78 verifyFields(t, []model.LabelPair{appLP}, []field{ 79 {"d.Spec.Selector.MatchLabels", d.Spec.Selector.MatchLabels}, 80 }) 81 } 82 83 func TestInjectLabelDeploymentMakeSelectorMatchOnConflict(t *testing.T) { 84 entity := parseOneEntity(t, testyaml.SanchoYAML) 85 lps := []model.LabelPair{ 86 { 87 Key: "app", 88 Value: "panza", 89 }, 90 } 91 newEntity, err := InjectLabels(entity, lps) 92 if err != nil { 93 t.Fatal(err) 94 } 95 96 d, ok := newEntity.Obj.(*appsv1.Deployment) 97 require.True(t, ok) 98 99 verifyFields(t, lps, []field{ 100 {"d.Labels", d.Labels}, 101 {"d.Spec.Template.Labels", d.Spec.Template.Labels}, 102 }) 103 // matchlabels only gets its existing 'app' label updated, it doesn't get any new labels added 104 verifyFields(t, []model.LabelPair{{Key: "app", Value: "panza"}}, []field{ 105 {"d.Spec.Selector.MatchLabels", d.Spec.Selector.MatchLabels}, 106 }) 107 } 108 109 func TestInjectLabelDeploymentBeta1(t *testing.T) { 110 entity := parseOneEntity(t, testyaml.SanchoBeta1YAML) 111 lps := []model.LabelPair{ 112 { 113 Key: "owner", 114 Value: "me", 115 }, 116 } 117 newEntity, err := InjectLabels(entity, lps) 118 if err != nil { 119 t.Fatal(err) 120 } 121 122 d, ok := newEntity.Obj.(*v1beta1.Deployment) 123 require.True(t, ok) 124 125 expectedLPs := append(lps, model.LabelPair{Key: "app", Value: "sancho"}) 126 127 verifyFields(t, expectedLPs, []field{ 128 {"d.Labels", d.Labels}, 129 {"d.Spec.Template.Labels", d.Spec.Template.Labels}, 130 {"d.Spec.Selector.MatchLabels", d.Spec.Selector.MatchLabels}, 131 }) 132 } 133 134 func TestInjectLabelStatefulSetBeta1(t *testing.T) { 135 entity := parseOneEntity(t, testyaml.SanchoStatefulSetBeta1YAML) 136 lps := []model.LabelPair{ 137 { 138 Key: "owner", 139 Value: "me", 140 }, 141 } 142 newEntity, err := InjectLabels(entity, lps) 143 if err != nil { 144 t.Fatal(err) 145 } 146 147 d, ok := newEntity.Obj.(*v1beta1.StatefulSet) 148 require.True(t, ok) 149 150 expectedLPs := append(lps, model.LabelPair{Key: "app", Value: "sancho"}) 151 152 verifyFields(t, expectedLPs, []field{ 153 {"d.Labels", d.Labels}, 154 {"d.Spec.Template.Labels", d.Spec.Template.Labels}, 155 {"d.Spec.Selector.MatchLabels", d.Spec.Selector.MatchLabels}, 156 }) 157 } 158 159 func TestInjectLabelDeploymentBeta2(t *testing.T) { 160 entity := parseOneEntity(t, testyaml.SanchoBeta2YAML) 161 lps := []model.LabelPair{ 162 { 163 Key: "owner", 164 Value: "me", 165 }, 166 } 167 newEntity, err := InjectLabels(entity, lps) 168 if err != nil { 169 t.Fatal(err) 170 } 171 172 d, ok := newEntity.Obj.(*v1beta2.Deployment) 173 require.True(t, ok) 174 175 expectedLPs := append(lps, model.LabelPair{Key: "app", Value: "sancho"}) 176 177 verifyFields(t, expectedLPs, []field{ 178 {"d.Labels", d.Labels}, 179 {"d.Spec.Template.Labels", d.Spec.Template.Labels}, 180 {"d.Spec.Selector.MatchLabels", d.Spec.Selector.MatchLabels}, 181 }) 182 } 183 184 func TestInjectLabelExtDeploymentBeta1(t *testing.T) { 185 entity := parseOneEntity(t, testyaml.SanchoExtBeta1YAML) 186 lps := []model.LabelPair{ 187 { 188 Key: "owner", 189 Value: "me", 190 }, 191 } 192 newEntity, err := InjectLabels(entity, lps) 193 if err != nil { 194 t.Fatal(err) 195 } 196 197 d, ok := newEntity.Obj.(*extbeta1.Deployment) 198 require.True(t, ok) 199 200 expectedLPs := append(lps, model.LabelPair{Key: "app", Value: "sancho"}) 201 202 verifyFields(t, expectedLPs, []field{ 203 {"d.Labels", d.Labels}, 204 {"d.Spec.Template.Labels", d.Spec.Template.Labels}, 205 {"d.Spec.Selector.MatchLabels", d.Spec.Selector.MatchLabels}, 206 }) 207 } 208 209 func TestInjectStatefulSet(t *testing.T) { 210 entity := parseOneEntity(t, testyaml.RedisStatefulSetYAML) 211 lps := []model.LabelPair{ 212 { 213 Key: "tilt-runid", 214 Value: "deadbeef", 215 }, 216 } 217 newEntity, err := InjectLabels(entity, lps) 218 if err != nil { 219 t.Fatal(err) 220 } 221 222 expectedLPs := append(lps, []model.LabelPair{ 223 {Key: "app", Value: "redis"}, 224 {Key: "chart", Value: "redis-5.1.3"}, 225 {Key: "release", Value: "test"}, 226 }...) 227 228 ss := newEntity.Obj.(*v1beta2.StatefulSet) 229 verifyFields(t, append(expectedLPs, model.LabelPair{Key: "heritage", Value: "Tiller"}), []field{ 230 {"ss.Labels", ss.Labels}, 231 }) 232 verifyFields(t, append(expectedLPs, model.LabelPair{Key: "role", Value: "master"}), []field{ 233 {"ss.Spec.Template.Labels", ss.Spec.Template.Labels}, 234 }) 235 verifyFields(t, 236 []model.LabelPair{ 237 {Key: "app", Value: "redis"}, 238 {Key: "release", Value: "test"}, 239 {Key: "role", Value: "master"}, 240 }, []field{ 241 {"ss.Spec.Selector.MatchLabels", ss.Spec.Selector.MatchLabels}, 242 }) 243 244 verifyFields(t, 245 []model.LabelPair{ 246 {Key: "app", Value: "redis"}, 247 {Key: "component", Value: "master"}, 248 {Key: "heritage", Value: "Tiller"}, 249 {Key: "release", Value: "test"}, 250 }, []field{ 251 {"ss.Spec.VolumeClaimTemplates[0].ObjectMeta.Labels", ss.Spec.VolumeClaimTemplates[0].ObjectMeta.Labels}, 252 }) 253 } 254 255 func TestInjectService(t *testing.T) { 256 entity := parseOneEntity(t, testyaml.DoggosServiceYaml) 257 lps := []model.LabelPair{ 258 {Key: "foo", Value: "bar"}, 259 {Key: "app", Value: "cattos"}, 260 } 261 newEntity, err := InjectLabels(entity, lps) 262 require.NoError(t, err) 263 264 svc, ok := newEntity.Obj.(*v1.Service) 265 require.True(t, ok) 266 267 expectedLPs := append(lps, model.LabelPair{Key: "whosAGoodBoy", Value: "imAGoodBoy"}) 268 verifyFields(t, expectedLPs, []field{ 269 {"svc.Labels", svc.Labels}, 270 }) 271 272 // selector only gets existing labels updated 273 verifyFields(t, []model.LabelPair{{Key: "app", Value: "cattos"}}, []field{ 274 {"svc.Spec.Selector", svc.Spec.Selector}, 275 }) 276 } 277 278 func TestInjectLabelAPIService(t *testing.T) { 279 entity := parseOneEntity(t, testyaml.APIServiceYAML) 280 lps := []model.LabelPair{ 281 { 282 Key: "tier", 283 Value: "test", 284 }, 285 } 286 newEntity, err := InjectLabels(entity, lps) 287 if err != nil { 288 t.Fatal(err) 289 } 290 291 meta, err := meta.Accessor(newEntity.Obj) 292 require.NoError(t, err) 293 labels := meta.GetLabels() 294 assert.Equal(t, map[string]string{ 295 "app.kubernetes.io/instance": "metrics-server", 296 "app.kubernetes.io/managed-by": "Helm", 297 "app.kubernetes.io/name": "metrics-server", 298 "app.kubernetes.io/version": "0.7.1", 299 "helm.sh/chart": "metrics-server-3.12.1", 300 "tier": "test", 301 }, labels) 302 } 303 304 func TestSelectorMatchesLabels(t *testing.T) { 305 entities, err := ParseYAMLFromString(testyaml.BlorgBackendYAML) 306 if err != nil { 307 t.Fatal(err) 308 } 309 if len(entities) != 2 { 310 t.Fatal("expected exactly two entities") 311 } 312 if entities[0].GVK().Kind != "Service" { 313 t.Fatal("expected first entity to be a Service") 314 } 315 if entities[1].GVK().Kind != "Deployment" { 316 t.Fatal("expected second entity to be a Deployment") 317 } 318 319 svc := entities[0] 320 dep := entities[1] 321 322 labels := map[string]string{ 323 "app": "blorg", 324 "owner": "nick", 325 "environment": "devel", 326 "tier": "backend", 327 "foo": "bar", // an extra label on the pod shouldn't affect the match 328 } 329 330 assert.True(t, svc.SelectorMatchesLabels(labels)) 331 332 assert.False(t, dep.SelectorMatchesLabels(labels), "kind Deployment does not support SelectorMatchesLabels") 333 334 labels["app"] = "not-blorg" 335 assert.False(t, svc.SelectorMatchesLabels(labels), "wrong value for an expected key") 336 337 delete(labels, "app") 338 assert.False(t, svc.SelectorMatchesLabels(labels), "expected key missing") 339 340 service, ok := svc.Obj.(*v1.Service) 341 require.True(t, ok, "typing svc as k8s Service") 342 service.Spec.Selector = nil 343 assert.False(t, svc.SelectorMatchesLabels(labels), "empty selector should match nothing") 344 } 345 346 func TestMatchesMetadataLabels(t *testing.T) { 347 entities, err := ParseYAMLFromString(testyaml.DoggosServiceYaml) 348 if err != nil { 349 t.Fatal(err) 350 } 351 if len(entities) != 1 { 352 t.Fatal("expected exactly two entities") 353 } 354 e := entities[0] 355 356 exactMatch := map[string]string{ 357 "app": "doggos", 358 "whosAGoodBoy": "imAGoodBoy", 359 } 360 assertMatchesMetadataLabels(t, e, exactMatch, true, "same set of labels should match") 361 362 subset := map[string]string{ 363 "app": "doggos", 364 } 365 assertMatchesMetadataLabels(t, e, subset, true, "subset of labels should match") 366 367 labelsWithExtra := map[string]string{ 368 "app": "doggos", 369 "whosAGoodBoy": "imAGoodBoy", 370 "tooManyLabels": "yep", 371 } 372 assertMatchesMetadataLabels(t, e, labelsWithExtra, false, "extra key not in metadata") 373 374 wrongValForKey := map[string]string{ 375 "app": "doggos", 376 "whosAGoodBoy": "notMeWhoops", 377 } 378 assertMatchesMetadataLabels(t, e, wrongValForKey, false, "label with wrong val for key") 379 } 380 381 func assertMatchesMetadataLabels(t *testing.T, e K8sEntity, labels map[string]string, expected bool, msg string) { 382 match, err := e.MatchesMetadataLabels(labels) 383 if err != nil { 384 t.Errorf("error checking if entity %s matches labels %v: %v", e.Name(), labels, err) 385 } 386 assert.Equal(t, expected, match, "expected entity %s matches metadata labels %v --> %t (%s)", 387 e.Name(), labels, expected, msg) 388 } 389 func parseOneEntity(t *testing.T, yaml string) K8sEntity { 390 entities, err := ParseYAMLFromString(yaml) 391 if err != nil { 392 t.Fatal(err) 393 } 394 395 if len(entities) != 1 { 396 t.Fatalf("Unexpected entities: %+v", entities) 397 } 398 return entities[0] 399 }