github.com/darkowlzz/helm@v2.5.1-0.20171213183701-6707fe0468d4+incompatible/pkg/tiller/release_install.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 relutil "k8s.io/helm/pkg/releaseutil" 30 "k8s.io/helm/pkg/timeconv" 31 ) 32 33 // InstallRelease installs a release and stores the release record. 34 func (s *ReleaseServer) InstallRelease(c ctx.Context, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) { 35 s.Log("preparing install for %s", req.Name) 36 rel, err := s.prepareRelease(req) 37 if err != nil { 38 s.Log("failed install prepare step: %s", err) 39 res := &services.InstallReleaseResponse{Release: rel} 40 41 // On dry run, append the manifest contents to a failed release. This is 42 // a stop-gap until we can revisit an error backchannel post-2.0. 43 if req.DryRun && strings.HasPrefix(err.Error(), "YAML parse error") { 44 err = fmt.Errorf("%s\n%s", err, rel.Manifest) 45 } 46 return res, err 47 } 48 49 s.Log("performing install for %s", req.Name) 50 res, err := s.performRelease(rel, req) 51 if err != nil { 52 s.Log("failed install perform step: %s", err) 53 } 54 return res, err 55 } 56 57 // prepareRelease builds a release for an install operation. 58 func (s *ReleaseServer) prepareRelease(req *services.InstallReleaseRequest) (*release.Release, error) { 59 if req.Chart == nil { 60 return nil, errMissingChart 61 } 62 63 name, err := s.uniqName(req.Name, req.ReuseName) 64 if err != nil { 65 return nil, err 66 } 67 68 caps, err := capabilities(s.clientset.Discovery()) 69 if err != nil { 70 return nil, err 71 } 72 73 revision := 1 74 ts := timeconv.Now() 75 options := chartutil.ReleaseOptions{ 76 Name: name, 77 Time: ts, 78 Namespace: req.Namespace, 79 Revision: revision, 80 IsInstall: true, 81 } 82 valuesToRender, err := chartutil.ToRenderValuesCaps(req.Chart, req.Values, options, caps) 83 if err != nil { 84 return nil, err 85 } 86 87 hooks, manifestDoc, notesTxt, err := s.renderResources(req.Chart, valuesToRender, caps.APIVersions) 88 if err != nil { 89 // Return a release with partial data so that client can show debugging 90 // information. 91 rel := &release.Release{ 92 Name: name, 93 Namespace: req.Namespace, 94 Chart: req.Chart, 95 Config: req.Values, 96 Info: &release.Info{ 97 FirstDeployed: ts, 98 LastDeployed: ts, 99 Status: &release.Status{Code: release.Status_UNKNOWN}, 100 Description: fmt.Sprintf("Install failed: %s", err), 101 }, 102 Version: 0, 103 } 104 if manifestDoc != nil { 105 rel.Manifest = manifestDoc.String() 106 } 107 return rel, err 108 } 109 110 // Store a release. 111 rel := &release.Release{ 112 Name: name, 113 Namespace: req.Namespace, 114 Chart: req.Chart, 115 Config: req.Values, 116 Info: &release.Info{ 117 FirstDeployed: ts, 118 LastDeployed: ts, 119 Status: &release.Status{Code: release.Status_PENDING_INSTALL}, 120 Description: "Initial install underway", // Will be overwritten. 121 }, 122 Manifest: manifestDoc.String(), 123 Hooks: hooks, 124 Version: int32(revision), 125 } 126 if len(notesTxt) > 0 { 127 rel.Info.Status.Notes = notesTxt 128 } 129 130 err = validateManifest(s.env.KubeClient, req.Namespace, manifestDoc.Bytes()) 131 return rel, err 132 } 133 134 // performRelease runs a release. 135 func (s *ReleaseServer) performRelease(r *release.Release, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) { 136 res := &services.InstallReleaseResponse{Release: r} 137 138 if req.DryRun { 139 s.Log("dry run for %s", r.Name) 140 res.Release.Info.Description = "Dry run complete" 141 return res, nil 142 } 143 144 // pre-install hooks 145 if !req.DisableHooks { 146 if err := s.execHook(r.Hooks, r.Name, r.Namespace, hooks.PreInstall, req.Timeout); err != nil { 147 return res, err 148 } 149 } else { 150 s.Log("install hooks disabled for %s", req.Name) 151 } 152 153 switch h, err := s.env.Releases.History(req.Name); { 154 // if this is a replace operation, append to the release history 155 case req.ReuseName && err == nil && len(h) >= 1: 156 s.Log("name reuse for %s requested, replacing release", req.Name) 157 // get latest release revision 158 relutil.Reverse(h, relutil.SortByRevision) 159 160 // old release 161 old := h[0] 162 163 // update old release status 164 old.Info.Status.Code = release.Status_SUPERSEDED 165 s.recordRelease(old, true) 166 167 // update new release with next revision number 168 // so as to append to the old release's history 169 r.Version = old.Version + 1 170 updateReq := &services.UpdateReleaseRequest{ 171 Wait: req.Wait, 172 Recreate: false, 173 Timeout: req.Timeout, 174 } 175 s.recordRelease(r, false) 176 if err := s.ReleaseModule.Update(old, r, updateReq, s.env); err != nil { 177 msg := fmt.Sprintf("Release replace %q failed: %s", r.Name, err) 178 s.Log("warning: %s", msg) 179 old.Info.Status.Code = release.Status_SUPERSEDED 180 r.Info.Status.Code = release.Status_FAILED 181 r.Info.Description = msg 182 s.recordRelease(old, true) 183 s.recordRelease(r, true) 184 return res, err 185 } 186 187 default: 188 // nothing to replace, create as normal 189 // regular manifests 190 s.recordRelease(r, false) 191 if err := s.ReleaseModule.Create(r, req, s.env); err != nil { 192 msg := fmt.Sprintf("Release %q failed: %s", r.Name, err) 193 s.Log("warning: %s", msg) 194 r.Info.Status.Code = release.Status_FAILED 195 r.Info.Description = msg 196 s.recordRelease(r, true) 197 return res, fmt.Errorf("release %s failed: %s", r.Name, err) 198 } 199 } 200 201 // post-install hooks 202 if !req.DisableHooks { 203 if err := s.execHook(r.Hooks, r.Name, r.Namespace, hooks.PostInstall, req.Timeout); err != nil { 204 msg := fmt.Sprintf("Release %q failed post-install: %s", r.Name, err) 205 s.Log("warning: %s", msg) 206 r.Info.Status.Code = release.Status_FAILED 207 r.Info.Description = msg 208 s.recordRelease(r, true) 209 return res, err 210 } 211 } 212 213 r.Info.Status.Code = release.Status_DEPLOYED 214 r.Info.Description = "Install complete" 215 // This is a tricky case. The release has been created, but the result 216 // cannot be recorded. The truest thing to tell the user is that the 217 // release was created. However, the user will not be able to do anything 218 // further with this release. 219 // 220 // One possible strategy would be to do a timed retry to see if we can get 221 // this stored in the future. 222 s.recordRelease(r, true) 223 224 return res, nil 225 }