github.com/latiif/helm@v2.15.0+incompatible/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 func (s *ReleaseServer) UpdateRelease(c ctx.Context, req *services.UpdateReleaseRequest) (*services.UpdateReleaseResponse, error) { 34 if err := validateReleaseName(req.Name); err != nil { 35 s.Log("updateRelease: Release name is invalid: %s", req.Name) 36 return nil, err 37 } 38 s.Log("preparing update for %s", req.Name) 39 currentRelease, updatedRelease, err := s.prepareUpdate(req) 40 if err != nil { 41 s.Log("failed to prepare update: %s", err) 42 if req.Force { 43 // Use the --force, Luke. 44 s.Log("performing force update for %s", req.Name) 45 return s.performUpdateForce(req) 46 } 47 return nil, err 48 } 49 50 if !req.DryRun { 51 s.Log("creating updated release for %s", req.Name) 52 if err := s.env.Releases.Create(updatedRelease); err != nil { 53 return nil, err 54 } 55 } 56 57 s.Log("performing update for %s", req.Name) 58 res, err := s.performUpdate(currentRelease, updatedRelease, req) 59 if err != nil { 60 return res, err 61 } 62 63 if !req.DryRun { 64 s.Log("updating status for updated release for %s", req.Name) 65 if err := s.env.Releases.Update(updatedRelease); err != nil { 66 return res, err 67 } 68 } 69 70 return res, nil 71 } 72 73 // prepareUpdate builds an updated release for an update operation. 74 func (s *ReleaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*release.Release, *release.Release, error) { 75 if req.Chart == nil { 76 return nil, nil, errMissingChart 77 } 78 79 // finds the deployed release with the given name 80 currentRelease, err := s.env.Releases.Deployed(req.Name) 81 if err != nil { 82 return nil, nil, err 83 } 84 85 // determine if values will be reused 86 if err := s.reuseValues(req, currentRelease); err != nil { 87 return nil, nil, err 88 } 89 90 // finds the non-deleted release with the given name 91 lastRelease, err := s.env.Releases.Last(req.Name) 92 if err != nil { 93 return nil, nil, err 94 } 95 96 // Increment revision count. This is passed to templates, and also stored on 97 // the release object. 98 revision := lastRelease.Version + 1 99 100 ts := timeconv.Now() 101 options := chartutil.ReleaseOptions{ 102 Name: req.Name, 103 Time: ts, 104 Namespace: currentRelease.Namespace, 105 IsUpgrade: true, 106 Revision: int(revision), 107 } 108 109 caps, err := capabilities(s.clientset.Discovery()) 110 if err != nil { 111 return nil, nil, err 112 } 113 valuesToRender, err := chartutil.ToRenderValuesCaps(req.Chart, req.Values, options, caps) 114 if err != nil { 115 return nil, nil, err 116 } 117 118 hooks, manifestDoc, notesTxt, err := s.renderResources(req.Chart, valuesToRender, req.SubNotes, caps.APIVersions) 119 if err != nil { 120 return nil, nil, err 121 } 122 123 // Store an updated release. 124 updatedRelease := &release.Release{ 125 Name: req.Name, 126 Namespace: currentRelease.Namespace, 127 Chart: req.Chart, 128 Config: req.Values, 129 Info: &release.Info{ 130 FirstDeployed: currentRelease.Info.FirstDeployed, 131 LastDeployed: ts, 132 Status: &release.Status{Code: release.Status_PENDING_UPGRADE}, 133 Description: "Preparing upgrade", // This should be overwritten later. 134 }, 135 Version: revision, 136 Manifest: manifestDoc.String(), 137 Hooks: hooks, 138 } 139 140 if len(notesTxt) > 0 { 141 updatedRelease.Info.Status.Notes = notesTxt 142 } 143 err = validateManifest(s.env.KubeClient, currentRelease.Namespace, manifestDoc.Bytes()) 144 return currentRelease, updatedRelease, err 145 } 146 147 // performUpdateForce performs the same action as a `helm delete && helm install --replace`. 148 func (s *ReleaseServer) performUpdateForce(req *services.UpdateReleaseRequest) (*services.UpdateReleaseResponse, error) { 149 // find the last release with the given name 150 oldRelease, err := s.env.Releases.Last(req.Name) 151 if err != nil { 152 return nil, err 153 } 154 155 res := &services.UpdateReleaseResponse{} 156 157 newRelease, err := s.prepareRelease(&services.InstallReleaseRequest{ 158 Chart: req.Chart, 159 Values: req.Values, 160 DryRun: req.DryRun, 161 Name: req.Name, 162 DisableHooks: req.DisableHooks, 163 Namespace: oldRelease.Namespace, 164 ReuseName: true, 165 Timeout: req.Timeout, 166 Wait: req.Wait, 167 }) 168 if err != nil { 169 s.Log("failed update prepare step: %s", err) 170 // On dry run, append the manifest contents to a failed release. This is 171 // a stop-gap until we can revisit an error backchannel post-2.0. 172 if req.DryRun && strings.HasPrefix(err.Error(), "YAML parse error") { 173 err = fmt.Errorf("%s\n%s", err, newRelease.Manifest) 174 } 175 return res, err 176 } 177 178 // update new release with next revision number so as to append to the old release's history 179 newRelease.Version = oldRelease.Version + 1 180 res.Release = newRelease 181 182 if req.DryRun { 183 s.Log("dry run for %s", newRelease.Name) 184 res.Release.Info.Description = "Dry run complete" 185 return res, nil 186 } 187 188 // From here on out, the release is considered to be in Status_DELETING or Status_DELETED 189 // state. There is no turning back. 190 oldRelease.Info.Status.Code = release.Status_DELETING 191 oldRelease.Info.Deleted = timeconv.Now() 192 oldRelease.Info.Description = "Deletion in progress (or silently failed)" 193 s.recordRelease(oldRelease, true) 194 195 // pre-delete hooks 196 if !req.DisableHooks { 197 if err := s.execHook(oldRelease.Hooks, oldRelease.Name, oldRelease.Namespace, hooks.PreDelete, req.Timeout); err != nil { 198 return res, err 199 } 200 } else { 201 s.Log("hooks disabled for %s", req.Name) 202 } 203 204 // delete manifests from the old release 205 _, errs := s.ReleaseModule.Delete(oldRelease, nil, s.env) 206 207 oldRelease.Info.Status.Code = release.Status_DELETED 208 oldRelease.Info.Description = "Deletion complete" 209 s.recordRelease(oldRelease, true) 210 211 if len(errs) > 0 { 212 es := make([]string, 0, len(errs)) 213 for _, e := range errs { 214 s.Log("error: %v", e) 215 es = append(es, e.Error()) 216 } 217 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, "; ")) 218 } 219 220 // post-delete hooks 221 if !req.DisableHooks { 222 if err := s.execHook(oldRelease.Hooks, oldRelease.Name, oldRelease.Namespace, hooks.PostDelete, req.Timeout); err != nil { 223 return res, err 224 } 225 } 226 227 // pre-install hooks 228 if !req.DisableHooks { 229 if err := s.execHook(newRelease.Hooks, newRelease.Name, newRelease.Namespace, hooks.PreInstall, req.Timeout); err != nil { 230 return res, err 231 } 232 } 233 234 s.recordRelease(newRelease, false) 235 if err := s.ReleaseModule.Update(oldRelease, newRelease, req, s.env); err != nil { 236 msg := fmt.Sprintf("Upgrade %q failed: %s", newRelease.Name, err) 237 s.Log("warning: %s", msg) 238 newRelease.Info.Status.Code = release.Status_FAILED 239 newRelease.Info.Description = msg 240 s.recordRelease(newRelease, true) 241 return res, err 242 } 243 244 // post-install hooks 245 if !req.DisableHooks { 246 if err := s.execHook(newRelease.Hooks, newRelease.Name, newRelease.Namespace, hooks.PostInstall, req.Timeout); err != nil { 247 msg := fmt.Sprintf("Release %q failed post-install: %s", newRelease.Name, err) 248 s.Log("warning: %s", msg) 249 newRelease.Info.Status.Code = release.Status_FAILED 250 newRelease.Info.Description = msg 251 s.recordRelease(newRelease, true) 252 return res, err 253 } 254 } 255 256 newRelease.Info.Status.Code = release.Status_DEPLOYED 257 if req.Description == "" { 258 newRelease.Info.Description = "Upgrade complete" 259 } else { 260 newRelease.Info.Description = req.Description 261 } 262 s.recordRelease(newRelease, true) 263 264 return res, nil 265 } 266 267 func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.Release, req *services.UpdateReleaseRequest) (*services.UpdateReleaseResponse, error) { 268 res := &services.UpdateReleaseResponse{Release: updatedRelease} 269 270 if req.DryRun { 271 s.Log("dry run for %s", updatedRelease.Name) 272 res.Release.Info.Description = "Dry run complete" 273 return res, nil 274 } 275 276 // pre-upgrade hooks 277 if !req.DisableHooks { 278 if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, hooks.PreUpgrade, req.Timeout); err != nil { 279 return res, err 280 } 281 } else { 282 s.Log("update hooks disabled for %s", req.Name) 283 } 284 if err := s.ReleaseModule.Update(originalRelease, updatedRelease, req, s.env); err != nil { 285 msg := fmt.Sprintf("Upgrade %q failed: %s", updatedRelease.Name, err) 286 s.Log("warning: %s", msg) 287 updatedRelease.Info.Status.Code = release.Status_FAILED 288 updatedRelease.Info.Description = msg 289 s.recordRelease(originalRelease, true) 290 s.recordRelease(updatedRelease, true) 291 return res, err 292 } 293 294 // post-upgrade hooks 295 if !req.DisableHooks { 296 if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, hooks.PostUpgrade, req.Timeout); err != nil { 297 return res, err 298 } 299 } 300 301 originalRelease.Info.Status.Code = release.Status_SUPERSEDED 302 s.recordRelease(originalRelease, true) 303 304 updatedRelease.Info.Status.Code = release.Status_DEPLOYED 305 if req.Description == "" { 306 updatedRelease.Info.Description = "Upgrade complete" 307 } else { 308 updatedRelease.Info.Description = req.Description 309 } 310 311 return res, nil 312 }