github.com/Beeketing/helm@v2.12.1+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 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 // determine if values will be reused 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 res := &services.UpdateReleaseResponse{} 154 155 newRelease, err := s.prepareRelease(&services.InstallReleaseRequest{ 156 Chart: req.Chart, 157 Values: req.Values, 158 DryRun: req.DryRun, 159 Name: req.Name, 160 DisableHooks: req.DisableHooks, 161 Namespace: oldRelease.Namespace, 162 ReuseName: true, 163 Timeout: req.Timeout, 164 Wait: req.Wait, 165 }) 166 if err != nil { 167 s.Log("failed update prepare step: %s", err) 168 // On dry run, append the manifest contents to a failed release. This is 169 // a stop-gap until we can revisit an error backchannel post-2.0. 170 if req.DryRun && strings.HasPrefix(err.Error(), "YAML parse error") { 171 err = fmt.Errorf("%s\n%s", err, newRelease.Manifest) 172 } 173 return res, err 174 } 175 176 // update new release with next revision number so as to append to the old release's history 177 newRelease.Version = oldRelease.Version + 1 178 res.Release = newRelease 179 180 if req.DryRun { 181 s.Log("dry run for %s", newRelease.Name) 182 res.Release.Info.Description = "Dry run complete" 183 return res, nil 184 } 185 186 // From here on out, the release is considered to be in Status_DELETING or Status_DELETED 187 // state. There is no turning back. 188 oldRelease.Info.Status.Code = release.Status_DELETING 189 oldRelease.Info.Deleted = timeconv.Now() 190 oldRelease.Info.Description = "Deletion in progress (or silently failed)" 191 s.recordRelease(oldRelease, true) 192 193 // pre-delete hooks 194 if !req.DisableHooks { 195 if err := s.execHook(oldRelease.Hooks, oldRelease.Name, oldRelease.Namespace, hooks.PreDelete, req.Timeout); err != nil { 196 return res, err 197 } 198 } else { 199 s.Log("hooks disabled for %s", req.Name) 200 } 201 202 // delete manifests from the old release 203 _, errs := s.ReleaseModule.Delete(oldRelease, nil, s.env) 204 205 oldRelease.Info.Status.Code = release.Status_DELETED 206 oldRelease.Info.Description = "Deletion complete" 207 s.recordRelease(oldRelease, true) 208 209 if len(errs) > 0 { 210 es := make([]string, 0, len(errs)) 211 for _, e := range errs { 212 s.Log("error: %v", e) 213 es = append(es, e.Error()) 214 } 215 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, "; ")) 216 } 217 218 // post-delete hooks 219 if !req.DisableHooks { 220 if err := s.execHook(oldRelease.Hooks, oldRelease.Name, oldRelease.Namespace, hooks.PostDelete, req.Timeout); err != nil { 221 return res, err 222 } 223 } 224 225 // pre-install hooks 226 if !req.DisableHooks { 227 if err := s.execHook(newRelease.Hooks, newRelease.Name, newRelease.Namespace, hooks.PreInstall, req.Timeout); err != nil { 228 return res, err 229 } 230 } 231 232 s.recordRelease(newRelease, false) 233 if err := s.ReleaseModule.Update(oldRelease, newRelease, req, s.env); err != nil { 234 msg := fmt.Sprintf("Upgrade %q failed: %s", newRelease.Name, err) 235 s.Log("warning: %s", msg) 236 newRelease.Info.Status.Code = release.Status_FAILED 237 newRelease.Info.Description = msg 238 s.recordRelease(newRelease, true) 239 return res, err 240 } 241 242 // post-install hooks 243 if !req.DisableHooks { 244 if err := s.execHook(newRelease.Hooks, newRelease.Name, newRelease.Namespace, hooks.PostInstall, req.Timeout); err != nil { 245 msg := fmt.Sprintf("Release %q failed post-install: %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 254 newRelease.Info.Status.Code = release.Status_DEPLOYED 255 if req.Description == "" { 256 newRelease.Info.Description = "Upgrade complete" 257 } else { 258 newRelease.Info.Description = req.Description 259 } 260 s.recordRelease(newRelease, true) 261 262 return res, nil 263 } 264 265 func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.Release, req *services.UpdateReleaseRequest) (*services.UpdateReleaseResponse, error) { 266 res := &services.UpdateReleaseResponse{Release: updatedRelease} 267 268 if req.DryRun { 269 s.Log("dry run for %s", updatedRelease.Name) 270 res.Release.Info.Description = "Dry run complete" 271 return res, nil 272 } 273 274 // pre-upgrade hooks 275 if !req.DisableHooks { 276 if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, hooks.PreUpgrade, req.Timeout); err != nil { 277 return res, err 278 } 279 } else { 280 s.Log("update hooks disabled for %s", req.Name) 281 } 282 if err := s.ReleaseModule.Update(originalRelease, updatedRelease, req, s.env); err != nil { 283 msg := fmt.Sprintf("Upgrade %q failed: %s", updatedRelease.Name, err) 284 s.Log("warning: %s", msg) 285 updatedRelease.Info.Status.Code = release.Status_FAILED 286 updatedRelease.Info.Description = msg 287 s.recordRelease(originalRelease, true) 288 s.recordRelease(updatedRelease, true) 289 return res, err 290 } 291 292 // post-upgrade hooks 293 if !req.DisableHooks { 294 if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, hooks.PostUpgrade, req.Timeout); err != nil { 295 return res, err 296 } 297 } 298 299 originalRelease.Info.Status.Code = release.Status_SUPERSEDED 300 s.recordRelease(originalRelease, true) 301 302 updatedRelease.Info.Status.Code = release.Status_DEPLOYED 303 if req.Description == "" { 304 updatedRelease.Info.Description = "Upgrade complete" 305 } else { 306 updatedRelease.Info.Description = req.Description 307 } 308 309 return res, nil 310 }