github.com/helmwave/helmwave@v0.36.4-0.20240509190856-b35563eba4c6/pkg/plan/rollback.go (about) 1 package plan 2 3 import ( 4 "context" 5 "errors" 6 "time" 7 8 "github.com/helmwave/helmwave/pkg/helper" 9 "github.com/helmwave/helmwave/pkg/kubedog" 10 "github.com/helmwave/helmwave/pkg/parallel" 11 "github.com/helmwave/helmwave/pkg/release" 12 log "github.com/sirupsen/logrus" 13 "github.com/werf/kubedog/pkg/kube" 14 "github.com/werf/kubedog/pkg/tracker" 15 "github.com/werf/kubedog/pkg/trackers/rollout/multitrack" 16 ) 17 18 // Rollback rollbacks helm release. 19 func (p *Plan) Rollback(ctx context.Context, version int, dog *kubedog.Config) (err error) { 20 // Run hooks 21 err = p.body.Lifecycle.RunPreRollback(ctx) 22 if err != nil { 23 return 24 } 25 26 defer func() { 27 lifecycleErr := p.body.Lifecycle.RunPostRollback(ctx) 28 if lifecycleErr != nil { 29 log.Errorf("got an error from postrollback hooks: %v", lifecycleErr) 30 if err == nil { 31 err = lifecycleErr 32 } 33 } 34 }() 35 36 if dog.Enabled { 37 log.Warn("🐶 kubedog is enabled") 38 kubedog.FixLog(ctx, dog.LogWidth) 39 err = p.rollbackReleasesKubedog(ctx, version, dog) 40 } else { 41 err = p.rollbackReleases(ctx, version) 42 } 43 44 return 45 } 46 47 func (p *Plan) rollbackReleases(ctx context.Context, version int) error { 48 wg := parallel.NewWaitGroup() 49 wg.Add(len(p.body.Releases)) 50 51 for i := range p.body.Releases { 52 go func(wg *parallel.WaitGroup, rel release.Config) { 53 defer wg.Done() 54 err := rel.Rollback(ctx, version) 55 if err != nil { 56 rel.Logger().WithError(err).Error("❌ rollback") 57 wg.ErrChan() <- err 58 } else { 59 rel.Logger().Info("✅ rollback!") 60 } 61 }(wg, p.body.Releases[i]) 62 } 63 64 return wg.Wait() 65 } 66 67 func (p *Plan) rollbackReleasesKubedog(ctx context.Context, version int, kubedogConfig *kubedog.Config) error { 68 ctxCancel, cancel := context.WithCancel(ctx) 69 defer cancel() // Don't forget! 70 71 specs, kubecontext, err := p.kubedogRollbackSpecs(version, kubedogConfig) 72 if err != nil { 73 return err 74 } 75 76 err = helper.KubeInit(kubecontext) 77 if err != nil { 78 return err 79 } 80 81 opts := multitrack.MultitrackOptions{ 82 DynamicClient: kube.DynamicClient, 83 DiscoveryClient: kube.CachedDiscoveryClient, 84 Mapper: kube.Mapper, 85 StatusProgressPeriod: kubedogConfig.StatusInterval, 86 Options: tracker.Options{ 87 ParentContext: ctxCancel, 88 Timeout: kubedogConfig.Timeout, 89 LogsFromTime: time.Now(), 90 }, 91 } 92 93 // Run kubedog 94 dogroup := parallel.NewWaitGroup() 95 dogroup.Add(1) 96 go func() { 97 defer dogroup.Done() 98 log.Trace("Multitrack is starting...") 99 dogroup.ErrChan() <- multitrack.Multitrack(kube.Client, specs, opts) 100 }() 101 102 // Run helm 103 time.Sleep(kubedogConfig.StartDelay) 104 err = p.rollbackReleases(ctx, version) 105 if err != nil { 106 cancel() 107 108 return err 109 } 110 111 // Allow kubedog to catch release installed 112 time.Sleep(kubedogConfig.StatusInterval) 113 cancel() // stop kubedog 114 115 err = dogroup.WaitWithContext(ctx) 116 if err != nil && !errors.Is(err, context.Canceled) { 117 // Ignore kubedog error 118 log.WithError(err).Warn("kubedog caught error while watching resources.") 119 } 120 121 return nil 122 } 123 124 func (p *Plan) kubedogRollbackSpecs( 125 version int, 126 kubedogConfig *kubedog.Config, 127 ) (multitrack.MultitrackSpecs, string, error) { 128 return p.kubedogSpecs(kubedogConfig, func(rel release.Config) (string, error) { 129 return p.kubedogRollbackManifest(version, rel) 130 }) 131 } 132 133 func (p *Plan) kubedogRollbackManifest(version int, rel release.Config) (string, error) { 134 r, err := rel.Get(version) 135 if err != nil { 136 return "", err 137 } 138 139 return r.Manifest, nil 140 }