@ -18,6 +18,7 @@ package tiller
import (
"fmt"
"strings"
ctx "golang.org/x/net/context"
@ -37,6 +38,10 @@ func (s *ReleaseServer) UpdateRelease(c ctx.Context, req *services.UpdateRelease
s . Log ( "preparing update for %s" , req . Name )
currentRelease , updatedRelease , err := s . prepareUpdate ( req )
if err != nil {
if req . Force {
// Use the --force, Luke.
return s . performUpdateForce ( req )
}
return nil , err
}
@ -137,6 +142,113 @@ func (s *ReleaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*rele
return currentRelease , updatedRelease , err
}
// performUpdateForce performs the same action as a `helm delete && helm install --replace`.
func ( s * ReleaseServer ) performUpdateForce ( req * services . UpdateReleaseRequest ) ( * services . UpdateReleaseResponse , error ) {
// find the last release with the given name
oldRelease , err := s . env . Releases . Last ( req . Name )
if err != nil {
return nil , err
}
newRelease , err := s . prepareRelease ( & services . InstallReleaseRequest {
Chart : req . Chart ,
Values : req . Values ,
DryRun : req . DryRun ,
Name : req . Name ,
DisableHooks : req . DisableHooks ,
Namespace : oldRelease . Namespace ,
ReuseName : true ,
Timeout : req . Timeout ,
Wait : req . Wait ,
} )
res := & services . UpdateReleaseResponse { Release : newRelease }
if err != nil {
s . Log ( "failed update prepare step: %s" , err )
// On dry run, append the manifest contents to a failed release. This is
// a stop-gap until we can revisit an error backchannel post-2.0.
if req . DryRun && strings . HasPrefix ( err . Error ( ) , "YAML parse error" ) {
err = fmt . Errorf ( "%s\n%s" , err , newRelease . Manifest )
}
return res , err
}
// From here on out, the release is considered to be in Status_DELETING or Status_DELETED
// state. There is no turning back.
oldRelease . Info . Status . Code = release . Status_DELETING
oldRelease . Info . Deleted = timeconv . Now ( )
oldRelease . Info . Description = "Deletion in progress (or silently failed)"
s . recordRelease ( oldRelease , true )
// pre-delete hooks
if ! req . DisableHooks {
if err := s . execHook ( oldRelease . Hooks , oldRelease . Name , oldRelease . Namespace , hooks . PreDelete , req . Timeout ) ; err != nil {
return res , err
}
} else {
s . Log ( "hooks disabled for %s" , req . Name )
}
// delete manifests from the old release
_ , errs := s . ReleaseModule . Delete ( oldRelease , nil , s . env )
oldRelease . Info . Status . Code = release . Status_DELETED
oldRelease . Info . Description = "Deletion complete"
s . recordRelease ( oldRelease , true )
if len ( errs ) > 0 {
es := make ( [ ] string , 0 , len ( errs ) )
for _ , e := range errs {
s . Log ( "error: %v" , e )
es = append ( es , e . Error ( ) )
}
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 , "; " ) )
}
// post-delete hooks
if ! req . DisableHooks {
if err := s . execHook ( oldRelease . Hooks , oldRelease . Name , oldRelease . Namespace , hooks . PostDelete , req . Timeout ) ; err != nil {
return res , err
}
}
// pre-install hooks
if ! req . DisableHooks {
if err := s . execHook ( newRelease . Hooks , newRelease . Name , newRelease . Namespace , hooks . PreInstall , req . Timeout ) ; err != nil {
return res , err
}
}
// update new release with next revision number so as to append to the old release's history
newRelease . Version = oldRelease . Version + 1
s . recordRelease ( newRelease , false )
if err := s . ReleaseModule . Update ( oldRelease , newRelease , req , s . env ) ; err != nil {
msg := fmt . Sprintf ( "Upgrade %q failed: %s" , newRelease . Name , err )
s . Log ( "warning: %s" , msg )
newRelease . Info . Status . Code = release . Status_FAILED
newRelease . Info . Description = msg
s . recordRelease ( newRelease , true )
return res , err
}
// post-install hooks
if ! req . DisableHooks {
if err := s . execHook ( newRelease . Hooks , newRelease . Name , newRelease . Namespace , hooks . PostInstall , req . Timeout ) ; err != nil {
msg := fmt . Sprintf ( "Release %q failed post-install: %s" , newRelease . Name , err )
s . Log ( "warning: %s" , msg )
newRelease . Info . Status . Code = release . Status_FAILED
newRelease . Info . Description = msg
s . recordRelease ( newRelease , true )
return res , err
}
}
newRelease . Info . Status . Code = release . Status_DEPLOYED
newRelease . Info . Description = "Upgrade complete"
s . recordRelease ( newRelease , true )
return res , nil
}
func ( s * ReleaseServer ) performUpdate ( originalRelease , updatedRelease * release . Release , req * services . UpdateReleaseRequest ) ( * services . UpdateReleaseResponse , error ) {
res := & services . UpdateReleaseResponse { Release : updatedRelease }