github.com/helmwave/helmwave@v0.36.4-0.20240509190856-b35563eba4c6/pkg/release/upgrade.go (about) 1 package release 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "strings" 8 9 "github.com/helmwave/helmwave/pkg/helper" 10 "helm.sh/helm/v3/pkg/chart" 11 "helm.sh/helm/v3/pkg/cli/values" 12 "helm.sh/helm/v3/pkg/getter" 13 "helm.sh/helm/v3/pkg/release" 14 ) 15 16 // Helm wraps a lot of meta.NoKindMatchError into fmt.Errorf which makes errors.Is unusable. 17 // So we have to find this substring in error string. 18 const errMissingCRD = "unable to build kubernetes objects from release manifest:" 19 20 func (rel *config) upgrade(ctx context.Context) (*release.Release, error) { 21 ch, err := rel.GetChart() 22 if err != nil { 23 return nil, err 24 } 25 26 // Values 27 valuesFiles := helper.SlicesMap(rel.Values(), func(v ValuesReference) string { 28 return v.Dst 29 }) 30 31 valOpts := &values.Options{ValueFiles: valuesFiles} 32 vals, err := valOpts.MergeValues(getter.All(rel.Helm())) 33 if err != nil { 34 return nil, fmt.Errorf("failed to merge values %v: %w", valuesFiles, err) 35 } 36 37 // Install or Template 38 if rel.dryRun { 39 rel.Logger().Debug("I'll dry-run.") 40 r, err := rel.installWithRetry(ctx, ch, vals) 41 if err != nil { 42 return nil, fmt.Errorf("failed with dry-run %q: %w", rel.Uniq(), err) 43 } 44 45 return r, nil 46 } else if !rel.dryRun && !rel.isInstalled() { 47 rel.Logger().Debug("🧐 Release does not exist. Installing it now.") 48 r, err := rel.installWithRetry(ctx, ch, vals) 49 if err != nil { 50 return nil, fmt.Errorf("failed to install %q: %w", rel.Uniq(), err) 51 } 52 53 return r, nil 54 } 55 56 pending, err := rel.isPending() 57 if err != nil { 58 return nil, fmt.Errorf("failed to check %q for pending status: %w", rel.Uniq(), err) 59 } 60 if pending { 61 err := rel.fixPending(ctx) 62 if err != nil { 63 return nil, fmt.Errorf("failed to fix %q pending status: %w", rel.Uniq(), err) 64 } 65 } 66 67 // Upgrade 68 r, err := rel.upgradeWithRetry(ctx, ch, vals) 69 if err != nil { 70 return nil, fmt.Errorf("failed to upgrade %s: %w", rel.Uniq(), err) 71 } 72 73 return r, nil 74 } 75 76 //nolint:wrapcheck // we wrap it later 77 func (rel *config) installWithRetry( 78 ctx context.Context, 79 ch *chart.Chart, 80 vals map[string]interface{}, 81 ) (*release.Release, error) { 82 r, err := rel.newInstall().RunWithContext(ctx, ch, vals) 83 84 if err != nil && strings.Contains(err.Error(), errMissingCRD) && rel.dryRun { 85 er := rel.forceOfflineKubeVersion() 86 // return original error if we can't get kubernetes version 87 if er != nil { 88 return r, err 89 } 90 91 return rel.newInstall().RunWithContext(ctx, ch, vals) 92 } 93 94 return r, err 95 } 96 97 //nolint:wrapcheck // we wrap it later 98 func (rel *config) upgradeWithRetry( 99 ctx context.Context, 100 ch *chart.Chart, 101 vals map[string]interface{}, 102 ) (*release.Release, error) { 103 r, err := rel.newUpgrade().RunWithContext(ctx, rel.Name(), ch, vals) 104 105 if err != nil && strings.Contains(err.Error(), errMissingCRD) && rel.dryRun { 106 er := rel.forceOfflineKubeVersion() 107 // return original error if we can't get kubernetes version 108 if er != nil { 109 return r, err 110 } 111 112 return rel.newUpgrade().RunWithContext(ctx, rel.Name(), ch, vals) 113 } 114 115 return r, err 116 } 117 118 func (rel *config) forceOfflineKubeVersion() error { 119 rel.Logger().Warn("🤔hmm, it looks like some required CRDs are not installed, setting offline_kube_version and trying again") 120 121 v, err := helper.GetKubernetesVersion(rel.Cfg()) 122 if err != nil { 123 rel.Logger().WithError(err).Error("cannot get current kubernetes version, you need to set it manually") 124 125 return err 126 } 127 128 rel.OfflineKubeVersionF = v.GitVersion 129 rel.Logger().WithField("version", rel.OfflineKubeVersionF).Info("discovered kubernetes version") 130 131 return nil 132 } 133 134 func (rel *config) test() error { 135 rel.Logger().Info("running helm tests") 136 137 client := rel.newTest() 138 r, err := client.Run(rel.Name()) 139 140 if (err != nil) || rel.Tests.ForceShowLogs { 141 var buf bytes.Buffer 142 _ = client.GetPodLogs(&buf, r) 143 144 if err != nil { 145 rel.Logger().WithError(err).WithField("output", buf.String()).Error("helm tests failed") 146 147 return NewHelmTestsError(err) 148 } 149 150 rel.Logger().WithField("output", buf.String()).Info("helm tests output") 151 } 152 153 return nil 154 }