github.com/pensu/helm@v2.6.1+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  	"io/ioutil"
    21  
    22  	"github.com/ghodss/yaml"
    23  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"k8s.io/apimachinery/pkg/util/intstr"
    26  	"k8s.io/client-go/kubernetes"
    27  	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
    28  	extensionsclient "k8s.io/client-go/kubernetes/typed/extensions/v1beta1"
    29  	"k8s.io/client-go/pkg/api/v1"
    30  	"k8s.io/client-go/pkg/apis/extensions/v1beta1"
    31  )
    32  
    33  // Install uses Kubernetes client to install Tiller.
    34  //
    35  // Returns an error if the command failed.
    36  func Install(client kubernetes.Interface, opts *Options) error {
    37  	if err := createDeployment(client.Extensions(), opts); err != nil {
    38  		return err
    39  	}
    40  	if err := createService(client.Core(), opts.Namespace); err != nil {
    41  		return err
    42  	}
    43  	if opts.tls() {
    44  		if err := createSecret(client.Core(), opts); err != nil {
    45  			return err
    46  		}
    47  	}
    48  	return nil
    49  }
    50  
    51  // Upgrade uses Kubernetes client to upgrade Tiller to current version.
    52  //
    53  // Returns an error if the command failed.
    54  func Upgrade(client kubernetes.Interface, opts *Options) error {
    55  	obj, err := client.Extensions().Deployments(opts.Namespace).Get(deploymentName, metav1.GetOptions{})
    56  	if err != nil {
    57  		return err
    58  	}
    59  	obj.Spec.Template.Spec.Containers[0].Image = opts.selectImage()
    60  	obj.Spec.Template.Spec.Containers[0].ImagePullPolicy = opts.pullPolicy()
    61  	obj.Spec.Template.Spec.ServiceAccountName = opts.ServiceAccount
    62  	if _, err := client.Extensions().Deployments(opts.Namespace).Update(obj); err != nil {
    63  		return err
    64  	}
    65  	// If the service does not exists that would mean we are upgrading from a Tiller version
    66  	// that didn't deploy the service, so install it.
    67  	_, err = client.Core().Services(opts.Namespace).Get(serviceName, metav1.GetOptions{})
    68  	if apierrors.IsNotFound(err) {
    69  		return createService(client.Core(), opts.Namespace)
    70  	}
    71  	return err
    72  }
    73  
    74  // createDeployment creates the Tiller Deployment resource.
    75  func createDeployment(client extensionsclient.DeploymentsGetter, opts *Options) error {
    76  	obj := deployment(opts)
    77  	_, err := client.Deployments(obj.Namespace).Create(obj)
    78  	return err
    79  }
    80  
    81  // deployment gets the deployment object that installs Tiller.
    82  func deployment(opts *Options) *v1beta1.Deployment {
    83  	return generateDeployment(opts)
    84  }
    85  
    86  // createService creates the Tiller service resource
    87  func createService(client corev1.ServicesGetter, namespace string) error {
    88  	obj := service(namespace)
    89  	_, err := client.Services(obj.Namespace).Create(obj)
    90  	return err
    91  }
    92  
    93  // service gets the service object that installs Tiller.
    94  func service(namespace string) *v1.Service {
    95  	return generateService(namespace)
    96  }
    97  
    98  // DeploymentManifest gets the manifest (as a string) that describes the Tiller Deployment
    99  // resource.
   100  func DeploymentManifest(opts *Options) (string, error) {
   101  	obj := deployment(opts)
   102  	buf, err := yaml.Marshal(obj)
   103  	return string(buf), err
   104  }
   105  
   106  // ServiceManifest gets the manifest (as a string) that describes the Tiller Service
   107  // resource.
   108  func ServiceManifest(namespace string) (string, error) {
   109  	obj := service(namespace)
   110  	buf, err := yaml.Marshal(obj)
   111  	return string(buf), err
   112  }
   113  
   114  func generateLabels(labels map[string]string) map[string]string {
   115  	labels["app"] = "helm"
   116  	return labels
   117  }
   118  
   119  func generateDeployment(opts *Options) *v1beta1.Deployment {
   120  	labels := generateLabels(map[string]string{"name": "tiller"})
   121  	d := &v1beta1.Deployment{
   122  		ObjectMeta: metav1.ObjectMeta{
   123  			Namespace: opts.Namespace,
   124  			Name:      deploymentName,
   125  			Labels:    labels,
   126  		},
   127  		Spec: v1beta1.DeploymentSpec{
   128  			Template: v1.PodTemplateSpec{
   129  				ObjectMeta: metav1.ObjectMeta{
   130  					Labels: labels,
   131  				},
   132  				Spec: v1.PodSpec{
   133  					ServiceAccountName: opts.ServiceAccount,
   134  					Containers: []v1.Container{
   135  						{
   136  							Name:            "tiller",
   137  							Image:           opts.selectImage(),
   138  							ImagePullPolicy: opts.pullPolicy(),
   139  							Ports: []v1.ContainerPort{
   140  								{ContainerPort: 44134, Name: "tiller"},
   141  							},
   142  							Env: []v1.EnvVar{
   143  								{Name: "TILLER_NAMESPACE", Value: opts.Namespace},
   144  							},
   145  							LivenessProbe: &v1.Probe{
   146  								Handler: v1.Handler{
   147  									HTTPGet: &v1.HTTPGetAction{
   148  										Path: "/liveness",
   149  										Port: intstr.FromInt(44135),
   150  									},
   151  								},
   152  								InitialDelaySeconds: 1,
   153  								TimeoutSeconds:      1,
   154  							},
   155  							ReadinessProbe: &v1.Probe{
   156  								Handler: v1.Handler{
   157  									HTTPGet: &v1.HTTPGetAction{
   158  										Path: "/readiness",
   159  										Port: intstr.FromInt(44135),
   160  									},
   161  								},
   162  								InitialDelaySeconds: 1,
   163  								TimeoutSeconds:      1,
   164  							},
   165  						},
   166  					},
   167  					HostNetwork: opts.EnableHostNetwork,
   168  					NodeSelector: map[string]string{
   169  						"beta.kubernetes.io/os": "linux",
   170  					},
   171  				},
   172  			},
   173  		},
   174  	}
   175  
   176  	if opts.tls() {
   177  		const certsDir = "/etc/certs"
   178  
   179  		var tlsVerify, tlsEnable = "", "1"
   180  		if opts.VerifyTLS {
   181  			tlsVerify = "1"
   182  		}
   183  
   184  		// Mount secret to "/etc/certs"
   185  		d.Spec.Template.Spec.Containers[0].VolumeMounts = append(d.Spec.Template.Spec.Containers[0].VolumeMounts, v1.VolumeMount{
   186  			Name:      "tiller-certs",
   187  			ReadOnly:  true,
   188  			MountPath: certsDir,
   189  		})
   190  		// Add environment variable required for enabling TLS
   191  		d.Spec.Template.Spec.Containers[0].Env = append(d.Spec.Template.Spec.Containers[0].Env, []v1.EnvVar{
   192  			{Name: "TILLER_TLS_VERIFY", Value: tlsVerify},
   193  			{Name: "TILLER_TLS_ENABLE", Value: tlsEnable},
   194  			{Name: "TILLER_TLS_CERTS", Value: certsDir},
   195  		}...)
   196  		// Add secret volume to deployment
   197  		d.Spec.Template.Spec.Volumes = append(d.Spec.Template.Spec.Volumes, v1.Volume{
   198  			Name: "tiller-certs",
   199  			VolumeSource: v1.VolumeSource{
   200  				Secret: &v1.SecretVolumeSource{
   201  					SecretName: "tiller-secret",
   202  				},
   203  			},
   204  		})
   205  	}
   206  	return d
   207  }
   208  
   209  func generateService(namespace string) *v1.Service {
   210  	labels := generateLabels(map[string]string{"name": "tiller"})
   211  	s := &v1.Service{
   212  		ObjectMeta: metav1.ObjectMeta{
   213  			Namespace: namespace,
   214  			Name:      serviceName,
   215  			Labels:    labels,
   216  		},
   217  		Spec: v1.ServiceSpec{
   218  			Type: v1.ServiceTypeClusterIP,
   219  			Ports: []v1.ServicePort{
   220  				{
   221  					Name:       "tiller",
   222  					Port:       44134,
   223  					TargetPort: intstr.FromString("tiller"),
   224  				},
   225  			},
   226  			Selector: labels,
   227  		},
   228  	}
   229  	return s
   230  }
   231  
   232  // SecretManifest gets the manifest (as a string) that describes the Tiller Secret resource.
   233  func SecretManifest(opts *Options) (string, error) {
   234  	o, err := generateSecret(opts)
   235  	if err != nil {
   236  		return "", err
   237  	}
   238  	buf, err := yaml.Marshal(o)
   239  	return string(buf), err
   240  }
   241  
   242  // createSecret creates the Tiller secret resource.
   243  func createSecret(client corev1.SecretsGetter, opts *Options) error {
   244  	o, err := generateSecret(opts)
   245  	if err != nil {
   246  		return err
   247  	}
   248  	_, err = client.Secrets(o.Namespace).Create(o)
   249  	return err
   250  }
   251  
   252  // generateSecret builds the secret object that hold Tiller secrets.
   253  func generateSecret(opts *Options) (*v1.Secret, error) {
   254  
   255  	labels := generateLabels(map[string]string{"name": "tiller"})
   256  	secret := &v1.Secret{
   257  		Type: v1.SecretTypeOpaque,
   258  		Data: make(map[string][]byte),
   259  		ObjectMeta: metav1.ObjectMeta{
   260  			Name:      secretName,
   261  			Labels:    labels,
   262  			Namespace: opts.Namespace,
   263  		},
   264  	}
   265  	var err error
   266  	if secret.Data["tls.key"], err = read(opts.TLSKeyFile); err != nil {
   267  		return nil, err
   268  	}
   269  	if secret.Data["tls.crt"], err = read(opts.TLSCertFile); err != nil {
   270  		return nil, err
   271  	}
   272  	if opts.VerifyTLS {
   273  		if secret.Data["ca.crt"], err = read(opts.TLSCaCertFile); err != nil {
   274  			return nil, err
   275  		}
   276  	}
   277  	return secret, nil
   278  }
   279  
   280  func read(path string) (b []byte, err error) { return ioutil.ReadFile(path) }