github.com/amy/helm@v2.7.2+incompatible/cmd/helm/installer/install_test.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors All rights reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package installer // import "k8s.io/helm/cmd/helm/installer" 18 19 import ( 20 "os" 21 "path/filepath" 22 "reflect" 23 "testing" 24 25 "github.com/ghodss/yaml" 26 "k8s.io/api/core/v1" 27 "k8s.io/api/extensions/v1beta1" 28 apierrors "k8s.io/apimachinery/pkg/api/errors" 29 "k8s.io/apimachinery/pkg/runtime" 30 "k8s.io/client-go/kubernetes/fake" 31 testcore "k8s.io/client-go/testing" 32 33 "k8s.io/helm/pkg/chartutil" 34 "k8s.io/helm/pkg/version" 35 ) 36 37 func TestDeploymentManifest(t *testing.T) { 38 tests := []struct { 39 name string 40 image string 41 canary bool 42 expect string 43 imagePullPolicy v1.PullPolicy 44 }{ 45 {"default", "", false, "gcr.io/kubernetes-helm/tiller:" + version.Version, "IfNotPresent"}, 46 {"canary", "example.com/tiller", true, "gcr.io/kubernetes-helm/tiller:canary", "Always"}, 47 {"custom", "example.com/tiller:latest", false, "example.com/tiller:latest", "IfNotPresent"}, 48 } 49 50 for _, tt := range tests { 51 o, err := DeploymentManifest(&Options{Namespace: v1.NamespaceDefault, ImageSpec: tt.image, UseCanary: tt.canary}) 52 if err != nil { 53 t.Fatalf("%s: error %q", tt.name, err) 54 } 55 var dep v1beta1.Deployment 56 if err := yaml.Unmarshal([]byte(o), &dep); err != nil { 57 t.Fatalf("%s: error %q", tt.name, err) 58 } 59 60 if got := dep.Spec.Template.Spec.Containers[0].Image; got != tt.expect { 61 t.Errorf("%s: expected image %q, got %q", tt.name, tt.expect, got) 62 } 63 64 if got := dep.Spec.Template.Spec.Containers[0].ImagePullPolicy; got != tt.imagePullPolicy { 65 t.Errorf("%s: expected imagePullPolicy %q, got %q", tt.name, tt.imagePullPolicy, got) 66 } 67 68 if got := dep.Spec.Template.Spec.Containers[0].Env[0].Value; got != v1.NamespaceDefault { 69 t.Errorf("%s: expected namespace %q, got %q", tt.name, v1.NamespaceDefault, got) 70 } 71 } 72 } 73 74 func TestDeploymentManifestForServiceAccount(t *testing.T) { 75 tests := []struct { 76 name string 77 image string 78 canary bool 79 expect string 80 imagePullPolicy v1.PullPolicy 81 serviceAccount string 82 }{ 83 {"withSA", "", false, "gcr.io/kubernetes-helm/tiller:latest", "IfNotPresent", "service-account"}, 84 {"withoutSA", "", false, "gcr.io/kubernetes-helm/tiller:latest", "IfNotPresent", ""}, 85 } 86 for _, tt := range tests { 87 o, err := DeploymentManifest(&Options{Namespace: v1.NamespaceDefault, ImageSpec: tt.image, UseCanary: tt.canary, ServiceAccount: tt.serviceAccount}) 88 if err != nil { 89 t.Fatalf("%s: error %q", tt.name, err) 90 } 91 92 var d v1beta1.Deployment 93 if err := yaml.Unmarshal([]byte(o), &d); err != nil { 94 t.Fatalf("%s: error %q", tt.name, err) 95 } 96 if got := d.Spec.Template.Spec.ServiceAccountName; got != tt.serviceAccount { 97 t.Errorf("%s: expected service account value %q, got %q", tt.name, tt.serviceAccount, got) 98 } 99 } 100 } 101 102 func TestDeploymentManifest_WithTLS(t *testing.T) { 103 tests := []struct { 104 opts Options 105 name string 106 enable string 107 verify string 108 }{ 109 { 110 Options{Namespace: v1.NamespaceDefault, EnableTLS: true, VerifyTLS: true}, 111 "tls enable (true), tls verify (true)", 112 "1", 113 "1", 114 }, 115 { 116 Options{Namespace: v1.NamespaceDefault, EnableTLS: true, VerifyTLS: false}, 117 "tls enable (true), tls verify (false)", 118 "1", 119 "", 120 }, 121 { 122 Options{Namespace: v1.NamespaceDefault, EnableTLS: false, VerifyTLS: true}, 123 "tls enable (false), tls verify (true)", 124 "1", 125 "1", 126 }, 127 } 128 for _, tt := range tests { 129 o, err := DeploymentManifest(&tt.opts) 130 if err != nil { 131 t.Fatalf("%s: error %q", tt.name, err) 132 } 133 134 var d v1beta1.Deployment 135 if err := yaml.Unmarshal([]byte(o), &d); err != nil { 136 t.Fatalf("%s: error %q", tt.name, err) 137 } 138 // verify environment variable in deployment reflect the use of tls being enabled. 139 if got := d.Spec.Template.Spec.Containers[0].Env[2].Value; got != tt.verify { 140 t.Errorf("%s: expected tls verify env value %q, got %q", tt.name, tt.verify, got) 141 } 142 if got := d.Spec.Template.Spec.Containers[0].Env[3].Value; got != tt.enable { 143 t.Errorf("%s: expected tls enable env value %q, got %q", tt.name, tt.enable, got) 144 } 145 } 146 } 147 148 func TestServiceManifest(t *testing.T) { 149 o, err := ServiceManifest(v1.NamespaceDefault) 150 if err != nil { 151 t.Fatalf("error %q", err) 152 } 153 var svc v1.Service 154 if err := yaml.Unmarshal([]byte(o), &svc); err != nil { 155 t.Fatalf("error %q", err) 156 } 157 158 if got := svc.ObjectMeta.Namespace; got != v1.NamespaceDefault { 159 t.Errorf("expected namespace %s, got %s", v1.NamespaceDefault, got) 160 } 161 } 162 163 func TestSecretManifest(t *testing.T) { 164 o, err := SecretManifest(&Options{ 165 VerifyTLS: true, 166 EnableTLS: true, 167 Namespace: v1.NamespaceDefault, 168 TLSKeyFile: tlsTestFile(t, "key.pem"), 169 TLSCertFile: tlsTestFile(t, "crt.pem"), 170 TLSCaCertFile: tlsTestFile(t, "ca.pem"), 171 }) 172 173 if err != nil { 174 t.Fatalf("error %q", err) 175 } 176 177 var obj v1.Secret 178 if err := yaml.Unmarshal([]byte(o), &obj); err != nil { 179 t.Fatalf("error %q", err) 180 } 181 182 if got := obj.ObjectMeta.Namespace; got != v1.NamespaceDefault { 183 t.Errorf("expected namespace %s, got %s", v1.NamespaceDefault, got) 184 } 185 if _, ok := obj.Data["tls.key"]; !ok { 186 t.Errorf("missing 'tls.key' in generated secret object") 187 } 188 if _, ok := obj.Data["tls.crt"]; !ok { 189 t.Errorf("missing 'tls.crt' in generated secret object") 190 } 191 if _, ok := obj.Data["ca.crt"]; !ok { 192 t.Errorf("missing 'ca.crt' in generated secret object") 193 } 194 } 195 196 func TestInstall(t *testing.T) { 197 image := "gcr.io/kubernetes-helm/tiller:v2.0.0" 198 199 fc := &fake.Clientset{} 200 fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { 201 obj := action.(testcore.CreateAction).GetObject().(*v1beta1.Deployment) 202 l := obj.GetLabels() 203 if reflect.DeepEqual(l, map[string]string{"app": "helm"}) { 204 t.Errorf("expected labels = '', got '%s'", l) 205 } 206 i := obj.Spec.Template.Spec.Containers[0].Image 207 if i != image { 208 t.Errorf("expected image = '%s', got '%s'", image, i) 209 } 210 return true, obj, nil 211 }) 212 fc.AddReactor("create", "services", func(action testcore.Action) (bool, runtime.Object, error) { 213 obj := action.(testcore.CreateAction).GetObject().(*v1.Service) 214 l := obj.GetLabels() 215 if reflect.DeepEqual(l, map[string]string{"app": "helm"}) { 216 t.Errorf("expected labels = '', got '%s'", l) 217 } 218 n := obj.ObjectMeta.Namespace 219 if n != v1.NamespaceDefault { 220 t.Errorf("expected namespace = '%s', got '%s'", v1.NamespaceDefault, n) 221 } 222 return true, obj, nil 223 }) 224 225 opts := &Options{Namespace: v1.NamespaceDefault, ImageSpec: image} 226 if err := Install(fc, opts); err != nil { 227 t.Errorf("unexpected error: %#+v", err) 228 } 229 230 if actions := fc.Actions(); len(actions) != 2 { 231 t.Errorf("unexpected actions: %v, expected 2 actions got %d", actions, len(actions)) 232 } 233 } 234 235 func TestInstall_WithTLS(t *testing.T) { 236 image := "gcr.io/kubernetes-helm/tiller:v2.0.0" 237 name := "tiller-secret" 238 239 fc := &fake.Clientset{} 240 fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { 241 obj := action.(testcore.CreateAction).GetObject().(*v1beta1.Deployment) 242 l := obj.GetLabels() 243 if reflect.DeepEqual(l, map[string]string{"app": "helm"}) { 244 t.Errorf("expected labels = '', got '%s'", l) 245 } 246 i := obj.Spec.Template.Spec.Containers[0].Image 247 if i != image { 248 t.Errorf("expected image = '%s', got '%s'", image, i) 249 } 250 return true, obj, nil 251 }) 252 fc.AddReactor("create", "services", func(action testcore.Action) (bool, runtime.Object, error) { 253 obj := action.(testcore.CreateAction).GetObject().(*v1.Service) 254 l := obj.GetLabels() 255 if reflect.DeepEqual(l, map[string]string{"app": "helm"}) { 256 t.Errorf("expected labels = '', got '%s'", l) 257 } 258 n := obj.ObjectMeta.Namespace 259 if n != v1.NamespaceDefault { 260 t.Errorf("expected namespace = '%s', got '%s'", v1.NamespaceDefault, n) 261 } 262 return true, obj, nil 263 }) 264 fc.AddReactor("create", "secrets", func(action testcore.Action) (bool, runtime.Object, error) { 265 obj := action.(testcore.CreateAction).GetObject().(*v1.Secret) 266 if l := obj.GetLabels(); reflect.DeepEqual(l, map[string]string{"app": "helm"}) { 267 t.Errorf("expected labels = '', got '%s'", l) 268 } 269 if n := obj.ObjectMeta.Namespace; n != v1.NamespaceDefault { 270 t.Errorf("expected namespace = '%s', got '%s'", v1.NamespaceDefault, n) 271 } 272 if s := obj.ObjectMeta.Name; s != name { 273 t.Errorf("expected name = '%s', got '%s'", name, s) 274 } 275 if _, ok := obj.Data["tls.key"]; !ok { 276 t.Errorf("missing 'tls.key' in generated secret object") 277 } 278 if _, ok := obj.Data["tls.crt"]; !ok { 279 t.Errorf("missing 'tls.crt' in generated secret object") 280 } 281 if _, ok := obj.Data["ca.crt"]; !ok { 282 t.Errorf("missing 'ca.crt' in generated secret object") 283 } 284 return true, obj, nil 285 }) 286 287 opts := &Options{ 288 Namespace: v1.NamespaceDefault, 289 ImageSpec: image, 290 EnableTLS: true, 291 VerifyTLS: true, 292 TLSKeyFile: tlsTestFile(t, "key.pem"), 293 TLSCertFile: tlsTestFile(t, "crt.pem"), 294 TLSCaCertFile: tlsTestFile(t, "ca.pem"), 295 } 296 297 if err := Install(fc, opts); err != nil { 298 t.Errorf("unexpected error: %#+v", err) 299 } 300 301 if actions := fc.Actions(); len(actions) != 3 { 302 t.Errorf("unexpected actions: %v, expected 3 actions got %d", actions, len(actions)) 303 } 304 } 305 306 func TestInstall_canary(t *testing.T) { 307 fc := &fake.Clientset{} 308 fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { 309 obj := action.(testcore.CreateAction).GetObject().(*v1beta1.Deployment) 310 i := obj.Spec.Template.Spec.Containers[0].Image 311 if i != "gcr.io/kubernetes-helm/tiller:canary" { 312 t.Errorf("expected canary image, got '%s'", i) 313 } 314 return true, obj, nil 315 }) 316 fc.AddReactor("create", "services", func(action testcore.Action) (bool, runtime.Object, error) { 317 obj := action.(testcore.CreateAction).GetObject().(*v1.Service) 318 return true, obj, nil 319 }) 320 321 opts := &Options{Namespace: v1.NamespaceDefault, UseCanary: true} 322 if err := Install(fc, opts); err != nil { 323 t.Errorf("unexpected error: %#+v", err) 324 } 325 326 if actions := fc.Actions(); len(actions) != 2 { 327 t.Errorf("unexpected actions: %v, expected 2 actions got %d", actions, len(actions)) 328 } 329 } 330 331 func TestUpgrade(t *testing.T) { 332 image := "gcr.io/kubernetes-helm/tiller:v2.0.0" 333 serviceAccount := "newServiceAccount" 334 existingDeployment, _ := deployment(&Options{ 335 Namespace: v1.NamespaceDefault, 336 ImageSpec: "imageToReplace", 337 ServiceAccount: "serviceAccountToReplace", 338 UseCanary: false, 339 }) 340 existingService := service(v1.NamespaceDefault) 341 342 fc := &fake.Clientset{} 343 fc.AddReactor("get", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { 344 return true, existingDeployment, nil 345 }) 346 fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { 347 obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment) 348 i := obj.Spec.Template.Spec.Containers[0].Image 349 if i != image { 350 t.Errorf("expected image = '%s', got '%s'", image, i) 351 } 352 sa := obj.Spec.Template.Spec.ServiceAccountName 353 if sa != serviceAccount { 354 t.Errorf("expected serviceAccountName = '%s', got '%s'", serviceAccount, sa) 355 } 356 return true, obj, nil 357 }) 358 fc.AddReactor("get", "services", func(action testcore.Action) (bool, runtime.Object, error) { 359 return true, existingService, nil 360 }) 361 362 opts := &Options{Namespace: v1.NamespaceDefault, ImageSpec: image, ServiceAccount: serviceAccount} 363 if err := Upgrade(fc, opts); err != nil { 364 t.Errorf("unexpected error: %#+v", err) 365 } 366 367 if actions := fc.Actions(); len(actions) != 3 { 368 t.Errorf("unexpected actions: %v, expected 3 actions got %d", actions, len(actions)) 369 } 370 } 371 372 func TestUpgrade_serviceNotFound(t *testing.T) { 373 image := "gcr.io/kubernetes-helm/tiller:v2.0.0" 374 375 existingDeployment, _ := deployment(&Options{ 376 Namespace: v1.NamespaceDefault, 377 ImageSpec: "imageToReplace", 378 UseCanary: false, 379 }) 380 381 fc := &fake.Clientset{} 382 fc.AddReactor("get", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { 383 return true, existingDeployment, nil 384 }) 385 fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { 386 obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment) 387 i := obj.Spec.Template.Spec.Containers[0].Image 388 if i != image { 389 t.Errorf("expected image = '%s', got '%s'", image, i) 390 } 391 return true, obj, nil 392 }) 393 fc.AddReactor("get", "services", func(action testcore.Action) (bool, runtime.Object, error) { 394 return true, nil, apierrors.NewNotFound(v1.Resource("services"), "1") 395 }) 396 fc.AddReactor("create", "services", func(action testcore.Action) (bool, runtime.Object, error) { 397 obj := action.(testcore.CreateAction).GetObject().(*v1.Service) 398 n := obj.ObjectMeta.Namespace 399 if n != v1.NamespaceDefault { 400 t.Errorf("expected namespace = '%s', got '%s'", v1.NamespaceDefault, n) 401 } 402 return true, obj, nil 403 }) 404 405 opts := &Options{Namespace: v1.NamespaceDefault, ImageSpec: image} 406 if err := Upgrade(fc, opts); err != nil { 407 t.Errorf("unexpected error: %#+v", err) 408 } 409 410 if actions := fc.Actions(); len(actions) != 4 { 411 t.Errorf("unexpected actions: %v, expected 4 actions got %d", actions, len(actions)) 412 } 413 } 414 415 func tlsTestFile(t *testing.T, path string) string { 416 const tlsTestDir = "../../../testdata" 417 path = filepath.Join(tlsTestDir, path) 418 if _, err := os.Stat(path); os.IsNotExist(err) { 419 t.Fatalf("tls test file %s does not exist", path) 420 } 421 return path 422 } 423 func TestDeploymentManifest_WithNodeSelectors(t *testing.T) { 424 tests := []struct { 425 opts Options 426 name string 427 expect map[string]interface{} 428 }{ 429 { 430 Options{Namespace: v1.NamespaceDefault, NodeSelectors: "app=tiller"}, 431 "nodeSelector app=tiller", 432 map[string]interface{}{"app": "tiller"}, 433 }, 434 { 435 Options{Namespace: v1.NamespaceDefault, NodeSelectors: "app=tiller,helm=rocks"}, 436 "nodeSelector app=tiller, helm=rocks", 437 map[string]interface{}{"app": "tiller", "helm": "rocks"}, 438 }, 439 // note: nodeSelector key and value are strings 440 { 441 Options{Namespace: v1.NamespaceDefault, NodeSelectors: "app=tiller,minCoolness=1"}, 442 "nodeSelector app=tiller, helm=rocks", 443 map[string]interface{}{"app": "tiller", "minCoolness": "1"}, 444 }, 445 } 446 for _, tt := range tests { 447 o, err := DeploymentManifest(&tt.opts) 448 if err != nil { 449 t.Fatalf("%s: error %q", tt.name, err) 450 } 451 452 var d v1beta1.Deployment 453 if err := yaml.Unmarshal([]byte(o), &d); err != nil { 454 t.Fatalf("%s: error %q", tt.name, err) 455 } 456 // Verify that environment variables in Deployment reflect the use of TLS being enabled. 457 got := d.Spec.Template.Spec.NodeSelector 458 for k, v := range tt.expect { 459 if got[k] != v { 460 t.Errorf("%s: expected nodeSelector value %q, got %q", tt.name, tt.expect, got) 461 } 462 } 463 } 464 } 465 func TestDeploymentManifest_WithSetValues(t *testing.T) { 466 tests := []struct { 467 opts Options 468 name string 469 expectPath string 470 expect interface{} 471 }{ 472 { 473 Options{Namespace: v1.NamespaceDefault, Values: []string{"spec.template.spec.nodeselector.app=tiller"}}, 474 "setValues spec.template.spec.nodeSelector.app=tiller", 475 "spec.template.spec.nodeSelector.app", 476 "tiller", 477 }, 478 { 479 Options{Namespace: v1.NamespaceDefault, Values: []string{"spec.replicas=2"}}, 480 "setValues spec.replicas=2", 481 "spec.replicas", 482 2, 483 }, 484 { 485 Options{Namespace: v1.NamespaceDefault, Values: []string{"spec.template.spec.activedeadlineseconds=120"}}, 486 "setValues spec.template.spec.activedeadlineseconds=120", 487 "spec.template.spec.activeDeadlineSeconds", 488 120, 489 }, 490 } 491 for _, tt := range tests { 492 o, err := DeploymentManifest(&tt.opts) 493 if err != nil { 494 t.Fatalf("%s: error %q", tt.name, err) 495 } 496 values, err := chartutil.ReadValues([]byte(o)) 497 if err != nil { 498 t.Errorf("Error converting Deployment manifest to Values: %s", err) 499 } 500 // path value 501 pv, err := values.PathValue(tt.expectPath) 502 if err != nil { 503 t.Errorf("Error retrieving path value from Deployment Values: %s", err) 504 } 505 506 // convert our expected value to match the result type for comparison 507 ev := tt.expect 508 switch pvt := pv.(type) { 509 case float64: 510 floatType := reflect.TypeOf(float64(0)) 511 v := reflect.ValueOf(ev) 512 v = reflect.Indirect(v) 513 if !v.Type().ConvertibleTo(floatType) { 514 t.Fatalf("Error converting expected value %v to float64", v.Type()) 515 } 516 fv := v.Convert(floatType) 517 if fv.Float() != pvt { 518 t.Errorf("%s: expected value %q, got %q", tt.name, tt.expect, pv) 519 } 520 default: 521 if pv != tt.expect { 522 t.Errorf("%s: expected value %q, got %q", tt.name, tt.expect, pv) 523 } 524 } 525 } 526 }