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) }