github.com/cloudposse/helm@v2.2.3+incompatible/cmd/helm/installer/install.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  	"fmt"
    21  
    22  	"github.com/ghodss/yaml"
    23  
    24  	"k8s.io/kubernetes/pkg/api"
    25  	kerrors "k8s.io/kubernetes/pkg/api/errors"
    26  	"k8s.io/kubernetes/pkg/apis/extensions"
    27  	"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
    28  	"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
    29  	extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion"
    30  	"k8s.io/kubernetes/pkg/util/intstr"
    31  
    32  	"k8s.io/helm/pkg/version"
    33  )
    34  
    35  const defaultImage = "gcr.io/kubernetes-helm/tiller"
    36  
    37  // Install uses kubernetes client to install tiller
    38  //
    39  // Returns the string output received from the operation, and an error if the
    40  // command failed.
    41  //
    42  // If verbose is true, this will print the manifest to stdout.
    43  func Install(client internalclientset.Interface, namespace, image string, canary, verbose bool) error {
    44  	if err := createDeployment(client.Extensions(), namespace, image, canary); err != nil {
    45  		return err
    46  	}
    47  	if err := createService(client.Core(), namespace); err != nil {
    48  		return err
    49  	}
    50  	return nil
    51  }
    52  
    53  //
    54  // Upgrade uses kubernetes client to upgrade tiller to current version
    55  //
    56  // Returns an error if the command failed.
    57  func Upgrade(client internalclientset.Interface, namespace, image string, canary bool) error {
    58  	obj, err := client.Extensions().Deployments(namespace).Get("tiller-deploy")
    59  	if err != nil {
    60  		return err
    61  	}
    62  	obj.Spec.Template.Spec.Containers[0].Image = selectImage(image, canary)
    63  	if _, err := client.Extensions().Deployments(namespace).Update(obj); err != nil {
    64  		return err
    65  	}
    66  	// If the service does not exists that would mean we are upgrading from a tiller version
    67  	// that didn't deploy the service, so install it.
    68  	if _, err := client.Core().Services(namespace).Get("tiller-deploy"); err != nil {
    69  		if !kerrors.IsNotFound(err) {
    70  			return err
    71  		}
    72  		if err := createService(client.Core(), namespace); err != nil {
    73  			return err
    74  		}
    75  	}
    76  	return nil
    77  }
    78  
    79  // createDeployment creates the Tiller deployment reource
    80  func createDeployment(client extensionsclient.DeploymentsGetter, namespace, image string, canary bool) error {
    81  	obj := deployment(namespace, image, canary)
    82  	_, err := client.Deployments(obj.Namespace).Create(obj)
    83  	return err
    84  }
    85  
    86  // deployment gets the deployment object that installs Tiller.
    87  func deployment(namespace, image string, canary bool) *extensions.Deployment {
    88  	return generateDeployment(namespace, selectImage(image, canary))
    89  }
    90  
    91  // createService creates the Tiller service resource
    92  func createService(client internalversion.ServicesGetter, namespace string) error {
    93  	obj := service(namespace)
    94  	_, err := client.Services(obj.Namespace).Create(obj)
    95  	return err
    96  }
    97  
    98  // service gets the service object that installs Tiller.
    99  func service(namespace string) *api.Service {
   100  	return generateService(namespace)
   101  }
   102  
   103  func selectImage(image string, canary bool) string {
   104  	switch {
   105  	case canary:
   106  		image = defaultImage + ":canary"
   107  	case image == "":
   108  		image = fmt.Sprintf("%s:%s", defaultImage, version.Version)
   109  	}
   110  	return image
   111  }
   112  
   113  // DeploymentManifest gets the manifest (as a string) that describes the Tiller Deployment
   114  // resource.
   115  func DeploymentManifest(namespace, image string, canary bool) (string, error) {
   116  	obj := deployment(namespace, image, canary)
   117  
   118  	buf, err := yaml.Marshal(obj)
   119  	return string(buf), err
   120  }
   121  
   122  // ServiceManifest gets the manifest (as a string) that describes the Tiller Service
   123  // resource.
   124  func ServiceManifest(namespace string) (string, error) {
   125  	obj := service(namespace)
   126  
   127  	buf, err := yaml.Marshal(obj)
   128  	return string(buf), err
   129  }
   130  
   131  func generateLabels(labels map[string]string) map[string]string {
   132  	labels["app"] = "helm"
   133  	return labels
   134  }
   135  
   136  func generateDeployment(namespace, image string) *extensions.Deployment {
   137  	labels := generateLabels(map[string]string{"name": "tiller"})
   138  	d := &extensions.Deployment{
   139  		ObjectMeta: api.ObjectMeta{
   140  			Namespace: namespace,
   141  			Name:      "tiller-deploy",
   142  			Labels:    labels,
   143  		},
   144  		Spec: extensions.DeploymentSpec{
   145  			Replicas: 1,
   146  			Template: api.PodTemplateSpec{
   147  				ObjectMeta: api.ObjectMeta{
   148  					Labels: labels,
   149  				},
   150  				Spec: api.PodSpec{
   151  					Containers: []api.Container{
   152  						{
   153  							Name:            "tiller",
   154  							Image:           image,
   155  							ImagePullPolicy: "IfNotPresent",
   156  							Ports: []api.ContainerPort{
   157  								{ContainerPort: 44134, Name: "tiller"},
   158  							},
   159  							Env: []api.EnvVar{
   160  								{Name: "TILLER_NAMESPACE", Value: namespace},
   161  							},
   162  							LivenessProbe: &api.Probe{
   163  								Handler: api.Handler{
   164  									HTTPGet: &api.HTTPGetAction{
   165  										Path: "/liveness",
   166  										Port: intstr.FromInt(44135),
   167  									},
   168  								},
   169  								InitialDelaySeconds: 1,
   170  								TimeoutSeconds:      1,
   171  							},
   172  							ReadinessProbe: &api.Probe{
   173  								Handler: api.Handler{
   174  									HTTPGet: &api.HTTPGetAction{
   175  										Path: "/readiness",
   176  										Port: intstr.FromInt(44135),
   177  									},
   178  								},
   179  								InitialDelaySeconds: 1,
   180  								TimeoutSeconds:      1,
   181  							},
   182  						},
   183  					},
   184  				},
   185  			},
   186  		},
   187  	}
   188  	return d
   189  }
   190  
   191  func generateService(namespace string) *api.Service {
   192  	labels := generateLabels(map[string]string{"name": "tiller"})
   193  	s := &api.Service{
   194  		ObjectMeta: api.ObjectMeta{
   195  			Namespace: namespace,
   196  			Name:      "tiller-deploy",
   197  			Labels:    labels,
   198  		},
   199  		Spec: api.ServiceSpec{
   200  			Type: api.ServiceTypeClusterIP,
   201  			Ports: []api.ServicePort{
   202  				{
   203  					Name:       "tiller",
   204  					Port:       44134,
   205  					TargetPort: intstr.FromString("tiller"),
   206  				},
   207  			},
   208  			Selector: labels,
   209  		},
   210  	}
   211  	return s
   212  }