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 }