Factor out Helm actions into runner package
This commit is contained in:
parent
1eea1bdc04
commit
c526837d76
|
|
@ -16,6 +16,7 @@ RUN go mod download
|
|||
# copy source code
|
||||
COPY main.go main.go
|
||||
COPY controllers/ controllers/
|
||||
COPY internal/ internal/
|
||||
|
||||
# build without specifing the arch
|
||||
RUN CGO_ENABLED=0 go build -a -o helm-controller main.go
|
||||
|
|
|
|||
|
|
@ -31,8 +31,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
|
|
@ -43,7 +41,6 @@ import (
|
|||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/client-go/rest"
|
||||
kuberecorder "k8s.io/client-go/tools/record"
|
||||
"k8s.io/client-go/tools/reference"
|
||||
|
|
@ -58,6 +55,7 @@ import (
|
|||
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
|
||||
|
||||
v2 "github.com/fluxcd/helm-controller/api/v2alpha1"
|
||||
"github.com/fluxcd/helm-controller/internal/runner"
|
||||
)
|
||||
|
||||
// HelmReleaseReconciler reconciles a HelmRelease object
|
||||
|
|
@ -307,13 +305,13 @@ func (r *HelmReleaseReconciler) release(ctx context.Context, log logr.Logger, hr
|
|||
}
|
||||
|
||||
// Initialize config
|
||||
cfg, err := newActionCfg(log, r.Config, hr)
|
||||
run, err := runner.NewRunner(r.Config, hr.Namespace, r.Log)
|
||||
if err != nil {
|
||||
return v2.HelmReleaseNotReady(hr, v2.InitFailedReason, "failed to initialize Helm action configuration"), err
|
||||
}
|
||||
|
||||
// Determine last release revision.
|
||||
rel, observeLastReleaseErr := observeLastRelease(cfg, hr)
|
||||
rel, observeLastReleaseErr := run.ObserveLastRelease(hr)
|
||||
if observeLastReleaseErr != nil {
|
||||
return v2.HelmReleaseNotReady(hr, v2.GetLastReleaseFailedReason, "failed to get last release revision"), err
|
||||
}
|
||||
|
|
@ -365,11 +363,11 @@ func (r *HelmReleaseReconciler) release(ctx context.Context, log logr.Logger, hr
|
|||
var deployAction v2.DeploymentAction
|
||||
if rel == nil {
|
||||
deployAction = hr.Spec.GetInstall()
|
||||
rel, err = install(cfg, loadedChart, hr, values)
|
||||
rel, err = run.Install(hr, loadedChart, values)
|
||||
err = r.handleHelmActionResult(&hr, revision, err, deployAction.GetDescription(), v2.ReleasedCondition, v2.InstallSucceededReason, v2.InstallFailedReason)
|
||||
} else {
|
||||
deployAction = hr.Spec.GetUpgrade()
|
||||
rel, err = upgrade(cfg, loadedChart, hr, values)
|
||||
rel, err = run.Upgrade(hr, loadedChart, values)
|
||||
err = r.handleHelmActionResult(&hr, revision, err, deployAction.GetDescription(), v2.ReleasedCondition, v2.UpgradeSucceededReason, v2.UpgradeFailedReason)
|
||||
}
|
||||
remediation := deployAction.GetRemediation()
|
||||
|
|
@ -381,7 +379,7 @@ func (r *HelmReleaseReconciler) release(ctx context.Context, log logr.Logger, hr
|
|||
|
||||
// If new release revision is successful and tests are enabled, run them.
|
||||
if err == nil && hr.Spec.GetTest().Enable {
|
||||
_, testErr := test(cfg, hr)
|
||||
_, testErr := run.Test(hr)
|
||||
testErr = r.handleHelmActionResult(&hr, revision, testErr, "test", v2.TestSuccessCondition, v2.TestSucceededReason, v2.TestFailedReason)
|
||||
|
||||
// Propagate any test error if not marked ignored.
|
||||
|
|
@ -404,10 +402,10 @@ func (r *HelmReleaseReconciler) release(ctx context.Context, log logr.Logger, hr
|
|||
var remediationErr error
|
||||
switch remediation.GetStrategy() {
|
||||
case v2.RollbackRemediationStrategy:
|
||||
rollbackErr := rollback(cfg, hr)
|
||||
rollbackErr := run.Rollback(hr)
|
||||
remediationErr = r.handleHelmActionResult(&hr, revision, rollbackErr, "rollback", v2.RemediatedCondition, v2.RollbackSucceededReason, v2.RollbackFailedReason)
|
||||
case v2.UninstallRemediationStrategy:
|
||||
uninstallErr := uninstall(cfg, hr)
|
||||
uninstallErr := run.Uninstall(hr)
|
||||
remediationErr = r.handleHelmActionResult(&hr, revision, uninstallErr, "uninstall", v2.RemediatedCondition, v2.UninstallSucceededReason, v2.UninstallFailedReason)
|
||||
}
|
||||
|
||||
|
|
@ -417,7 +415,7 @@ func (r *HelmReleaseReconciler) release(ctx context.Context, log logr.Logger, hr
|
|||
}
|
||||
|
||||
// Determine release after remediation.
|
||||
rel, observeLastReleaseErr = observeLastRelease(cfg, hr)
|
||||
rel, observeLastReleaseErr = run.ObserveLastRelease(hr)
|
||||
if observeLastReleaseErr != nil {
|
||||
err = &ConditionError{
|
||||
Reason: v2.GetLastReleaseFailedReason,
|
||||
|
|
@ -491,16 +489,16 @@ func (r *HelmReleaseReconciler) gc(ctx context.Context, log logr.Logger, hr v2.H
|
|||
// Uninstall the Helm release
|
||||
var uninstallErr error
|
||||
if !hr.Spec.Suspend {
|
||||
cfg, err := newActionCfg(log, r.Config, hr)
|
||||
run, err := runner.NewRunner(r.Config, hr.Namespace, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = cfg.Releases.Deployed(hr.GetReleaseName())
|
||||
_, err = run.Config.Releases.Deployed(hr.GetReleaseName())
|
||||
switch {
|
||||
case errors.Is(err, driver.ErrNoDeployedReleases):
|
||||
// noop
|
||||
case err == nil:
|
||||
uninstallErr = uninstall(cfg, hr)
|
||||
uninstallErr = run.Uninstall(hr)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
|
|
@ -657,15 +655,6 @@ func helmChartRequiresUpdate(hr v2.HelmRelease, chart sourcev1.HelmChart) bool {
|
|||
}
|
||||
}
|
||||
|
||||
// observeLastRelease observes the last revision, if there is one, for for actual helm release associated with the given HelmRelease.
|
||||
func observeLastRelease(cfg *action.Configuration, hr v2.HelmRelease) (*release.Release, error) {
|
||||
rel, err := cfg.Releases.Last(hr.GetReleaseName())
|
||||
if err != nil && errors.Is(err, driver.ErrReleaseNotFound) {
|
||||
err = nil
|
||||
}
|
||||
return rel, err
|
||||
}
|
||||
|
||||
// getReleaseRevision returns the revision of the given release.Release.
|
||||
func getReleaseRevision(rel *release.Release) int {
|
||||
if rel == nil {
|
||||
|
|
@ -674,65 +663,6 @@ func getReleaseRevision(rel *release.Release) int {
|
|||
return rel.Version
|
||||
}
|
||||
|
||||
func install(cfg *action.Configuration, chart *chart.Chart, hr v2.HelmRelease, values chartutil.Values) (*release.Release, error) {
|
||||
install := action.NewInstall(cfg)
|
||||
install.ReleaseName = hr.GetReleaseName()
|
||||
install.Namespace = hr.GetReleaseNamespace()
|
||||
install.Timeout = hr.Spec.GetInstall().GetTimeout(hr.GetTimeout()).Duration
|
||||
install.Wait = !hr.Spec.GetInstall().DisableWait
|
||||
install.DisableHooks = hr.Spec.GetInstall().DisableHooks
|
||||
install.DisableOpenAPIValidation = hr.Spec.GetInstall().DisableOpenAPIValidation
|
||||
install.Replace = hr.Spec.GetInstall().Replace
|
||||
install.SkipCRDs = hr.Spec.GetInstall().SkipCRDs
|
||||
|
||||
return install.Run(chart, values.AsMap())
|
||||
}
|
||||
|
||||
func upgrade(cfg *action.Configuration, chart *chart.Chart, hr v2.HelmRelease, values chartutil.Values) (*release.Release, error) {
|
||||
upgrade := action.NewUpgrade(cfg)
|
||||
upgrade.Namespace = hr.GetReleaseNamespace()
|
||||
upgrade.ResetValues = !hr.Spec.GetUpgrade().PreserveValues
|
||||
upgrade.ReuseValues = hr.Spec.GetUpgrade().PreserveValues
|
||||
upgrade.MaxHistory = hr.GetMaxHistory()
|
||||
upgrade.Timeout = hr.Spec.GetUpgrade().GetTimeout(hr.GetTimeout()).Duration
|
||||
upgrade.Wait = !hr.Spec.GetUpgrade().DisableWait
|
||||
upgrade.DisableHooks = hr.Spec.GetUpgrade().DisableHooks
|
||||
upgrade.Force = hr.Spec.GetUpgrade().Force
|
||||
upgrade.CleanupOnFail = hr.Spec.GetUpgrade().CleanupOnFail
|
||||
|
||||
return upgrade.Run(hr.GetReleaseName(), chart, values.AsMap())
|
||||
}
|
||||
|
||||
func test(cfg *action.Configuration, hr v2.HelmRelease) (*release.Release, error) {
|
||||
test := action.NewReleaseTesting(cfg)
|
||||
test.Namespace = hr.GetReleaseNamespace()
|
||||
test.Timeout = hr.Spec.GetTest().GetTimeout(hr.GetTimeout()).Duration
|
||||
|
||||
return test.Run(hr.GetReleaseName())
|
||||
}
|
||||
|
||||
func rollback(cfg *action.Configuration, hr v2.HelmRelease) error {
|
||||
rollback := action.NewRollback(cfg)
|
||||
rollback.Timeout = hr.Spec.GetRollback().GetTimeout(hr.GetTimeout()).Duration
|
||||
rollback.Wait = !hr.Spec.GetRollback().DisableWait
|
||||
rollback.DisableHooks = hr.Spec.GetRollback().DisableHooks
|
||||
rollback.Force = hr.Spec.GetRollback().Force
|
||||
rollback.Recreate = hr.Spec.GetRollback().Recreate
|
||||
rollback.CleanupOnFail = hr.Spec.GetRollback().CleanupOnFail
|
||||
|
||||
return rollback.Run(hr.GetReleaseName())
|
||||
}
|
||||
|
||||
func uninstall(cfg *action.Configuration, hr v2.HelmRelease) error {
|
||||
uninstall := action.NewUninstall(cfg)
|
||||
uninstall.Timeout = hr.Spec.GetUninstall().GetTimeout(hr.GetTimeout()).Duration
|
||||
uninstall.DisableHooks = hr.Spec.GetUninstall().DisableHooks
|
||||
uninstall.KeepHistory = hr.Spec.GetUninstall().KeepHistory
|
||||
|
||||
_, err := uninstall.Run(hr.GetReleaseName())
|
||||
return err
|
||||
}
|
||||
|
||||
func lock(name string) (unlock func(), err error) {
|
||||
lockFile := path.Join(os.TempDir(), name+".lock")
|
||||
mutex := lockedfile.MutexAt(lockFile)
|
||||
|
|
@ -764,24 +694,6 @@ func download(url, tmpDir string) (string, error) {
|
|||
return fp, nil
|
||||
}
|
||||
|
||||
func newActionCfg(log logr.Logger, clusterCfg *rest.Config, hr v2.HelmRelease) (*action.Configuration, error) {
|
||||
cfg := new(action.Configuration)
|
||||
ns := hr.GetReleaseNamespace()
|
||||
err := cfg.Init(&genericclioptions.ConfigFlags{
|
||||
Namespace: &ns,
|
||||
APIServer: &clusterCfg.Host,
|
||||
CAFile: &clusterCfg.CAFile,
|
||||
BearerToken: &clusterCfg.BearerToken,
|
||||
}, hr.Namespace, "secret", actionLogger(log))
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
func actionLogger(logger logr.Logger) func(format string, v ...interface{}) {
|
||||
return func(format string, v ...interface{}) {
|
||||
logger.Info(fmt.Sprintf(format, v...))
|
||||
}
|
||||
}
|
||||
|
||||
// mergeMaps merges map b into given map a and returns the result.
|
||||
// It allows overwrites of map values with flat values, and vice versa.
|
||||
// This is copied from https://github.com/helm/helm/blob/v3.3.0/pkg/cli/values/options.go#L88,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package runner
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/storage/driver"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
v2 "github.com/fluxcd/helm-controller/api/v2alpha1"
|
||||
)
|
||||
|
||||
// Runner represents a Helm action runner capable of performing Helm
|
||||
// operations for a v2alpha1.HelmRelease.
|
||||
type Runner struct {
|
||||
Config *action.Configuration
|
||||
}
|
||||
|
||||
// NewRunner constructs a new Runner configured to run Helm actions with the
|
||||
// given rest.Config, and the storage namespace configured to the provided
|
||||
// namespace.
|
||||
func NewRunner(clusterCfg *rest.Config, namespace string, logger logr.Logger) (*Runner, error) {
|
||||
cfg := new(action.Configuration)
|
||||
if err := cfg.Init(&genericclioptions.ConfigFlags{
|
||||
APIServer: &clusterCfg.Host,
|
||||
CAFile: &clusterCfg.CAFile,
|
||||
BearerToken: &clusterCfg.BearerToken,
|
||||
}, namespace, "secret", debugLogger(logger)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Runner{Config: cfg}, nil
|
||||
}
|
||||
|
||||
// Install runs an Helm install action for the given v2alpha1.HelmRelease.
|
||||
func (r *Runner) Install(hr v2.HelmRelease, chart *chart.Chart, values chartutil.Values) (*release.Release, error) {
|
||||
install := action.NewInstall(r.Config)
|
||||
install.ReleaseName = hr.GetReleaseName()
|
||||
install.Namespace = hr.GetReleaseNamespace()
|
||||
install.Timeout = hr.Spec.GetInstall().GetTimeout(hr.GetTimeout()).Duration
|
||||
install.Wait = !hr.Spec.GetInstall().DisableWait
|
||||
install.DisableHooks = hr.Spec.GetInstall().DisableHooks
|
||||
install.DisableOpenAPIValidation = hr.Spec.GetInstall().DisableOpenAPIValidation
|
||||
install.Replace = hr.Spec.GetInstall().Replace
|
||||
install.SkipCRDs = hr.Spec.GetInstall().SkipCRDs
|
||||
|
||||
return install.Run(chart, values.AsMap())
|
||||
}
|
||||
|
||||
// Upgrade runs an Helm upgrade action for the given v2alpha1.HelmRelease.
|
||||
func (r *Runner) Upgrade(hr v2.HelmRelease, chart *chart.Chart, values chartutil.Values) (*release.Release, error) {
|
||||
upgrade := action.NewUpgrade(r.Config)
|
||||
upgrade.Namespace = hr.GetReleaseNamespace()
|
||||
upgrade.ResetValues = !hr.Spec.GetUpgrade().PreserveValues
|
||||
upgrade.ReuseValues = hr.Spec.GetUpgrade().PreserveValues
|
||||
upgrade.MaxHistory = hr.GetMaxHistory()
|
||||
upgrade.Timeout = hr.Spec.GetUpgrade().GetTimeout(hr.GetTimeout()).Duration
|
||||
upgrade.Wait = !hr.Spec.GetUpgrade().DisableWait
|
||||
upgrade.DisableHooks = hr.Spec.GetUpgrade().DisableHooks
|
||||
upgrade.Force = hr.Spec.GetUpgrade().Force
|
||||
upgrade.CleanupOnFail = hr.Spec.GetUpgrade().CleanupOnFail
|
||||
|
||||
return upgrade.Run(hr.GetReleaseName(), chart, values.AsMap())
|
||||
}
|
||||
|
||||
// Test runs an Helm test action for the given v2alpha1.HelmRelease.
|
||||
func (r *Runner) Test(hr v2.HelmRelease) (*release.Release, error) {
|
||||
test := action.NewReleaseTesting(r.Config)
|
||||
test.Namespace = hr.GetReleaseNamespace()
|
||||
test.Timeout = hr.Spec.GetTest().GetTimeout(hr.GetTimeout()).Duration
|
||||
|
||||
return test.Run(hr.GetReleaseName())
|
||||
}
|
||||
|
||||
// Rollback runs an Helm rollback action for the given v2alpha1.HelmRelease.
|
||||
func (r *Runner) Rollback(hr v2.HelmRelease) error {
|
||||
rollback := action.NewRollback(r.Config)
|
||||
rollback.Timeout = hr.Spec.GetRollback().GetTimeout(hr.GetTimeout()).Duration
|
||||
rollback.Wait = !hr.Spec.GetRollback().DisableWait
|
||||
rollback.DisableHooks = hr.Spec.GetRollback().DisableHooks
|
||||
rollback.Force = hr.Spec.GetRollback().Force
|
||||
rollback.Recreate = hr.Spec.GetRollback().Recreate
|
||||
rollback.CleanupOnFail = hr.Spec.GetRollback().CleanupOnFail
|
||||
|
||||
return rollback.Run(hr.GetReleaseName())
|
||||
}
|
||||
|
||||
// Uninstall runs an Helm uninstall action for the given v2alpha1.HelmRelease.
|
||||
func (r *Runner) Uninstall(hr v2.HelmRelease) error {
|
||||
uninstall := action.NewUninstall(r.Config)
|
||||
uninstall.Timeout = hr.Spec.GetUninstall().GetTimeout(hr.GetTimeout()).Duration
|
||||
uninstall.DisableHooks = hr.Spec.GetUninstall().DisableHooks
|
||||
uninstall.KeepHistory = hr.Spec.GetUninstall().KeepHistory
|
||||
|
||||
_, err := uninstall.Run(hr.GetReleaseName())
|
||||
return err
|
||||
}
|
||||
|
||||
// ObserveLastRelease observes the last revision, if there is one,
|
||||
// for the actual Helm release associated with the given v2alpha1.HelmRelease.
|
||||
func (r *Runner) ObserveLastRelease(hr v2.HelmRelease) (*release.Release, error) {
|
||||
rel, err := r.Config.Releases.Last(hr.GetReleaseName())
|
||||
if err != nil && errors.Is(err, driver.ErrReleaseNotFound) {
|
||||
err = nil
|
||||
}
|
||||
return rel, err
|
||||
}
|
||||
|
||||
func debugLogger(logger logr.Logger) func(format string, v ...interface{}) {
|
||||
return func(format string, v ...interface{}) {
|
||||
logger.V(1).Info(fmt.Sprintf(format, v...))
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue