Upgrade k8s package verison (#5358)

* upgrade k8s package version

Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io>

* Script upgrade and code formatting.

Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io>

Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io>
This commit is contained in:
hongzhouzi
2022-11-15 14:56:38 +08:00
committed by GitHub
parent 5f91c1663a
commit 44167aa47a
3106 changed files with 321340 additions and 172080 deletions

View File

@@ -72,6 +72,7 @@ func SupportsOwnerReference(restMapper meta.RESTMapper, owner, dependent client.
ownerClusterScoped := ownerMapping.Scope.Name() == meta.RESTScopeNameRoot
ownerNamespace := owner.GetNamespace()
depClusterScoped := depMapping.Scope.Name() == meta.RESTScopeNameRoot
depNamespace := dependent.GetNamespace()
if ownerClusterScoped {

View File

@@ -44,7 +44,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
"github.com/operator-framework/helm-operator-plugins/pkg/internal/sdk/controllerutil"
"github.com/operator-framework/helm-operator-plugins/internal/sdk/controllerutil"
"github.com/operator-framework/helm-operator-plugins/pkg/manifestutil"
)
@@ -140,7 +140,7 @@ func (c *actionClient) Install(name, namespace string, chrt *chart.Chart, vals m
// Only return an error about a rollback failure if the failure was
// caused by something other than the release not being found.
_, uninstallErr := c.Uninstall(name)
if !errors.Is(uninstallErr, driver.ErrReleaseNotFound) {
if uninstallErr != nil && !errors.Is(uninstallErr, driver.ErrReleaseNotFound) {
return nil, fmt.Errorf("uninstall failed: %v: original install error: %w", uninstallErr, err)
}
}
@@ -163,6 +163,7 @@ func (c *actionClient) Upgrade(name, namespace string, chrt *chart.Chart, vals m
if rel != nil {
rollback := action.NewRollback(c.conf)
rollback.Force = true
rollback.MaxHistory = upgrade.MaxHistory
// As of Helm 2.13, if Upgrade returns a non-nil release, that
// means the release was also recorded in the release store.
@@ -263,9 +264,17 @@ func createPatch(existing runtime.Object, expected *resource.Info) ([]byte, apit
if err != nil {
return nil, apitypes.StrategicMergePatchType, err
}
patch, err := strategicpatch.CreateThreeWayMergePatch(expectedJSON, expectedJSON, existingJSON, patchMeta, true)
return patch, apitypes.StrategicMergePatchType, err
if err != nil {
return nil, apitypes.StrategicMergePatchType, err
}
// An empty patch could be in the form of "{}" which represents an empty map out of the 3-way merge;
// filter them out here too to avoid sending the apiserver empty patch requests.
if len(patch) == 0 || bytes.Equal(patch, []byte("{}")) {
return nil, apitypes.StrategicMergePatchType, nil
}
return patch, apitypes.StrategicMergePatchType, nil
}
func createJSONMergePatch(existingJSON, expectedJSON []byte) ([]byte, error) {

View File

@@ -60,7 +60,7 @@ func (acg *actionConfigGetter) ActionConfigFor(obj client.Object) (*action.Confi
// Setup the debug log function that Helm will use
debugLog := func(format string, v ...interface{}) {
if acg.log != nil {
if acg.log.GetSink() != nil {
acg.log.V(1).Info(fmt.Sprintf(format, v...))
}
}

View File

@@ -22,7 +22,7 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kubeclock "k8s.io/apimachinery/pkg/util/clock"
kubeclock "k8s.io/utils/clock"
)
// clock is used to set status condition timestamps.

View File

@@ -21,7 +21,7 @@ import (
corev1 "k8s.io/api/core/v1"
"github.com/operator-framework/helm-operator-plugins/pkg/internal/sdk/status"
"github.com/operator-framework/helm-operator-plugins/pkg/internal/status"
)
const (

View File

@@ -0,0 +1,61 @@
// Copyright 2022 The Operator-SDK Authors
//
// 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 diff
import (
"bytes"
"regexp"
"strings"
"github.com/sergi/go-diff/diffmatchpatch"
)
// Generate generates a diff between a and b, in color.
func Generate(a, b string) string {
dmp := diffmatchpatch.New()
wSrc, wDst, warray := dmp.DiffLinesToRunes(a, b)
diffs := dmp.DiffMainRunes(wSrc, wDst, false)
diffs = dmp.DiffCharsToLines(diffs, warray)
var buff bytes.Buffer
for _, diff := range diffs {
text := diff.Text
switch diff.Type {
case diffmatchpatch.DiffInsert:
_, _ = buff.WriteString("\x1b[32m")
_, _ = buff.WriteString(prefixLines(text, "+"))
_, _ = buff.WriteString("\x1b[0m")
case diffmatchpatch.DiffDelete:
_, _ = buff.WriteString("\x1b[31m")
_, _ = buff.WriteString(prefixLines(text, "-"))
_, _ = buff.WriteString("\x1b[0m")
case diffmatchpatch.DiffEqual:
_, _ = buff.WriteString(prefixLines(text, " "))
}
}
return buff.String()
}
func prefixLines(s, prefix string) string {
var buf bytes.Buffer
lines := strings.Split(s, "\n")
ls := regexp.MustCompile("^")
for _, line := range lines[:len(lines)-1] {
buf.WriteString(ls.ReplaceAllString(line, prefix))
buf.WriteString("\n")
}
return buf.String()
}

View File

@@ -25,15 +25,16 @@ import (
"helm.sh/helm/v3/pkg/releaseutil"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/source"
"sigs.k8s.io/yaml"
"github.com/operator-framework/helm-operator-plugins/internal/sdk/controllerutil"
"github.com/operator-framework/helm-operator-plugins/pkg/hook"
"github.com/operator-framework/helm-operator-plugins/pkg/internal/sdk/controllerutil"
"github.com/operator-framework/helm-operator-plugins/pkg/internal/sdk/predicate"
"github.com/operator-framework/helm-operator-plugins/pkg/internal/predicate"
"github.com/operator-framework/helm-operator-plugins/pkg/manifestutil"
)
@@ -69,32 +70,65 @@ func (d *dependentResourceWatcher) Exec(owner *unstructured.Unstructured, rel re
}
depGVK := obj.GroupVersionKind()
if _, ok := d.watches[depGVK]; ok || depGVK.Empty() {
if depGVK.Empty() {
continue
}
useOwnerRef, err := controllerutil.SupportsOwnerReference(d.restMapper, owner, &obj)
if err != nil {
return err
var setWatchOnResource = func(dependent runtime.Object) error {
unstructuredObj := dependent.(*unstructured.Unstructured)
gvkDependent := unstructuredObj.GroupVersionKind()
if gvkDependent.Empty() {
return nil
}
_, ok := d.watches[gvkDependent]
if ok {
return nil
}
useOwnerRef, err := controllerutil.SupportsOwnerReference(d.restMapper, owner, unstructuredObj)
if err != nil {
return err
}
if useOwnerRef && !manifestutil.HasResourcePolicyKeep(unstructuredObj.GetAnnotations()) { // Setup watch using owner references.
if err := d.controller.Watch(&source.Kind{Type: unstructuredObj}, &handler.EnqueueRequestForOwner{
OwnerType: owner,
IsController: true,
}, dependentPredicate); err != nil {
return err
}
} else { // Setup watch using annotations.
if err := d.controller.Watch(&source.Kind{Type: unstructuredObj}, &sdkhandler.EnqueueRequestForAnnotation{
Type: owner.GetObjectKind().GroupVersionKind().GroupKind(),
}, dependentPredicate); err != nil {
return err
}
}
d.watches[depGVK] = struct{}{}
log.V(1).Info("Watching dependent resource", "dependentAPIVersion", depGVK.GroupVersion(), "dependentKind", depGVK.Kind)
return nil
}
if useOwnerRef && !manifestutil.HasResourcePolicyKeep(obj.GetAnnotations()) {
if err := d.controller.Watch(&source.Kind{Type: &obj}, &handler.EnqueueRequestForOwner{
OwnerType: owner,
IsController: true,
}, dependentPredicate); err != nil {
return err
// List is not actually a resource and therefore cannot have a
// watch on it. The watch will be on the kinds listed in the list
// and will therefore need to be handled individually.
listGVK := schema.GroupVersionKind{Group: "", Version: "v1", Kind: "List"}
if depGVK == listGVK {
errListItem := obj.EachListItem(func(o runtime.Object) error {
return setWatchOnResource(o)
})
if errListItem != nil {
return errListItem
}
} else {
if err := d.controller.Watch(&source.Kind{Type: &obj}, &sdkhandler.EnqueueRequestForAnnotation{
Type: owner.GetObjectKind().GroupVersionKind().GroupKind(),
}, dependentPredicate); err != nil {
err := setWatchOnResource(&obj)
if err != nil {
return err
}
}
d.watches[depGVK] = struct{}{}
log.V(1).Info("Watching dependent resource", "dependentAPIVersion", depGVK.GroupVersion(), "dependentKind", depGVK.Kind)
}
return nil
}

View File

@@ -26,8 +26,8 @@ import (
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/operator-framework/helm-operator-plugins/pkg/internal/sdk/controllerutil"
"github.com/operator-framework/helm-operator-plugins/pkg/internal/sdk/status"
"github.com/operator-framework/helm-operator-plugins/internal/sdk/controllerutil"
"github.com/operator-framework/helm-operator-plugins/pkg/internal/status"
)
func New(client client.Client) Updater {

View File

@@ -17,21 +17,37 @@ limitations under the License.
package values
import (
"context"
"fmt"
"os"
"helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/strvals"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"os"
"github.com/operator-framework/helm-operator-plugins/pkg/values"
)
type Values struct {
m map[string]interface{}
var DefaultMapper = values.MapperFunc(func(v chartutil.Values) chartutil.Values { return v })
var DefaultTranslator = values.TranslatorFunc(func(ctx context.Context, u *unstructured.Unstructured) (chartutil.Values, error) {
return getSpecMap(u)
})
func ApplyOverrides(overrideValues map[string]string, obj *unstructured.Unstructured) error {
specMap, err := getSpecMap(obj)
if err != nil {
return err
}
for inK, inV := range overrideValues {
val := fmt.Sprintf("%s=%s", inK, os.ExpandEnv(inV))
if err := strvals.ParseInto(val, specMap); err != nil {
return err
}
}
return nil
}
func FromUnstructured(obj *unstructured.Unstructured) (*Values, error) {
func getSpecMap(obj *unstructured.Unstructured) (map[string]interface{}, error) {
if obj == nil || obj.Object == nil {
return nil, fmt.Errorf("nil object")
}
@@ -43,28 +59,5 @@ func FromUnstructured(obj *unstructured.Unstructured) (*Values, error) {
if !ok {
return nil, fmt.Errorf("spec must be a map")
}
return New(specMap), nil
return specMap, nil
}
func New(m map[string]interface{}) *Values {
return &Values{m: m}
}
func (v *Values) Map() map[string]interface{} {
if v == nil {
return nil
}
return v.m
}
func (v *Values) ApplyOverrides(in map[string]string) error {
for inK, inV := range in {
val := fmt.Sprintf("%s=%s", inK, os.ExpandEnv(inV))
if err := strvals.ParseInto(val, v.m); err != nil {
return err
}
}
return nil
}
var DefaultMapper = values.MapperFunc(func(v chartutil.Values) chartutil.Values { return v })

View File

@@ -42,13 +42,16 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/predicate"
ctrlpredicate "sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/source"
"github.com/operator-framework/helm-operator-plugins/internal/sdk/controllerutil"
"github.com/operator-framework/helm-operator-plugins/pkg/annotation"
helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client"
"github.com/operator-framework/helm-operator-plugins/pkg/hook"
"github.com/operator-framework/helm-operator-plugins/pkg/internal/sdk/controllerutil"
"github.com/operator-framework/helm-operator-plugins/pkg/reconciler/internal/conditions"
"github.com/operator-framework/helm-operator-plugins/pkg/reconciler/internal/diff"
internalhook "github.com/operator-framework/helm-operator-plugins/pkg/reconciler/internal/hook"
"github.com/operator-framework/helm-operator-plugins/pkg/reconciler/internal/updater"
internalvalues "github.com/operator-framework/helm-operator-plugins/pkg/reconciler/internal/values"
@@ -61,18 +64,22 @@ const uninstallFinalizer = "uninstall-helm-release"
type Reconciler struct {
client client.Client
actionClientGetter helmclient.ActionClientGetter
valueMapper values.Mapper
valueTranslator values.Translator
valueMapper values.Mapper // nolint:staticcheck
eventRecorder record.EventRecorder
preHooks []hook.PreHook
postHooks []hook.PostHook
log logr.Logger
gvk *schema.GroupVersionKind
chrt *chart.Chart
overrideValues map[string]string
skipDependentWatches bool
maxConcurrentReconciles int
reconcilePeriod time.Duration
log logr.Logger
gvk *schema.GroupVersionKind
chrt *chart.Chart
selectorPredicate predicate.Predicate
overrideValues map[string]string
skipDependentWatches bool
maxConcurrentReconciles int
reconcilePeriod time.Duration
maxHistory int
skipPrimaryGVKSchemeRegistration bool
annotSetupOnce sync.Once
annotations map[string]struct{}
@@ -126,7 +133,9 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
controllerName := fmt.Sprintf("%v-controller", strings.ToLower(r.gvk.Kind))
r.addDefaults(mgr, controllerName)
r.setupScheme(mgr)
if !r.skipPrimaryGVKSchemeRegistration {
r.setupScheme(mgr)
}
c, err := controller.New(controllerName, mgr, controller.Options{Reconciler: r, MaxConcurrentReconciles: r.maxConcurrentReconciles})
if err != nil {
@@ -230,8 +239,8 @@ func WithOverrideValues(overrides map[string]string) Option {
// Validate that overrides can be parsed and applied
// so that we fail fast during operator setup rather
// than during the first reconciliation.
m := internalvalues.New(map[string]interface{}{})
if err := m.ApplyOverrides(overrides); err != nil {
obj := &unstructured.Unstructured{Object: map[string]interface{}{"spec": map[string]interface{}{}}}
if err := internalvalues.ApplyOverrides(overrides, obj); err != nil {
return err
}
@@ -252,6 +261,52 @@ func SkipDependentWatches(skip bool) Option {
}
}
// SkipPrimaryGVKSchemeRegistration is an Option that allows to disable the default behaviour of
// registering unstructured.Unstructured as underlying type for the GVK scheme.
//
// Disabling this built-in registration is necessary when building operators
// for which it is desired to have the underlying GVK scheme backed by a
// custom struct type.
//
// Example for using a custom type for the GVK scheme instead of unstructured.Unstructured:
//
// // Define custom type for GVK scheme.
// //+kubebuilder:object:root=true
// type Custom struct {
// // [...]
// }
//
// // Register custom type along with common meta types in scheme.
// scheme := runtime.NewScheme()
// scheme.AddKnownTypes(SchemeGroupVersion, &Custom{})
// metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
//
// // Create new manager using the controller-runtime, injecting above scheme.
// options := ctrl.Options{
// Scheme = scheme,
// // [...]
// }
// mgr, err := ctrl.NewManager(config, options)
//
// // Create reconciler with generic scheme registration being disabled.
// r, err := reconciler.New(
// reconciler.WithChart(chart),
// reconciler.SkipPrimaryGVKSchemeRegistration(true),
// // [...]
// )
//
// // Setup reconciler with above manager.
// err = r.SetupWithManager(mgr)
//
// By default, skipping of the generic scheme setup is disabled, which means that
// unstructured.Unstructured is used for the GVK scheme.
func SkipPrimaryGVKSchemeRegistration(skip bool) Option {
return func(r *Reconciler) error {
r.skipPrimaryGVKSchemeRegistration = skip
return nil
}
}
// WithMaxConcurrentReconciles is an Option that configures the number of
// concurrent reconciles that the controller will run.
//
@@ -280,6 +335,18 @@ func WithReconcilePeriod(rp time.Duration) Option {
}
}
// WithMaxReleaseHistory specifies the maximum size of the Helm release history maintained
// on upgrades/rollbacks. Zero (default) means unlimited.
func WithMaxReleaseHistory(maxHistory int) Option {
return func(r *Reconciler) error {
if maxHistory < 0 {
return errors.New("maximum Helm release history size must not be negative")
}
r.maxHistory = maxHistory
return nil
}
}
// WithInstallAnnotations is an Option that configures Install annotations
// to enable custom action.Install fields to be set based on the value of
// annotations found in the custom resource watched by this reconciler.
@@ -362,8 +429,36 @@ func WithPostHook(h hook.PostHook) Option {
}
}
// WithValueTranslator is an Option that configures a function that translates a
// custom resource to the values passed to Helm.
// Use this if you need to customize the logic that translates your custom resource to Helm values.
// If you wish to, you can convert the Unstructured that is passed to your Translator to your own
// Custom Resource struct like this:
//
// import "k8s.io/apimachinery/pkg/runtime"
// foo := your.Foo{}
// if err = runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &foo); err != nil {
// return nil, err
// }
// // work with the type-safe foo
//
// Alternatively, your translator can also work similarly to a Mapper, by accessing the spec with:
//
// u.Object["spec"].(map[string]interface{})
func WithValueTranslator(t values.Translator) Option {
return func(r *Reconciler) error {
r.valueTranslator = t
return nil
}
}
// WithValueMapper is an Option that configures a function that maps values
// from a custom resource spec to the values passed to Helm
// from a custom resource spec to the values passed to Helm.
// Use this if you want to apply a transformation on the values obtained from your custom resource, before
// they are passed to Helm.
//
// Deprecated: Use WithValueTranslator instead.
// WithValueMapper will be removed in a future release.
func WithValueMapper(m values.Mapper) Option {
return func(r *Reconciler) error {
r.valueMapper = m
@@ -371,6 +466,19 @@ func WithValueMapper(m values.Mapper) Option {
}
}
// WithSelector is an Option that configures the reconciler to creates a
// predicate that is used to filter resources based on the specified selector
func WithSelector(s metav1.LabelSelector) Option {
return func(r *Reconciler) error {
p, err := ctrlpredicate.LabelSelectorPredicate(s)
if err != nil {
return err
}
r.selectorPredicate = p
return nil
}
}
// Reconcile reconciles a CR that defines a Helm v3 release.
//
// - If a release does not exist for this CR, a new release is installed.
@@ -397,11 +505,13 @@ func WithValueMapper(m values.Mapper) Option {
// - Irreconcilable - an error occurred during reconciliation
func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl.Result, err error) {
log := r.log.WithValues(strings.ToLower(r.gvk.Kind), req.NamespacedName)
log.V(1).Info("Reconciliation triggered")
obj := &unstructured.Unstructured{}
obj.SetGroupVersionKind(*r.gvk)
err = r.client.Get(ctx, req.NamespacedName, obj)
if apierrors.IsNotFound(err) {
log.V(1).Info("Resource %s/%s not found, nothing to do", req.NamespacedName.Namespace, req.NamespacedName.Name)
return ctrl.Result{}, nil
}
if err != nil {
@@ -458,7 +568,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl.
return ctrl.Result{}, err
}
vals, err := r.getValues(obj)
vals, err := r.getValues(ctx, obj)
if err != nil {
u.UpdateStatus(
updater.EnsureCondition(conditions.Irreconcilable(corev1.ConditionTrue, conditions.ReasonErrorGettingValues, err)),
@@ -521,15 +631,15 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl.
return ctrl.Result{RequeueAfter: r.reconcilePeriod}, nil
}
func (r *Reconciler) getValues(obj *unstructured.Unstructured) (chartutil.Values, error) {
crVals, err := internalvalues.FromUnstructured(obj)
func (r *Reconciler) getValues(ctx context.Context, obj *unstructured.Unstructured) (chartutil.Values, error) {
if err := internalvalues.ApplyOverrides(r.overrideValues, obj); err != nil {
return chartutil.Values{}, err
}
vals, err := r.valueTranslator.Translate(ctx, obj)
if err != nil {
return chartutil.Values{}, err
}
if err := crVals.ApplyOverrides(r.overrideValues); err != nil {
return chartutil.Values{}, err
}
vals := r.valueMapper.Map(crVals.Map())
vals = r.valueMapper.Map(vals)
vals, err = chartutil.CoalesceValues(r.chrt, vals)
if err != nil {
return chartutil.Values{}, err
@@ -592,6 +702,12 @@ func (r *Reconciler) getReleaseState(client helmclient.ActionInterface, obj meta
}
var opts []helmclient.UpgradeOption
if r.maxHistory > 0 {
opts = append(opts, func(u *action.Upgrade) error {
u.MaxHistory = r.maxHistory
return nil
})
}
for name, annot := range r.upgradeAnnotations {
if v, ok := obj.GetAnnotations()[name]; ok {
opts = append(opts, annot.UpgradeOption(v))
@@ -631,17 +747,35 @@ func (r *Reconciler) doInstall(actionClient helmclient.ActionInterface, u *updat
r.reportOverrideEvents(obj)
log.Info("Release installed", "name", rel.Name, "version", rel.Version)
// If log verbosity is higher, output Helm Release Manifest that was installed
if log.V(4).Enabled() {
fmt.Println(diff.Generate("", rel.Manifest))
}
return rel, nil
}
func (r *Reconciler) doUpgrade(actionClient helmclient.ActionInterface, u *updater.Updater, obj *unstructured.Unstructured, vals map[string]interface{}, log logr.Logger) (*release.Release, error) {
var opts []helmclient.UpgradeOption
if r.maxHistory > 0 {
opts = append(opts, func(u *action.Upgrade) error {
u.MaxHistory = r.maxHistory
return nil
})
}
for name, annot := range r.upgradeAnnotations {
if v, ok := obj.GetAnnotations()[name]; ok {
opts = append(opts, annot.UpgradeOption(v))
}
}
// Get the current release so we can compare the new release in the diff if the diff is being logged.
curRel, err := actionClient.Get(obj.GetName())
if err != nil {
return nil, fmt.Errorf("could not get the current Helm Release: %w", err)
}
rel, err := actionClient.Upgrade(obj.GetName(), obj.GetNamespace(), r.chrt, vals, opts...)
if err != nil {
u.UpdateStatus(
@@ -653,6 +787,11 @@ func (r *Reconciler) doUpgrade(actionClient helmclient.ActionInterface, u *updat
r.reportOverrideEvents(obj)
log.Info("Release upgraded", "name", rel.Name, "version", rel.Version)
// If log verbosity is higher, output upgraded Helm Release Manifest
if log.V(4).Enabled() {
fmt.Println(diff.Generate(curRel.Manifest, rel.Manifest))
}
return rel, nil
}
@@ -702,6 +841,11 @@ func (r *Reconciler) doUninstall(actionClient helmclient.ActionInterface, u *upd
return err
} else {
log.Info("Release uninstalled", "name", resp.Release.Name, "version", resp.Release.Version)
// If log verbosity is higher, output Helm Release Manifest that was uninstalled
if log.V(4).Enabled() {
fmt.Println(diff.Generate(resp.Release.Manifest, ""))
}
}
u.Update(updater.RemoveFinalizer(uninstallFinalizer))
u.UpdateStatus(
@@ -726,7 +870,7 @@ func (r *Reconciler) addDefaults(mgr ctrl.Manager, controllerName string) {
if r.client == nil {
r.client = mgr.GetClient()
}
if r.log == nil {
if r.log.GetSink() == nil {
r.log = ctrl.Log.WithName("controllers").WithName("Helm")
}
if r.actionClientGetter == nil {
@@ -736,6 +880,9 @@ func (r *Reconciler) addDefaults(mgr ctrl.Manager, controllerName string) {
if r.eventRecorder == nil {
r.eventRecorder = mgr.GetEventRecorderFor(controllerName)
}
if r.valueTranslator == nil {
r.valueTranslator = internalvalues.DefaultTranslator
}
if r.valueMapper == nil {
r.valueMapper = internalvalues.DefaultMapper
}
@@ -749,9 +896,16 @@ func (r *Reconciler) setupScheme(mgr ctrl.Manager) {
func (r *Reconciler) setupWatches(mgr ctrl.Manager, c controller.Controller) error {
obj := &unstructured.Unstructured{}
obj.SetGroupVersionKind(*r.gvk)
var preds []ctrlpredicate.Predicate
if r.selectorPredicate != nil {
preds = append(preds, r.selectorPredicate)
}
if err := c.Watch(
&source.Kind{Type: obj},
&sdkhandler.InstrumentedEnqueueRequestForObject{},
preds...,
); err != nil {
return err
}

View File

@@ -17,9 +17,14 @@ limitations under the License.
package values
import (
"context"
"helm.sh/helm/v3/pkg/chartutil"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
// Mapper is an interface expected by the reconciler.WithValueMapper option.
//
// Deprecated: use Translator instead.
type Mapper interface {
Map(chartutil.Values) chartutil.Values
}
@@ -29,3 +34,20 @@ type MapperFunc func(chartutil.Values) chartutil.Values
func (m MapperFunc) Map(v chartutil.Values) chartutil.Values {
return m(v)
}
// Translator is an interface expected by the reconciler.WithValueTranslator option.
//
// Translate should return helm values based on the content of the unstructured object
// which is being reconciled.
//
// See also the option documentation.
type Translator interface {
Translate(ctx context.Context, unstructured *unstructured.Unstructured) (chartutil.Values, error)
}
// TranslatorFunc is a helper type for passing a function as a Translator.
type TranslatorFunc func(context.Context, *unstructured.Unstructured) (chartutil.Values, error)
func (t TranslatorFunc) Translate(ctx context.Context, u *unstructured.Unstructured) (chartutil.Values, error) {
return t(ctx, u)
}

View File

@@ -15,11 +15,15 @@
package watches
import (
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"text/template"
sprig "github.com/go-task/slim-sprig"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chart/loader"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -31,12 +35,12 @@ type Watch struct {
schema.GroupVersionKind `json:",inline"`
ChartPath string `json:"chart"`
WatchDependentResources *bool `json:"watchDependentResources,omitempty"`
OverrideValues map[string]string `json:"overrideValues,omitempty"`
ReconcilePeriod *metav1.Duration `json:"reconcilePeriod,omitempty"`
MaxConcurrentReconciles *int `json:"maxConcurrentReconciles,omitempty"`
Chart *chart.Chart `json:"-"`
WatchDependentResources *bool `json:"watchDependentResources,omitempty"`
OverrideValues map[string]string `json:"overrideValues,omitempty"`
ReconcilePeriod *metav1.Duration `json:"reconcilePeriod,omitempty"`
MaxConcurrentReconciles *int `json:"maxConcurrentReconciles,omitempty"`
Selector *metav1.LabelSelector `json:"selector,omitempty"`
Chart *chart.Chart `json:"-"`
}
// Load loads a slice of Watches from the watch file at `path`. For each entry
@@ -44,7 +48,22 @@ type Watch struct {
// encountered loading the file or verifying the configuration, it will be
// returned.
func Load(path string) ([]Watch, error) {
b, err := ioutil.ReadFile(path)
f, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("could not open watches file: %w", err)
}
w, err := LoadReader(f)
// Make sure to close the file, regardless of the error returned by
// LoadReader.
if err := f.Close(); err != nil {
return nil, fmt.Errorf("could not close watches file: %w", err)
}
return w, err
}
func LoadReader(reader io.Reader) ([]Watch, error) {
b, err := ioutil.ReadAll(reader)
if err != nil {
return nil, err
}
@@ -55,10 +74,12 @@ func Load(path string) ([]Watch, error) {
return nil, err
}
watchesMap := make(map[schema.GroupVersionKind]Watch)
watchesMap := make(map[schema.GroupVersionKind]struct{})
for i, w := range watches {
if err := verifyGVK(w.GroupVersionKind); err != nil {
return nil, fmt.Errorf("invalid GVK: %s: %w", w.GroupVersionKind, err)
gvk := w.GroupVersionKind
if err := verifyGVK(gvk); err != nil {
return nil, fmt.Errorf("invalid GVK: %s: %w", gvk, err)
}
cl, err := loader.Load(w.ChartPath)
@@ -66,31 +87,50 @@ func Load(path string) ([]Watch, error) {
return nil, fmt.Errorf("invalid chart %s: %w", w.ChartPath, err)
}
w.Chart = cl
w.OverrideValues = expandOverrideEnvs(w.OverrideValues)
if _, ok := watchesMap[gvk]; ok {
return nil, fmt.Errorf("duplicate GVK: %s", gvk)
}
watchesMap[gvk] = struct{}{}
if w.WatchDependentResources == nil {
trueVal := true
w.WatchDependentResources = &trueVal
}
if _, ok := watchesMap[w.GroupVersionKind]; ok {
return nil, fmt.Errorf("duplicate GVK: %s", w.GroupVersionKind)
if w.Selector == nil {
w.Selector = &metav1.LabelSelector{}
}
w.OverrideValues, err = expandOverrideValues(w.OverrideValues)
if err != nil {
return nil, fmt.Errorf("failed to expand override values")
}
watchesMap[w.GroupVersionKind] = w
watches[i] = w
}
return watches, nil
}
func expandOverrideEnvs(in map[string]string) map[string]string {
func expandOverrideValues(in map[string]string) (map[string]string, error) {
if in == nil {
return nil
return nil, nil
}
out := make(map[string]string)
for k, v := range in {
out[k] = os.ExpandEnv(v)
envV := os.ExpandEnv(v)
v := &bytes.Buffer{}
tmplV, err := template.New(k).Funcs(sprig.TxtFuncMap()).Parse(envV)
if err != nil {
return nil, fmt.Errorf("invalid template string %q: %v", envV, err)
}
if err := tmplV.Execute(v, nil); err != nil {
return nil, fmt.Errorf("failed to execute template %q: %v", envV, err)
}
out[k] = v.String()
}
return out
return out, nil
}
func verifyGVK(gvk schema.GroupVersionKind) error {