|
|
|
@ -21,6 +21,7 @@ import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
|
|
|
|
"strings"
|
|
|
|
|
"sync"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
@ -100,6 +101,13 @@ type Upgrade struct {
|
|
|
|
|
DisableOpenAPIValidation bool
|
|
|
|
|
// Get missing dependencies
|
|
|
|
|
DependencyUpdate bool
|
|
|
|
|
// Lock to control raceconditions when the process receives a SIGTERM
|
|
|
|
|
Lock sync.Mutex
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type resultMessage struct {
|
|
|
|
|
r *release.Release
|
|
|
|
|
e error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewUpgrade creates a new Upgrade object with the given configuration.
|
|
|
|
@ -109,8 +117,14 @@ func NewUpgrade(cfg *Configuration) *Upgrade {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Run executes the upgrade on the given release.
|
|
|
|
|
// Run executes the upgrade on the given release
|
|
|
|
|
func (u *Upgrade) Run(name string, chart *chart.Chart, vals map[string]interface{}) (*release.Release, error) {
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
return u.RunWithContext(ctx, name, chart, vals)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Run executes the upgrade on the given release with context.
|
|
|
|
|
func (u *Upgrade) RunWithContext(ctx context.Context, name string, chart *chart.Chart, vals map[string]interface{}) (*release.Release, error) {
|
|
|
|
|
if err := u.cfg.KubeClient.IsReachable(); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
@ -131,7 +145,7 @@ func (u *Upgrade) Run(name string, chart *chart.Chart, vals map[string]interface
|
|
|
|
|
u.cfg.Releases.MaxHistory = u.MaxHistory
|
|
|
|
|
|
|
|
|
|
u.cfg.Log("performing update for %s", name)
|
|
|
|
|
res, err := u.performUpgrade(currentRelease, upgradedRelease)
|
|
|
|
|
res, err := u.performUpgrade(ctx, currentRelease, upgradedRelease)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return res, err
|
|
|
|
|
}
|
|
|
|
@ -243,7 +257,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin
|
|
|
|
|
return currentRelease, upgradedRelease, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Release) (*release.Release, error) {
|
|
|
|
|
func (u *Upgrade) performUpgrade(ctx context.Context, originalRelease, upgradedRelease *release.Release) (*release.Release, error) {
|
|
|
|
|
current, err := u.cfg.KubeClient.Build(bytes.NewBufferString(originalRelease.Manifest), false)
|
|
|
|
|
if err != nil {
|
|
|
|
|
// Checking for removed Kubernetes API error so can provide a more informative error message to the user
|
|
|
|
@ -306,11 +320,43 @@ func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Relea
|
|
|
|
|
if err := u.cfg.Releases.Create(upgradedRelease); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
rChan := make(chan resultMessage)
|
|
|
|
|
go u.releasingUpgrade(rChan, upgradedRelease, current, target, originalRelease)
|
|
|
|
|
go u.handleContext(ctx, rChan, upgradedRelease)
|
|
|
|
|
result := <-rChan
|
|
|
|
|
|
|
|
|
|
return result.r, result.e
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Function used to lock the Mutex, this is important for the case when the atomic flag is set.
|
|
|
|
|
// In that case the upgrade will finish before the rollback is finished so it is necessary to wait for the rollback to finish.
|
|
|
|
|
// The rollback will be trigger by the function failRelease
|
|
|
|
|
func (u *Upgrade) reportToPerformUpgrade(c chan<- resultMessage, rel *release.Release, created kube.ResourceList, err error) {
|
|
|
|
|
u.Lock.Lock()
|
|
|
|
|
if err != nil {
|
|
|
|
|
rel, err = u.failRelease(rel, created, err)
|
|
|
|
|
}
|
|
|
|
|
c <- resultMessage{r: rel, e: err}
|
|
|
|
|
u.Lock.Unlock()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Setup listener for SIGINT and SIGTERM
|
|
|
|
|
func (u *Upgrade) handleContext(ctx context.Context, c chan<- resultMessage, upgradedRelease *release.Release) {
|
|
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
|
<-ctx.Done()
|
|
|
|
|
err := ctx.Err()
|
|
|
|
|
// when the atomic flag is set the ongoing release finish first and doesn't give time for the rollback happens.
|
|
|
|
|
u.reportToPerformUpgrade(c, upgradedRelease, kube.ResourceList{}, err)
|
|
|
|
|
}()
|
|
|
|
|
}
|
|
|
|
|
func (u *Upgrade) releasingUpgrade(c chan<- resultMessage, upgradedRelease *release.Release, current kube.ResourceList, target kube.ResourceList, originalRelease *release.Release) {
|
|
|
|
|
// pre-upgrade hooks
|
|
|
|
|
|
|
|
|
|
if !u.DisableHooks {
|
|
|
|
|
if err := u.cfg.execHook(upgradedRelease, release.HookPreUpgrade, u.Timeout); err != nil {
|
|
|
|
|
return u.failRelease(upgradedRelease, kube.ResourceList{}, fmt.Errorf("pre-upgrade hooks failed: %s", err))
|
|
|
|
|
u.reportToPerformUpgrade(c, upgradedRelease, kube.ResourceList{}, fmt.Errorf("pre-upgrade hooks failed: %s", err))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
u.cfg.Log("upgrade hooks disabled for %s", upgradedRelease.Name)
|
|
|
|
@ -319,7 +365,8 @@ func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Relea
|
|
|
|
|
results, err := u.cfg.KubeClient.Update(current, target, u.Force)
|
|
|
|
|
if err != nil {
|
|
|
|
|
u.cfg.recordRelease(originalRelease)
|
|
|
|
|
return u.failRelease(upgradedRelease, results.Created, err)
|
|
|
|
|
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if u.Recreate {
|
|
|
|
@ -336,12 +383,14 @@ func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Relea
|
|
|
|
|
if u.WaitForJobs {
|
|
|
|
|
if err := u.cfg.KubeClient.WaitWithJobs(target, u.Timeout); err != nil {
|
|
|
|
|
u.cfg.recordRelease(originalRelease)
|
|
|
|
|
return u.failRelease(upgradedRelease, results.Created, err)
|
|
|
|
|
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if err := u.cfg.KubeClient.Wait(target, u.Timeout); err != nil {
|
|
|
|
|
u.cfg.recordRelease(originalRelease)
|
|
|
|
|
return u.failRelease(upgradedRelease, results.Created, err)
|
|
|
|
|
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -349,7 +398,8 @@ func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Relea
|
|
|
|
|
// post-upgrade hooks
|
|
|
|
|
if !u.DisableHooks {
|
|
|
|
|
if err := u.cfg.execHook(upgradedRelease, release.HookPostUpgrade, u.Timeout); err != nil {
|
|
|
|
|
return u.failRelease(upgradedRelease, results.Created, fmt.Errorf("post-upgrade hooks failed: %s", err))
|
|
|
|
|
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, fmt.Errorf("post-upgrade hooks failed: %s", err))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -362,8 +412,7 @@ func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Relea
|
|
|
|
|
} else {
|
|
|
|
|
upgradedRelease.Info.Description = "Upgrade complete"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return upgradedRelease, nil
|
|
|
|
|
u.reportToPerformUpgrade(c, upgradedRelease, nil, nil)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (u *Upgrade) failRelease(rel *release.Release, created kube.ResourceList, err error) (*release.Release, error) {
|
|
|
|
|