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  }