github.com/zoumo/helm@v2.5.0+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_UNKNOWN}, 120 Description: "Initial install underway", // Will be overwritten. 121 }, 122 Manifest: manifestDoc.String(), 123 Hooks: hooks, 124 Version: int32(revision), 125 Annotations: req.Annotations, 126 } 127 if len(notesTxt) > 0 { 128 rel.Info.Status.Notes = notesTxt 129 } 130 131 err = validateManifest(s.env.KubeClient, req.Namespace, manifestDoc.Bytes()) 132 return rel, err 133 } 134 135 // performRelease runs a release. 136 func (s *ReleaseServer) performRelease(r *release.Release, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) { 137 res := &services.InstallReleaseResponse{Release: r} 138 139 if req.DryRun { 140 s.Log("dry run for %s", r.Name) 141 res.Release.Info.Description = "Dry run complete" 142 return res, nil 143 } 144 145 // pre-install hooks 146 if !req.DisableHooks { 147 if err := s.execHook(r.Hooks, r.Name, r.Namespace, hooks.PreInstall, req.Timeout); err != nil { 148 return res, err 149 } 150 } else { 151 s.Log("install hooks disabled for %s", req.Name) 152 } 153 154 switch h, err := s.env.Releases.History(req.Name); { 155 // if this is a replace operation, append to the release history 156 case req.ReuseName && err == nil && len(h) >= 1: 157 s.Log("name reuse for %s requested, replacing release", req.Name) 158 // get latest release revision 159 relutil.Reverse(h, relutil.SortByRevision) 160 161 // old release 162 old := h[0] 163 164 // update old release status 165 old.Info.Status.Code = release.Status_SUPERSEDED 166 s.recordRelease(old, true) 167 168 // update new release with next revision number 169 // so as to append to the old release's history 170 r.Version = old.Version + 1 171 updateReq := &services.UpdateReleaseRequest{ 172 Wait: req.Wait, 173 Recreate: false, 174 Timeout: req.Timeout, 175 } 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, false) 184 return res, err 185 } 186 187 default: 188 // nothing to replace, create as normal 189 // regular manifests 190 if err := s.ReleaseModule.Create(r, req, s.env); err != nil { 191 msg := fmt.Sprintf("Release %q failed: %s", r.Name, err) 192 s.Log("warning: %s", msg) 193 r.Info.Status.Code = release.Status_FAILED 194 r.Info.Description = msg 195 s.recordRelease(r, false) 196 return res, fmt.Errorf("release %s failed: %s", r.Name, err) 197 } 198 } 199 200 // post-install hooks 201 if !req.DisableHooks { 202 if err := s.execHook(r.Hooks, r.Name, r.Namespace, hooks.PostInstall, req.Timeout); err != nil { 203 msg := fmt.Sprintf("Release %q failed post-install: %s", r.Name, err) 204 s.Log("warning: %s", msg) 205 r.Info.Status.Code = release.Status_FAILED 206 r.Info.Description = msg 207 s.recordRelease(r, false) 208 return res, err 209 } 210 } 211 212 r.Info.Status.Code = release.Status_DEPLOYED 213 r.Info.Description = "Install complete" 214 // This is a tricky case. The release has been created, but the result 215 // cannot be recorded. The truest thing to tell the user is that the 216 // release was created. However, the user will not be able to do anything 217 // further with this release. 218 // 219 // One possible strategy would be to do a timed retry to see if we can get 220 // this stored in the future. 221 s.recordRelease(r, false) 222 223 return res, nil 224 }