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

@@ -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

@@ -1,90 +0,0 @@
/*
Copyright 2020 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 controllerutil
import (
"context"
"time"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
var (
AddFinalizer = controllerutil.AddFinalizer
RemoveFinalizer = controllerutil.RemoveFinalizer
ContainsFinalizer = func(obj metav1.Object, finalizer string) bool {
for _, f := range obj.GetFinalizers() {
if f == finalizer {
return true
}
}
return false
}
)
func WaitForDeletion(ctx context.Context, cl client.Reader, o client.Object) error {
key := client.ObjectKeyFromObject(o)
return wait.PollImmediateUntil(time.Millisecond*10, func() (bool, error) {
err := cl.Get(ctx, key, o)
if apierrors.IsNotFound(err) {
return true, nil
}
if err != nil {
return false, err
}
return false, nil
}, ctx.Done())
}
func SupportsOwnerReference(restMapper meta.RESTMapper, owner, dependent client.Object) (bool, error) {
ownerGVK := owner.GetObjectKind().GroupVersionKind()
ownerMapping, err := restMapper.RESTMapping(ownerGVK.GroupKind(), ownerGVK.Version)
if err != nil {
return false, err
}
depGVK := dependent.GetObjectKind().GroupVersionKind()
depMapping, err := restMapper.RESTMapping(depGVK.GroupKind(), depGVK.Version)
if err != nil {
return false, err
}
ownerClusterScoped := ownerMapping.Scope.Name() == meta.RESTScopeNameRoot
ownerNamespace := owner.GetNamespace()
depClusterScoped := depMapping.Scope.Name() == meta.RESTScopeNameRoot
depNamespace := dependent.GetNamespace()
if ownerClusterScoped {
return true, nil
}
if depClusterScoped {
return false, nil
}
if ownerNamespace != depNamespace {
return false, nil
}
return true, nil
}

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 {