github.com/amy/helm@v2.7.2+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  	"io/ioutil"
    22  	"strings"
    23  
    24  	"github.com/ghodss/yaml"
    25  	"k8s.io/api/core/v1"
    26  	"k8s.io/api/extensions/v1beta1"
    27  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/util/intstr"
    30  	"k8s.io/client-go/kubernetes"
    31  	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
    32  	extensionsclient "k8s.io/client-go/kubernetes/typed/extensions/v1beta1"
    33  	"k8s.io/helm/pkg/chartutil"
    34  )
    35  
    36  // Install uses Kubernetes client to install Tiller.
    37  //
    38  // Returns an error if the command failed.
    39  func Install(client kubernetes.Interface, opts *Options) error {
    40  	if err := createDeployment(client.Extensions(), opts); err != nil {
    41  		return err
    42  	}
    43  	if err := createService(client.Core(), opts.Namespace); err != nil {
    44  		return err
    45  	}
    46  	if opts.tls() {
    47  		if err := createSecret(client.Core(), opts); err != nil {
    48  			return err
    49  		}
    50  	}
    51  	return nil
    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 kubernetes.Interface, opts *Options) error {
    58  	obj, err := client.Extensions().Deployments(opts.Namespace).Get(deploymentName, metav1.GetOptions{})
    59  	if err != nil {
    60  		return err
    61  	}
    62  	obj.Spec.Template.Spec.Containers[0].Image = opts.selectImage()
    63  	obj.Spec.Template.Spec.Containers[0].ImagePullPolicy = opts.pullPolicy()
    64  	obj.Spec.Template.Spec.ServiceAccountName = opts.ServiceAccount
    65  	if _, err := client.Extensions().Deployments(opts.Namespace).Update(obj); err != nil {
    66  		return err
    67  	}
    68  	// If the service does not exists that would mean we are upgrading from a Tiller version
    69  	// that didn't deploy the service, so install it.
    70  	_, err = client.Core().Services(opts.Namespace).Get(serviceName, metav1.GetOptions{})
    71  	if apierrors.IsNotFound(err) {
    72  		return createService(client.Core(), opts.Namespace)
    73  	}
    74  	return err
    75  }
    76  
    77  // createDeployment creates the Tiller Deployment resource.
    78  func createDeployment(client extensionsclient.DeploymentsGetter, opts *Options) error {
    79  	obj, err := deployment(opts)
    80  	if err != nil {
    81  		return err
    82  	}
    83  	_, err = client.Deployments(obj.Namespace).Create(obj)
    84  	return err
    85  
    86  }
    87  
    88  // deployment gets the deployment object that installs Tiller.
    89  func deployment(opts *Options) (*v1beta1.Deployment, error) {
    90  	return generateDeployment(opts)
    91  }
    92  
    93  // createService creates the Tiller service resource
    94  func createService(client corev1.ServicesGetter, namespace string) error {
    95  	obj := service(namespace)
    96  	_, err := client.Services(obj.Namespace).Create(obj)
    97  	return err
    98  }
    99  
   100  // service gets the service object that installs Tiller.
   101  func service(namespace string) *v1.Service {
   102  	return generateService(namespace)
   103  }
   104  
   105  // DeploymentManifest gets the manifest (as a string) that describes the Tiller Deployment
   106  // resource.
   107  func DeploymentManifest(opts *Options) (string, error) {
   108  	obj, err := deployment(opts)
   109  	if err != nil {
   110  		return "", err
   111  	}
   112  	buf, err := yaml.Marshal(obj)
   113  	return string(buf), err
   114  }
   115  
   116  // ServiceManifest gets the manifest (as a string) that describes the Tiller Service
   117  // resource.
   118  func ServiceManifest(namespace string) (string, error) {
   119  	obj := service(namespace)
   120  	buf, err := yaml.Marshal(obj)
   121  	return string(buf), err
   122  }
   123  
   124  func generateLabels(labels map[string]string) map[string]string {
   125  	labels["app"] = "helm"
   126  	return labels
   127  }
   128  
   129  // parseNodeSelectors parses a comma delimited list of key=values pairs into a map.
   130  func parseNodeSelectorsInto(labels string, m map[string]string) error {
   131  	kv := strings.Split(labels, ",")
   132  	for _, v := range kv {
   133  		el := strings.Split(v, "=")
   134  		if len(el) == 2 {
   135  			m[el[0]] = el[1]
   136  		} else {
   137  			return fmt.Errorf("invalid nodeSelector label: %q", kv)
   138  		}
   139  	}
   140  	return nil
   141  }
   142  func generateDeployment(opts *Options) (*v1beta1.Deployment, error) {
   143  	labels := generateLabels(map[string]string{"name": "tiller"})
   144  	nodeSelectors := map[string]string{}
   145  	if len(opts.NodeSelectors) > 0 {
   146  		err := parseNodeSelectorsInto(opts.NodeSelectors, nodeSelectors)
   147  		if err != nil {
   148  			return nil, err
   149  		}
   150  	}
   151  	d := &v1beta1.Deployment{
   152  		ObjectMeta: metav1.ObjectMeta{
   153  			Namespace: opts.Namespace,
   154  			Name:      deploymentName,
   155  			Labels:    labels,
   156  		},
   157  		Spec: v1beta1.DeploymentSpec{
   158  			Template: v1.PodTemplateSpec{
   159  				ObjectMeta: metav1.ObjectMeta{
   160  					Labels: labels,
   161  				},
   162  				Spec: v1.PodSpec{
   163  					ServiceAccountName: opts.ServiceAccount,
   164  					Containers: []v1.Container{
   165  						{
   166  							Name:            "tiller",
   167  							Image:           opts.selectImage(),
   168  							ImagePullPolicy: opts.pullPolicy(),
   169  							Ports: []v1.ContainerPort{
   170  								{ContainerPort: 44134, Name: "tiller"},
   171  							},
   172  							Env: []v1.EnvVar{
   173  								{Name: "TILLER_NAMESPACE", Value: opts.Namespace},
   174  								{Name: "TILLER_HISTORY_MAX", Value: fmt.Sprintf("%d", opts.MaxHistory)},
   175  							},
   176  							LivenessProbe: &v1.Probe{
   177  								Handler: v1.Handler{
   178  									HTTPGet: &v1.HTTPGetAction{
   179  										Path: "/liveness",
   180  										Port: intstr.FromInt(44135),
   181  									},
   182  								},
   183  								InitialDelaySeconds: 1,
   184  								TimeoutSeconds:      1,
   185  							},
   186  							ReadinessProbe: &v1.Probe{
   187  								Handler: v1.Handler{
   188  									HTTPGet: &v1.HTTPGetAction{
   189  										Path: "/readiness",
   190  										Port: intstr.FromInt(44135),
   191  									},
   192  								},
   193  								InitialDelaySeconds: 1,
   194  								TimeoutSeconds:      1,
   195  							},
   196  						},
   197  					},
   198  					HostNetwork:  opts.EnableHostNetwork,
   199  					NodeSelector: nodeSelectors,
   200  				},
   201  			},
   202  		},
   203  	}
   204  
   205  	if opts.tls() {
   206  		const certsDir = "/etc/certs"
   207  
   208  		var tlsVerify, tlsEnable = "", "1"
   209  		if opts.VerifyTLS {
   210  			tlsVerify = "1"
   211  		}
   212  
   213  		// Mount secret to "/etc/certs"
   214  		d.Spec.Template.Spec.Containers[0].VolumeMounts = append(d.Spec.Template.Spec.Containers[0].VolumeMounts, v1.VolumeMount{
   215  			Name:      "tiller-certs",
   216  			ReadOnly:  true,
   217  			MountPath: certsDir,
   218  		})
   219  		// Add environment variable required for enabling TLS
   220  		d.Spec.Template.Spec.Containers[0].Env = append(d.Spec.Template.Spec.Containers[0].Env, []v1.EnvVar{
   221  			{Name: "TILLER_TLS_VERIFY", Value: tlsVerify},
   222  			{Name: "TILLER_TLS_ENABLE", Value: tlsEnable},
   223  			{Name: "TILLER_TLS_CERTS", Value: certsDir},
   224  		}...)
   225  		// Add secret volume to deployment
   226  		d.Spec.Template.Spec.Volumes = append(d.Spec.Template.Spec.Volumes, v1.Volume{
   227  			Name: "tiller-certs",
   228  			VolumeSource: v1.VolumeSource{
   229  				Secret: &v1.SecretVolumeSource{
   230  					SecretName: "tiller-secret",
   231  				},
   232  			},
   233  		})
   234  	}
   235  	// if --override values were specified, ultimately convert values and deployment to maps,
   236  	// merge them and convert back to Deployment
   237  	if len(opts.Values) > 0 {
   238  		// base deployment struct
   239  		var dd v1beta1.Deployment
   240  		// get YAML from original deployment
   241  		dy, err := yaml.Marshal(d)
   242  		if err != nil {
   243  			return nil, fmt.Errorf("Error marshalling base Tiller Deployment: %s", err)
   244  		}
   245  		// convert deployment YAML to values
   246  		dv, err := chartutil.ReadValues(dy)
   247  		if err != nil {
   248  			return nil, fmt.Errorf("Error converting Deployment manifest: %s ", err)
   249  		}
   250  		dm := dv.AsMap()
   251  		// merge --set values into our map
   252  		sm, err := opts.valuesMap(dm)
   253  		if err != nil {
   254  			return nil, fmt.Errorf("Error merging --set values into Deployment manifest")
   255  		}
   256  		finalY, err := yaml.Marshal(sm)
   257  		if err != nil {
   258  			return nil, fmt.Errorf("Error marshalling merged map to YAML: %s ", err)
   259  		}
   260  		// convert merged values back into deployment
   261  		err = yaml.Unmarshal(finalY, &dd)
   262  		if err != nil {
   263  			return nil, fmt.Errorf("Error unmarshalling Values to Deployment manifest: %s ", err)
   264  		}
   265  		d = &dd
   266  	}
   267  
   268  	return d, nil
   269  }
   270  
   271  func generateService(namespace string) *v1.Service {
   272  	labels := generateLabels(map[string]string{"name": "tiller"})
   273  	s := &v1.Service{
   274  		ObjectMeta: metav1.ObjectMeta{
   275  			Namespace: namespace,
   276  			Name:      serviceName,
   277  			Labels:    labels,
   278  		},
   279  		Spec: v1.ServiceSpec{
   280  			Type: v1.ServiceTypeClusterIP,
   281  			Ports: []v1.ServicePort{
   282  				{
   283  					Name:       "tiller",
   284  					Port:       44134,
   285  					TargetPort: intstr.FromString("tiller"),
   286  				},
   287  			},
   288  			Selector: labels,
   289  		},
   290  	}
   291  	return s
   292  }
   293  
   294  // SecretManifest gets the manifest (as a string) that describes the Tiller Secret resource.
   295  func SecretManifest(opts *Options) (string, error) {
   296  	o, err := generateSecret(opts)
   297  	if err != nil {
   298  		return "", err
   299  	}
   300  	buf, err := yaml.Marshal(o)
   301  	return string(buf), err
   302  }
   303  
   304  // createSecret creates the Tiller secret resource.
   305  func createSecret(client corev1.SecretsGetter, opts *Options) error {
   306  	o, err := generateSecret(opts)
   307  	if err != nil {
   308  		return err
   309  	}
   310  	_, err = client.Secrets(o.Namespace).Create(o)
   311  	return err
   312  }
   313  
   314  // generateSecret builds the secret object that hold Tiller secrets.
   315  func generateSecret(opts *Options) (*v1.Secret, error) {
   316  
   317  	labels := generateLabels(map[string]string{"name": "tiller"})
   318  	secret := &v1.Secret{
   319  		Type: v1.SecretTypeOpaque,
   320  		Data: make(map[string][]byte),
   321  		ObjectMeta: metav1.ObjectMeta{
   322  			Name:      secretName,
   323  			Labels:    labels,
   324  			Namespace: opts.Namespace,
   325  		},
   326  	}
   327  	var err error
   328  	if secret.Data["tls.key"], err = read(opts.TLSKeyFile); err != nil {
   329  		return nil, err
   330  	}
   331  	if secret.Data["tls.crt"], err = read(opts.TLSCertFile); err != nil {
   332  		return nil, err
   333  	}
   334  	if opts.VerifyTLS {
   335  		if secret.Data["ca.crt"], err = read(opts.TLSCaCertFile); err != nil {
   336  			return nil, err
   337  		}
   338  	}
   339  	return secret, nil
   340  }
   341  
   342  func read(path string) (b []byte, err error) { return ioutil.ReadFile(path) }