github.com/zoumo/helm@v2.5.0+incompatible/pkg/tiller/release_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 tiller
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  
    23  	ctx "golang.org/x/net/context"
    24  
    25  	"k8s.io/helm/pkg/chartutil"
    26  	"k8s.io/helm/pkg/hooks"
    27  	"k8s.io/helm/pkg/proto/hapi/release"
    28  	"k8s.io/helm/pkg/proto/hapi/services"
    29  	relutil "k8s.io/helm/pkg/releaseutil"
    30  	"k8s.io/helm/pkg/timeconv"
    31  )
    32  
    33  // InstallRelease installs a release and stores the release record.
    34  func (s *ReleaseServer) InstallRelease(c ctx.Context, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) {
    35  	s.Log("preparing install for %s", req.Name)
    36  	rel, err := s.prepareRelease(req)
    37  	if err != nil {
    38  		s.Log("failed install prepare step: %s", err)
    39  		res := &services.InstallReleaseResponse{Release: rel}
    40  
    41  		// On dry run, append the manifest contents to a failed release. This is
    42  		// a stop-gap until we can revisit an error backchannel post-2.0.
    43  		if req.DryRun && strings.HasPrefix(err.Error(), "YAML parse error") {
    44  			err = fmt.Errorf("%s\n%s", err, rel.Manifest)
    45  		}
    46  		return res, err
    47  	}
    48  
    49  	s.Log("performing install for %s", req.Name)
    50  	res, err := s.performRelease(rel, req)
    51  	if err != nil {
    52  		s.Log("failed install perform step: %s", err)
    53  	}
    54  	return res, err
    55  }
    56  
    57  // prepareRelease builds a release for an install operation.
    58  func (s *ReleaseServer) prepareRelease(req *services.InstallReleaseRequest) (*release.Release, error) {
    59  	if req.Chart == nil {
    60  		return nil, errMissingChart
    61  	}
    62  
    63  	name, err := s.uniqName(req.Name, req.ReuseName)
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  
    68  	caps, err := capabilities(s.clientset.Discovery())
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	revision := 1
    74  	ts := timeconv.Now()
    75  	options := chartutil.ReleaseOptions{
    76  		Name:      name,
    77  		Time:      ts,
    78  		Namespace: req.Namespace,
    79  		Revision:  revision,
    80  		IsInstall: true,
    81  	}
    82  	valuesToRender, err := chartutil.ToRenderValuesCaps(req.Chart, req.Values, options, caps)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	hooks, manifestDoc, notesTxt, err := s.renderResources(req.Chart, valuesToRender, caps.APIVersions)
    88  	if err != nil {
    89  		// Return a release with partial data so that client can show debugging
    90  		// information.
    91  		rel := &release.Release{
    92  			Name:      name,
    93  			Namespace: req.Namespace,
    94  			Chart:     req.Chart,
    95  			Config:    req.Values,
    96  			Info: &release.Info{
    97  				FirstDeployed: ts,
    98  				LastDeployed:  ts,
    99  				Status:        &release.Status{Code: release.Status_UNKNOWN},
   100  				Description:   fmt.Sprintf("Install failed: %s", err),
   101  			},
   102  			Version: 0,
   103  		}
   104  		if manifestDoc != nil {
   105  			rel.Manifest = manifestDoc.String()
   106  		}
   107  		return rel, err
   108  	}
   109  
   110  	// Store a release.
   111  	rel := &release.Release{
   112  		Name:      name,
   113  		Namespace: req.Namespace,
   114  		Chart:     req.Chart,
   115  		Config:    req.Values,
   116  		Info: &release.Info{
   117  			FirstDeployed: ts,
   118  			LastDeployed:  ts,
   119  			Status:        &release.Status{Code: release.Status_UNKNOWN},
   120  			Description:   "Initial install underway", // Will be overwritten.
   121  		},
   122  		Manifest:    manifestDoc.String(),
   123  		Hooks:       hooks,
   124  		Version:     int32(revision),
   125  		Annotations: req.Annotations,
   126  	}
   127  	if len(notesTxt) > 0 {
   128  		rel.Info.Status.Notes = notesTxt
   129  	}
   130  
   131  	err = validateManifest(s.env.KubeClient, req.Namespace, manifestDoc.Bytes())
   132  	return rel, err
   133  }
   134  
   135  // performRelease runs a release.
   136  func (s *ReleaseServer) performRelease(r *release.Release, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) {
   137  	res := &services.InstallReleaseResponse{Release: r}
   138  
   139  	if req.DryRun {
   140  		s.Log("dry run for %s", r.Name)
   141  		res.Release.Info.Description = "Dry run complete"
   142  		return res, nil
   143  	}
   144  
   145  	// pre-install hooks
   146  	if !req.DisableHooks {
   147  		if err := s.execHook(r.Hooks, r.Name, r.Namespace, hooks.PreInstall, req.Timeout); err != nil {
   148  			return res, err
   149  		}
   150  	} else {
   151  		s.Log("install hooks disabled for %s", req.Name)
   152  	}
   153  
   154  	switch h, err := s.env.Releases.History(req.Name); {
   155  	// if this is a replace operation, append to the release history
   156  	case req.ReuseName && err == nil && len(h) >= 1:
   157  		s.Log("name reuse for %s requested, replacing release", req.Name)
   158  		// get latest release revision
   159  		relutil.Reverse(h, relutil.SortByRevision)
   160  
   161  		// old release
   162  		old := h[0]
   163  
   164  		// update old release status
   165  		old.Info.Status.Code = release.Status_SUPERSEDED
   166  		s.recordRelease(old, true)
   167  
   168  		// update new release with next revision number
   169  		// so as to append to the old release's history
   170  		r.Version = old.Version + 1
   171  		updateReq := &services.UpdateReleaseRequest{
   172  			Wait:     req.Wait,
   173  			Recreate: false,
   174  			Timeout:  req.Timeout,
   175  		}
   176  		if err := s.ReleaseModule.Update(old, r, updateReq, s.env); err != nil {
   177  			msg := fmt.Sprintf("Release replace %q failed: %s", r.Name, err)
   178  			s.Log("warning: %s", msg)
   179  			old.Info.Status.Code = release.Status_SUPERSEDED
   180  			r.Info.Status.Code = release.Status_FAILED
   181  			r.Info.Description = msg
   182  			s.recordRelease(old, true)
   183  			s.recordRelease(r, false)
   184  			return res, err
   185  		}
   186  
   187  	default:
   188  		// nothing to replace, create as normal
   189  		// regular manifests
   190  		if err := s.ReleaseModule.Create(r, req, s.env); err != nil {
   191  			msg := fmt.Sprintf("Release %q failed: %s", r.Name, err)
   192  			s.Log("warning: %s", msg)
   193  			r.Info.Status.Code = release.Status_FAILED
   194  			r.Info.Description = msg
   195  			s.recordRelease(r, false)
   196  			return res, fmt.Errorf("release %s failed: %s", r.Name, err)
   197  		}
   198  	}
   199  
   200  	// post-install hooks
   201  	if !req.DisableHooks {
   202  		if err := s.execHook(r.Hooks, r.Name, r.Namespace, hooks.PostInstall, req.Timeout); err != nil {
   203  			msg := fmt.Sprintf("Release %q failed post-install: %s", r.Name, err)
   204  			s.Log("warning: %s", msg)
   205  			r.Info.Status.Code = release.Status_FAILED
   206  			r.Info.Description = msg
   207  			s.recordRelease(r, false)
   208  			return res, err
   209  		}
   210  	}
   211  
   212  	r.Info.Status.Code = release.Status_DEPLOYED
   213  	r.Info.Description = "Install complete"
   214  	// This is a tricky case. The release has been created, but the result
   215  	// cannot be recorded. The truest thing to tell the user is that the
   216  	// release was created. However, the user will not be able to do anything
   217  	// further with this release.
   218  	//
   219  	// One possible strategy would be to do a timed retry to see if we can get
   220  	// this stored in the future.
   221  	s.recordRelease(r, false)
   222  
   223  	return res, nil
   224  }