github.com/darkowlzz/helm@v2.5.1-0.20171213183701-6707fe0468d4+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_PENDING_INSTALL},
   120  			Description:   "Initial install underway", // Will be overwritten.
   121  		},
   122  		Manifest: manifestDoc.String(),
   123  		Hooks:    hooks,
   124  		Version:  int32(revision),
   125  	}
   126  	if len(notesTxt) > 0 {
   127  		rel.Info.Status.Notes = notesTxt
   128  	}
   129  
   130  	err = validateManifest(s.env.KubeClient, req.Namespace, manifestDoc.Bytes())
   131  	return rel, err
   132  }
   133  
   134  // performRelease runs a release.
   135  func (s *ReleaseServer) performRelease(r *release.Release, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) {
   136  	res := &services.InstallReleaseResponse{Release: r}
   137  
   138  	if req.DryRun {
   139  		s.Log("dry run for %s", r.Name)
   140  		res.Release.Info.Description = "Dry run complete"
   141  		return res, nil
   142  	}
   143  
   144  	// pre-install hooks
   145  	if !req.DisableHooks {
   146  		if err := s.execHook(r.Hooks, r.Name, r.Namespace, hooks.PreInstall, req.Timeout); err != nil {
   147  			return res, err
   148  		}
   149  	} else {
   150  		s.Log("install hooks disabled for %s", req.Name)
   151  	}
   152  
   153  	switch h, err := s.env.Releases.History(req.Name); {
   154  	// if this is a replace operation, append to the release history
   155  	case req.ReuseName && err == nil && len(h) >= 1:
   156  		s.Log("name reuse for %s requested, replacing release", req.Name)
   157  		// get latest release revision
   158  		relutil.Reverse(h, relutil.SortByRevision)
   159  
   160  		// old release
   161  		old := h[0]
   162  
   163  		// update old release status
   164  		old.Info.Status.Code = release.Status_SUPERSEDED
   165  		s.recordRelease(old, true)
   166  
   167  		// update new release with next revision number
   168  		// so as to append to the old release's history
   169  		r.Version = old.Version + 1
   170  		updateReq := &services.UpdateReleaseRequest{
   171  			Wait:     req.Wait,
   172  			Recreate: false,
   173  			Timeout:  req.Timeout,
   174  		}
   175  		s.recordRelease(r, false)
   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, true)
   184  			return res, err
   185  		}
   186  
   187  	default:
   188  		// nothing to replace, create as normal
   189  		// regular manifests
   190  		s.recordRelease(r, false)
   191  		if err := s.ReleaseModule.Create(r, req, s.env); err != nil {
   192  			msg := fmt.Sprintf("Release %q failed: %s", r.Name, err)
   193  			s.Log("warning: %s", msg)
   194  			r.Info.Status.Code = release.Status_FAILED
   195  			r.Info.Description = msg
   196  			s.recordRelease(r, true)
   197  			return res, fmt.Errorf("release %s failed: %s", r.Name, err)
   198  		}
   199  	}
   200  
   201  	// post-install hooks
   202  	if !req.DisableHooks {
   203  		if err := s.execHook(r.Hooks, r.Name, r.Namespace, hooks.PostInstall, req.Timeout); err != nil {
   204  			msg := fmt.Sprintf("Release %q failed post-install: %s", r.Name, err)
   205  			s.Log("warning: %s", msg)
   206  			r.Info.Status.Code = release.Status_FAILED
   207  			r.Info.Description = msg
   208  			s.recordRelease(r, true)
   209  			return res, err
   210  		}
   211  	}
   212  
   213  	r.Info.Status.Code = release.Status_DEPLOYED
   214  	r.Info.Description = "Install complete"
   215  	// This is a tricky case. The release has been created, but the result
   216  	// cannot be recorded. The truest thing to tell the user is that the
   217  	// release was created. However, the user will not be able to do anything
   218  	// further with this release.
   219  	//
   220  	// One possible strategy would be to do a timed retry to see if we can get
   221  	// this stored in the future.
   222  	s.recordRelease(r, true)
   223  
   224  	return res, nil
   225  }