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  }