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