github.com/grahambrereton-form3/tilt@v0.10.18/internal/k8s/entity_test.go (about) 1 package k8s 2 3 import ( 4 "fmt" 5 "testing" 6 7 "github.com/stretchr/testify/assert" 8 "github.com/stretchr/testify/require" 9 appsv1 "k8s.io/api/apps/v1" 10 v1 "k8s.io/api/core/v1" 11 "k8s.io/apimachinery/pkg/runtime" 12 "k8s.io/apimachinery/pkg/runtime/schema" 13 14 "github.com/windmilleng/tilt/internal/k8s/testyaml" 15 "github.com/windmilleng/tilt/internal/kustomize" 16 ) 17 18 func TestTypedPodGVK(t *testing.T) { 19 entity := NewK8sEntity(&v1.Pod{}) 20 assert.Equal(t, "", entity.GVK().Group) 21 assert.Equal(t, "v1", entity.GVK().Version) 22 assert.Equal(t, "Pod", entity.GVK().Kind) 23 } 24 25 func TestTypedDeploymentGVK(t *testing.T) { 26 entity := NewK8sEntity(&appsv1.Deployment{}) 27 assert.Equal(t, "apps", entity.GVK().Group) 28 assert.Equal(t, "v1", entity.GVK().Version) 29 assert.Equal(t, "Deployment", entity.GVK().Kind) 30 } 31 32 func TestName(t *testing.T) { 33 entities, err := ParseYAMLFromString(testyaml.BlorgBackendYAML) 34 if err != nil { 35 t.Fatal(err) 36 } 37 38 assert.Equal(t, 2, len(entities)) 39 assert.Equal(t, "devel-nick-lb-blorg-be", entities[0].Name()) 40 assert.Equal(t, "devel-nick-blorg-be", entities[1].Name()) 41 } 42 43 func TestNamespace(t *testing.T) { 44 entities, err := ParseYAMLFromString(testyaml.SyncletYAML) 45 if err != nil { 46 t.Fatal(err) 47 } 48 49 assert.Equal(t, 1, len(entities)) 50 assert.Equal(t, "kube-system", string(entities[0].Namespace())) 51 } 52 53 func TestImmutableFilter(t *testing.T) { 54 yaml := fmt.Sprintf("%s\n---\n%s\n---\n%s", testyaml.JobYAML, testyaml.SanchoYAML, testyaml.PodYAML) 55 entities, err := ParseYAMLFromString(yaml) 56 if err != nil { 57 t.Fatal(err) 58 } 59 60 immEntities := ImmutableEntities(entities) 61 if len(immEntities) != 2 { 62 t.Fatalf("Expected 2 entities, actual: %d", len(immEntities)) 63 } 64 65 if immEntities[0].GVK().Kind != "Job" { 66 t.Errorf("Expected Job entity, actual: %+v", immEntities) 67 } 68 if immEntities[1].GVK().Kind != "Pod" { 69 t.Errorf("Expected Pod entity, actual: %+v", immEntities) 70 } 71 } 72 73 func TestMutableFilter(t *testing.T) { 74 yaml := fmt.Sprintf("%s\n---\n%s", testyaml.JobYAML, testyaml.SanchoYAML) 75 entities, err := ParseYAMLFromString(yaml) 76 if err != nil { 77 t.Fatal(err) 78 } 79 80 results := MutableEntities(entities) 81 if len(results) != 1 { 82 t.Fatalf("Expected 1 entity, actual: %d", len(results)) 83 } 84 85 if results[0].GVK().Kind != "Deployment" { 86 t.Errorf("Expected Deployment entity, actual: %+v", results) 87 } 88 } 89 90 func TestLoadBalancerSpecs(t *testing.T) { 91 entities, err := ParseYAMLFromString(testyaml.BlorgBackendYAML) 92 if err != nil { 93 t.Fatal(err) 94 } 95 96 lbs := ToLoadBalancerSpecs(entities) 97 if len(lbs) != 1 { 98 t.Fatalf("Expected 1 loadbalancer, actual %d: %v", len(lbs), lbs) 99 } 100 101 if lbs[0].Name != "devel-nick-lb-blorg-be" || 102 lbs[0].Ports[0] != 8080 { 103 t.Fatalf("Unexpected loadbalancer: %+v", lbs[0]) 104 } 105 } 106 107 func TestFilter(t *testing.T) { 108 entities, err := parseYAMLFromStrings(testyaml.BlorgBackendYAML, testyaml.BlorgJobYAML) 109 if err != nil { 110 t.Fatal(err) 111 } 112 113 test := func(e K8sEntity) (bool, error) { 114 if e.GVK().Kind == "Deployment" || e.GVK().Kind == "Job" { 115 return true, nil 116 } 117 return false, nil 118 } 119 120 popped, rest, err := Filter(entities, test) 121 if err != nil { 122 t.Fatal(err) 123 } 124 expectedPopped := []K8sEntity{entities[1], entities[2]} // deployment, job 125 expectedRest := []K8sEntity{entities[0]} // service 126 assert.Equal(t, popped, expectedPopped) 127 assert.Equal(t, rest, expectedRest) 128 129 returnFalse := func(e K8sEntity) (bool, error) { return false, nil } 130 popped, rest, err = Filter(entities, returnFalse) 131 if err != nil { 132 t.Fatal(err) 133 } 134 assert.Empty(t, popped) 135 assert.Equal(t, rest, entities) 136 137 returnErr := func(e K8sEntity) (bool, error) { 138 return false, fmt.Errorf("omgwtfbbq") 139 } 140 popped, rest, err = Filter(entities, returnErr) 141 if assert.Error(t, err, "expected Filter to propagate err from test func") { 142 assert.Equal(t, err.Error(), "omgwtfbbq") 143 } 144 } 145 146 func TestHasName(t *testing.T) { 147 entities, err := parseYAMLFromStrings(testyaml.DoggosDeploymentYaml, testyaml.SnackYaml) 148 if err != nil { 149 t.Fatal(err) 150 } 151 if len(entities) != 2 { 152 t.Fatalf("expected 2 entites, got %d: %v", len(entities), entities) 153 } 154 155 doggos := entities[0] 156 assert.True(t, doggos.HasName(testyaml.DoggosName)) 157 158 snack := entities[1] 159 assert.False(t, snack.HasName(testyaml.DoggosName)) 160 } 161 162 func TestHasNamespace(t *testing.T) { 163 entities, err := parseYAMLFromStrings(testyaml.DoggosDeploymentYaml, testyaml.SnackYaml) 164 if err != nil { 165 t.Fatal(err) 166 } 167 if len(entities) != 2 { 168 t.Fatalf("expected 2 entites, got %d: %v", len(entities), entities) 169 } 170 171 doggos := entities[0] 172 assert.True(t, doggos.HasNamespace(testyaml.DoggosNamespace)) 173 174 snack := entities[1] 175 assert.False(t, snack.HasNamespace(testyaml.DoggosNamespace)) 176 } 177 178 func TestHasKind(t *testing.T) { 179 entities, err := parseYAMLFromStrings(testyaml.DoggosDeploymentYaml, testyaml.DoggosServiceYaml) 180 if err != nil { 181 t.Fatal(err) 182 } 183 if len(entities) != 2 { 184 t.Fatalf("expected 2 entites, got %d: %v", len(entities), entities) 185 } 186 187 depl := entities[0] 188 assert.True(t, depl.HasKind("deployment")) 189 assert.False(t, depl.HasKind("service")) 190 191 svc := entities[1] 192 assert.False(t, svc.HasKind("deployment")) 193 assert.True(t, svc.HasKind("service")) 194 } 195 196 func TestSortEntities(t *testing.T) { 197 for _, test := range []struct { 198 name string 199 inputKindOrder []string 200 expectedKindOrder []string 201 }{ 202 {"all explicitly sorted", 203 []string{"Deployment", "Namespace", "Service"}, 204 []string{"Namespace", "Service", "Deployment"}, 205 }, 206 {"preserve order if not explicitly sorted", 207 []string{"custom1", "custom2", "custom3"}, 208 []string{"custom1", "custom2", "custom3"}, 209 }, 210 {"preserve order if not explicitly sorted, also sort others", 211 []string{"custom1", "custom2", "Secret", "custom3", "ConfigMap"}, 212 []string{"ConfigMap", "Secret", "custom1", "custom2", "custom3"}, 213 }, 214 {"pod and job not sorted", 215 []string{"Pod", "Job", "Job", "Pod"}, 216 []string{"Pod", "Job", "Job", "Pod"}, 217 }, 218 {"preserve order if not explicitly sorted if many elements", 219 // sort.Sort started by comparing input[0] and input[6], which resulted in unexpected order. 220 // (didn't preserve order of "Job" vs. "Pod"). Make sure that doesn't happen anymore. 221 []string{"Job", "PersistentVolumeClaim", "Service", "Pod", "ConfigMap", "PersistentVolume", "StatefulSet"}, 222 []string{"PersistentVolume", "PersistentVolumeClaim", "ConfigMap", "Service", "StatefulSet", "Job", "Pod"}, 223 }, 224 } { 225 t.Run(string(test.name), func(t *testing.T) { 226 input := entitiesWithKinds(test.inputKindOrder) 227 sorted := SortedEntities(input) 228 assertKindOrder(t, test.expectedKindOrder, sorted, "sorted entities") 229 }) 230 } 231 } 232 233 func TestMutableAndImmutableEntities(t *testing.T) { 234 for _, test := range []struct { 235 name string 236 inputKindOrder []string 237 expectedMutableKindOrder []string 238 expectedImmutableKindOrder []string 239 }{ 240 {"only mutable", 241 []string{"Deployment", "Namespace", "Service"}, 242 []string{"Deployment", "Namespace", "Service"}, 243 []string{}, 244 }, 245 {"only immutable", 246 []string{"Job", "Pod"}, 247 []string{}, 248 []string{"Job", "Pod"}, 249 }, 250 {"mutable and immutable interspersed", 251 []string{"Deployment", "Job", "Namespace", "Pod", "Service"}, 252 []string{"Deployment", "Namespace", "Service"}, 253 []string{"Job", "Pod"}, 254 }, 255 {"no explicitly sorted kinds are immutable", 256 // If any kinds in the explicit sort list are also immutable, things will get weird 257 kustomize.OrderFirst, 258 kustomize.OrderFirst, 259 []string{}, 260 }, 261 } { 262 t.Run(string(test.name), func(t *testing.T) { 263 input := entitiesWithKinds(test.inputKindOrder) 264 mutable, immutable := MutableAndImmutableEntities(input) 265 assertKindOrder(t, test.expectedMutableKindOrder, mutable, "mutable entities") 266 assertKindOrder(t, test.expectedImmutableKindOrder, immutable, "immutable entities") 267 }) 268 } 269 } 270 271 func entitiesWithKinds(kinds []string) []K8sEntity { 272 entities := make([]K8sEntity, len(kinds)) 273 for i, k := range kinds { 274 entities[i] = entityWithKind(k) 275 } 276 return entities 277 } 278 279 func entityWithKind(kind string) K8sEntity { 280 return K8sEntity{ 281 Obj: fakeObject{ 282 kind: fakeKind(kind), 283 }, 284 } 285 } 286 287 type fakeObject struct { 288 kind fakeKind 289 } 290 291 func (obj fakeObject) GetObjectKind() schema.ObjectKind { return obj.kind } 292 func (obj fakeObject) DeepCopyObject() runtime.Object { return obj } 293 294 type fakeKind string 295 296 func (k fakeKind) SetGroupVersionKind(gvk schema.GroupVersionKind) { panic("unsupported") } 297 func (k fakeKind) GroupVersionKind() schema.GroupVersionKind { 298 return schema.GroupVersionKind{Kind: string(k)} 299 } 300 301 func assertKindOrder(t *testing.T, expectedKinds []string, actual []K8sEntity, msg string) { 302 require.Len(t, actual, len(expectedKinds), "len(expectedKinds) != len(actualKinds): "+msg) 303 actualKinds := make([]string, len(expectedKinds)) 304 for i, e := range actual { 305 actualKinds[i] = e.GVK().Kind 306 } 307 assert.Equal(t, expectedKinds, actualKinds, msg) 308 } 309 310 func parseYAMLFromStrings(yaml ...string) ([]K8sEntity, error) { 311 var res []K8sEntity 312 for _, s := range yaml { 313 entities, err := ParseYAMLFromString(s) 314 if err != nil { 315 return nil, err 316 } 317 res = append(res, entities...) 318 } 319 return res, nil 320 } 321 322 func mustParseYAML(t *testing.T, yaml string) []K8sEntity { 323 entities, err := ParseYAMLFromString(yaml) 324 if err != nil { 325 t.Fatalf("ERROR %v parsing k8s YAML:\n%s", err, yaml) 326 } 327 return entities 328 }