github.com/wangchanggan/helm@v0.0.0-20211020154240-11b1b7d5406d/pkg/tiller/release_update.go (about)

     1  /*
     2  Copyright The Helm Authors.
     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  	"k8s.io/helm/pkg/timeconv"
    30  )
    31  
    32  // UpdateRelease takes an existing release and new information, and upgrades the release.
    33  // 首先需要准备更新,先下载依赖项,将所有依赖的Chart下载到本地,然后根据传递过来的参数覆盖原values,最后渲染出一个成型的yaml文件。
    34  func (s *ReleaseServer) UpdateRelease(c ctx.Context, req *services.UpdateReleaseRequest) (*services.UpdateReleaseResponse, error) {
    35  	if err := validateReleaseName(req.Name); err != nil {
    36  		s.Log("updateRelease: Release name is invalid: %s", req.Name)
    37  		return nil, err
    38  	}
    39  	s.Log("preparing update for %s", req.Name)
    40  	currentRelease, updatedRelease, err := s.prepareUpdate(req)
    41  	if err != nil {
    42  		s.Log("failed to prepare update: %s", err)
    43  		if req.Force {
    44  			// Use the --force, Luke.
    45  			s.Log("performing force update for %s", req.Name)
    46  			return s.performUpdateForce(req)
    47  		}
    48  		return nil, err
    49  	}
    50  
    51  	if !req.DryRun {
    52  		s.Log("creating updated release for %s", req.Name)
    53  		if err := s.env.Releases.Create(updatedRelease); err != nil {
    54  			return nil, err
    55  		}
    56  	}
    57  
    58  	s.Log("performing update for %s", req.Name)
    59  	res, err := s.performUpdate(currentRelease, updatedRelease, req)
    60  	if err != nil {
    61  		return res, err
    62  	}
    63  
    64  	if !req.DryRun {
    65  		s.Log("updating status for updated release for %s", req.Name)
    66  		if err := s.env.Releases.Update(updatedRelease); err != nil {
    67  			return res, err
    68  		}
    69  	}
    70  
    71  	return res, nil
    72  }
    73  
    74  // prepareUpdate builds an updated release for an update operation.
    75  func (s *ReleaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*release.Release, *release.Release, error) {
    76  	if req.Chart == nil {
    77  		return nil, nil, errMissingChart
    78  	}
    79  
    80  	// finds the deployed release with the given name
    81  	currentRelease, err := s.env.Releases.Deployed(req.Name)
    82  	if err != nil {
    83  		return nil, nil, err
    84  	}
    85  
    86  	// determine if values will be reused
    87  	if err := s.reuseValues(req, currentRelease); err != nil {
    88  		return nil, nil, err
    89  	}
    90  
    91  	// finds the non-deleted release with the given name
    92  	lastRelease, err := s.env.Releases.Last(req.Name)
    93  	if err != nil {
    94  		return nil, nil, err
    95  	}
    96  
    97  	// Concurrent `helm upgrade`s will either fail here with `errPending` or
    98  	// when creating the release with "already exists". This should act as a
    99  	// pessimistic lock.
   100  	sc := lastRelease.Info.Status.Code
   101  	if sc == release.Status_PENDING_INSTALL || sc == release.Status_PENDING_UPGRADE || sc == release.Status_PENDING_ROLLBACK {
   102  		return nil, nil, errPending
   103  	}
   104  
   105  	// Increment revision count. This is passed to templates, and also stored on
   106  	// the release object.
   107  	revision := lastRelease.Version + 1
   108  
   109  	ts := timeconv.Now()
   110  	options := chartutil.ReleaseOptions{
   111  		Name:      req.Name,
   112  		Time:      ts,
   113  		Namespace: currentRelease.Namespace,
   114  		IsUpgrade: true,
   115  		Revision:  int(revision),
   116  	}
   117  
   118  	caps, err := capabilities(s.clientset.Discovery())
   119  	if err != nil {
   120  		return nil, nil, err
   121  	}
   122  	valuesToRender, err := chartutil.ToRenderValuesCaps(req.Chart, req.Values, options, caps)
   123  	if err != nil {
   124  		return nil, nil, err
   125  	}
   126  
   127  	hooks, manifestDoc, notesTxt, err := s.renderResources(req.Chart, valuesToRender, req.SubNotes, caps.APIVersions)
   128  	if err != nil {
   129  		return nil, nil, err
   130  	}
   131  
   132  	// Store an updated release.
   133  	updatedRelease := &release.Release{
   134  		Name:      req.Name,
   135  		Namespace: currentRelease.Namespace,
   136  		Chart:     req.Chart,
   137  		Config:    req.Values,
   138  		Info: &release.Info{
   139  			FirstDeployed: currentRelease.Info.FirstDeployed,
   140  			LastDeployed:  ts,
   141  			Status:        &release.Status{Code: release.Status_PENDING_UPGRADE},
   142  			Description:   "Preparing upgrade", // This should be overwritten later.
   143  		},
   144  		Version:  revision,
   145  		Manifest: manifestDoc.String(),
   146  		Hooks:    hooks,
   147  	}
   148  
   149  	if len(notesTxt) > 0 {
   150  		updatedRelease.Info.Status.Notes = notesTxt
   151  	}
   152  	err = validateManifest(s.env.KubeClient, currentRelease.Namespace, manifestDoc.Bytes())
   153  	return currentRelease, updatedRelease, err
   154  }
   155  
   156  // performUpdateForce performs the same action as a `helm delete && helm install --replace`.
   157  func (s *ReleaseServer) performUpdateForce(req *services.UpdateReleaseRequest) (*services.UpdateReleaseResponse, error) {
   158  	// find the last release with the given name
   159  	oldRelease, err := s.env.Releases.Last(req.Name)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	res := &services.UpdateReleaseResponse{}
   165  
   166  	newRelease, err := s.prepareRelease(&services.InstallReleaseRequest{
   167  		Chart:        req.Chart,
   168  		Values:       req.Values,
   169  		DryRun:       req.DryRun,
   170  		Name:         req.Name,
   171  		DisableHooks: req.DisableHooks,
   172  		Namespace:    oldRelease.Namespace,
   173  		ReuseName:    true,
   174  		Timeout:      req.Timeout,
   175  		Wait:         req.Wait,
   176  	})
   177  	if err != nil {
   178  		s.Log("failed update prepare step: %s", err)
   179  		// On dry run, append the manifest contents to a failed release. This is
   180  		// a stop-gap until we can revisit an error backchannel post-2.0.
   181  		if req.DryRun && strings.HasPrefix(err.Error(), "YAML parse error") {
   182  			err = fmt.Errorf("%s\n%s", err, newRelease.Manifest)
   183  		}
   184  		return res, err
   185  	}
   186  
   187  	// update new release with next revision number so as to append to the old release's history
   188  	newRelease.Version = oldRelease.Version + 1
   189  	res.Release = newRelease
   190  
   191  	if req.DryRun {
   192  		s.Log("dry run for %s", newRelease.Name)
   193  		res.Release.Info.Description = "Dry run complete"
   194  		return res, nil
   195  	}
   196  
   197  	// From here on out, the release is considered to be in Status_DELETING or Status_DELETED
   198  	// state. There is no turning back.
   199  	oldRelease.Info.Status.Code = release.Status_DELETING
   200  	oldRelease.Info.Deleted = timeconv.Now()
   201  	oldRelease.Info.Description = "Deletion in progress (or silently failed)"
   202  	s.recordRelease(oldRelease, true)
   203  
   204  	// pre-delete hooks
   205  	if !req.DisableHooks {
   206  		if err := s.execHook(oldRelease.Hooks, oldRelease.Name, oldRelease.Namespace, hooks.PreDelete, req.Timeout); err != nil {
   207  			return res, err
   208  		}
   209  	} else {
   210  		s.Log("hooks disabled for %s", req.Name)
   211  	}
   212  
   213  	// delete manifests from the old release
   214  	_, errs := s.ReleaseModule.Delete(oldRelease, nil, s.env)
   215  
   216  	oldRelease.Info.Status.Code = release.Status_DELETED
   217  	oldRelease.Info.Description = "Deletion complete"
   218  	s.recordRelease(oldRelease, true)
   219  
   220  	if len(errs) > 0 {
   221  		es := make([]string, 0, len(errs))
   222  		for _, e := range errs {
   223  			s.Log("error: %v", e)
   224  			es = append(es, e.Error())
   225  		}
   226  		return res, fmt.Errorf("Upgrade --force successfully deleted the previous release, but encountered %d error(s) and cannot continue: %s", len(es), strings.Join(es, "; "))
   227  	}
   228  
   229  	// post-delete hooks
   230  	if !req.DisableHooks {
   231  		if err := s.execHook(oldRelease.Hooks, oldRelease.Name, oldRelease.Namespace, hooks.PostDelete, req.Timeout); err != nil {
   232  			return res, err
   233  		}
   234  	}
   235  
   236  	// pre-install hooks
   237  	if !req.DisableHooks {
   238  		if err := s.execHook(newRelease.Hooks, newRelease.Name, newRelease.Namespace, hooks.PreInstall, req.Timeout); err != nil {
   239  			return res, err
   240  		}
   241  	}
   242  
   243  	s.recordRelease(newRelease, false)
   244  	if err := s.ReleaseModule.Update(oldRelease, newRelease, req, s.env); err != nil {
   245  		msg := fmt.Sprintf("Upgrade %q failed: %s", newRelease.Name, err)
   246  		s.Log("warning: %s", msg)
   247  		newRelease.Info.Status.Code = release.Status_FAILED
   248  		newRelease.Info.Description = msg
   249  		s.recordRelease(newRelease, true)
   250  		return res, err
   251  	}
   252  
   253  	// post-install hooks
   254  	if !req.DisableHooks {
   255  		if err := s.execHook(newRelease.Hooks, newRelease.Name, newRelease.Namespace, hooks.PostInstall, req.Timeout); err != nil {
   256  			msg := fmt.Sprintf("Release %q failed post-install: %s", newRelease.Name, err)
   257  			s.Log("warning: %s", msg)
   258  			newRelease.Info.Status.Code = release.Status_FAILED
   259  			newRelease.Info.Description = msg
   260  			s.recordRelease(newRelease, true)
   261  			return res, err
   262  		}
   263  	}
   264  
   265  	newRelease.Info.Status.Code = release.Status_DEPLOYED
   266  	if req.Description == "" {
   267  		newRelease.Info.Description = "Upgrade complete"
   268  	} else {
   269  		newRelease.Info.Description = req.Description
   270  	}
   271  	s.recordRelease(newRelease, true)
   272  
   273  	return res, nil
   274  }
   275  
   276  func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.Release, req *services.UpdateReleaseRequest) (*services.UpdateReleaseResponse, error) {
   277  	res := &services.UpdateReleaseResponse{Release: updatedRelease}
   278  
   279  	if req.DryRun {
   280  		s.Log("dry run for %s", updatedRelease.Name)
   281  		res.Release.Info.Description = "Dry run complete"
   282  		return res, nil
   283  	}
   284  
   285  	// pre-upgrade hooks
   286  	if !req.DisableHooks {
   287  		// 运行pre-upgrade的Hooks。
   288  		if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, hooks.PreUpgrade, req.Timeout); err != nil {
   289  			return res, err
   290  		}
   291  	} else {
   292  		s.Log("update hooks disabled for %s", req.Name)
   293  	}
   294  	// 将渲染好的yaml提交给ApiServer,如果此次更新yaml模板对应的资源没有发生变化,Kubernetes不会更新对应的资源
   295  	// 这个操作是在KubernetesApiServer层面执行的,Helm Client和Server并没有对此做特别的操作。
   296  	if err := s.ReleaseModule.Update(originalRelease, updatedRelease, req, s.env); err != nil {
   297  		msg := fmt.Sprintf("Upgrade %q failed: %s", updatedRelease.Name, err)
   298  		s.Log("warning: %s", msg)
   299  		updatedRelease.Info.Status.Code = release.Status_FAILED
   300  		updatedRelease.Info.Description = msg
   301  		s.recordRelease(originalRelease, true)
   302  		s.recordRelease(updatedRelease, true)
   303  		return res, err
   304  	}
   305  
   306  	// post-upgrade hooks
   307  	if !req.DisableHooks {
   308  		// 运行post-update的Hooks。
   309  		if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, hooks.PostUpgrade, req.Timeout); err != nil {
   310  			return res, err
   311  		}
   312  	}
   313  
   314  	originalRelease.Info.Status.Code = release.Status_SUPERSEDED
   315  	s.recordRelease(originalRelease, true)
   316  
   317  	// 更改Release的安装状态。
   318  	updatedRelease.Info.Status.Code = release.Status_DEPLOYED
   319  	if req.Description == "" {
   320  		updatedRelease.Info.Description = "Upgrade complete"
   321  	} else {
   322  		updatedRelease.Info.Description = req.Description
   323  	}
   324  
   325  	return res, nil
   326  }