Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ require (
github.com/fluxcd/pkg/apis/acl v0.12.0
github.com/fluxcd/pkg/apis/event v0.32.0
github.com/fluxcd/pkg/apis/kustomize v1.23.0
github.com/fluxcd/pkg/apis/meta v1.35.0
github.com/fluxcd/pkg/auth v0.62.0
github.com/fluxcd/pkg/cache v0.17.0
github.com/fluxcd/pkg/apis/meta v1.36.1-0.20261020182949-c5194b17fdad
github.com/fluxcd/pkg/auth v0.63.1-0.20261020182949-c5194b17fdad
github.com/fluxcd/pkg/cache v0.17.1-0.20261020182949-c5194b17fdad
github.com/fluxcd/pkg/chartutil v1.32.0
github.com/fluxcd/pkg/runtime v0.119.0
github.com/fluxcd/pkg/runtime v0.120.1-0.20261020182949-c5194b17fdad
github.com/fluxcd/pkg/ssa v0.80.0
github.com/fluxcd/pkg/testserver v0.15.0
github.com/fluxcd/source-controller/api v1.9.6
Expand Down Expand Up @@ -111,6 +111,7 @@ require (
github.com/extism/go-sdk v1.7.1 // indirect
github.com/fatih/color v1.19.0 // indirect
github.com/felixge/httpsnoop v1.1.0 // indirect
github.com/fluxcd/pkg/apis/crypto v0.4.1-0.20261020182949-c5194b17fdad // indirect
github.com/fsnotify/fsnotify v1.10.1 // indirect
github.com/fxamacker/cbor/v2 v2.9.3 // indirect
github.com/go-errors/errors v1.5.1 // indirect
Expand Down Expand Up @@ -180,6 +181,7 @@ require (
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/spf13/cobra v1.10.2 // indirect
github.com/spiffe/go-spiffe/v2 v2.9.0 // indirect
github.com/stretchr/testify v1.12.1 // indirect
github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 // indirect
github.com/tetratelabs/wazero v1.12.0 // indirect
Expand Down
20 changes: 12 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -150,20 +150,22 @@ github.com/fluxcd/cli-utils v1.4.0 h1:zlTiCWcVDQvvwZXILYeu5rpbnGt1sS+w32y3+rkOpi
github.com/fluxcd/cli-utils v1.4.0/go.mod h1:OupUOj2boEKTbiguMbGMz5Z/d59JUcb1c7pPESe8Pjw=
github.com/fluxcd/pkg/apis/acl v0.12.0 h1:UWpZDqKxpKDW38wdXIMHV8Uv3POPu2vUrra0JH1frH0=
github.com/fluxcd/pkg/apis/acl v0.12.0/go.mod h1:PJrGIu5Bw/6/siO95uhS+pi5oKSU+gVJqHqsPHb8Kok=
github.com/fluxcd/pkg/apis/crypto v0.4.1-0.20261020182949-c5194b17fdad h1:SAzeZibDaqyyq7CaccwDEySCcsOMGGuHmEN28LcRPvM=
github.com/fluxcd/pkg/apis/crypto v0.4.1-0.20261020182949-c5194b17fdad/go.mod h1:bEwkDgBjLXMVJ2BwmVbbj32PkirmNSicabgl2Jvw7Dk=
github.com/fluxcd/pkg/apis/event v0.32.0 h1:3zyjdUspwQN+EQJPlUXwCvCMbeqtB+azlNmeenAcbuE=
github.com/fluxcd/pkg/apis/event v0.32.0/go.mod h1:ALma8dTqo+NbI+z4BawSzt0pAPjetCSNNUSSS/9p0VU=
github.com/fluxcd/pkg/apis/kustomize v1.23.0 h1:tT/sUURyh6ixQG4+Y/ZoUu+hh5/DQLRraWBgy42ubqQ=
github.com/fluxcd/pkg/apis/kustomize v1.23.0/go.mod h1:Jm/LmEsqJIldhhTmhkORAq8JKVZK3ZV2Q88rntjGxEI=
github.com/fluxcd/pkg/apis/meta v1.35.0 h1:/k5d7uEFrxkz/hf6JTrdsKBr3UQWVHW2J4G6VSSae/k=
github.com/fluxcd/pkg/apis/meta v1.35.0/go.mod h1:1MLvK2Xu3UdTKBzg+Q1Oj7H9PHeiKsUB1UW3uwxAwiM=
github.com/fluxcd/pkg/auth v0.62.0 h1:ZhrkfoLTaH5Q095sNGLZ47zLYw/AHw4nVnPAspGwT/c=
github.com/fluxcd/pkg/auth v0.62.0/go.mod h1:46lKYjDvx5Q7fsq09ZC1BxXJcstTOfm2X/NFLLFn7wA=
github.com/fluxcd/pkg/cache v0.17.0 h1:197ST7bSJD2Nw4G01y5cMjtWQpwQsyjpl2ox4MaGeU8=
github.com/fluxcd/pkg/cache v0.17.0/go.mod h1:OW3fncHhPnPZFZav5FdEa//cyMHSndI0JS+IM69LXOU=
github.com/fluxcd/pkg/apis/meta v1.36.1-0.20261020182949-c5194b17fdad h1:p/W9d+gf9FuNNmiwUZJT7ReGzTzwuYLUago5szqkq5I=
github.com/fluxcd/pkg/apis/meta v1.36.1-0.20261020182949-c5194b17fdad/go.mod h1:1MLvK2Xu3UdTKBzg+Q1Oj7H9PHeiKsUB1UW3uwxAwiM=
github.com/fluxcd/pkg/auth v0.63.1-0.20261020182949-c5194b17fdad h1:lUjY+hEXSYG8RdJhuNYl5mjkrnLpwuG/+A+iSybCRlE=
github.com/fluxcd/pkg/auth v0.63.1-0.20261020182949-c5194b17fdad/go.mod h1:gWbB4MzuHWalVyI0o4lE84aINidor9+S63ithckm+94=
github.com/fluxcd/pkg/cache v0.17.1-0.20261020182949-c5194b17fdad h1:Lf7S9vl776ZW7SDxdQtZcEu1vjWroZS86ePd8gost4M=
github.com/fluxcd/pkg/cache v0.17.1-0.20261020182949-c5194b17fdad/go.mod h1:OW3fncHhPnPZFZav5FdEa//cyMHSndI0JS+IM69LXOU=
github.com/fluxcd/pkg/chartutil v1.32.0 h1:rqBhgiGsrS4am/jsCfKYlQvPQ+jATug10BtO95UHdq0=
github.com/fluxcd/pkg/chartutil v1.32.0/go.mod h1:pBqCvJVDLpKKnU+k9A+TnceZn+DnnivfVGhzNzHJXZU=
github.com/fluxcd/pkg/runtime v0.119.0 h1:cY2KpQ4WwpQKG+pFToslSIXUjKK8ckHQ7cvPUc/QHiY=
github.com/fluxcd/pkg/runtime v0.119.0/go.mod h1:7LJFbZPXwl++ATADx6bt63Jh9CverBoXQxbluxPwspw=
github.com/fluxcd/pkg/runtime v0.120.1-0.20261020182949-c5194b17fdad h1:AcQzxznBRZyRthcVgFjPV49uLfu7eDOoHO1B5WyRsO8=
github.com/fluxcd/pkg/runtime v0.120.1-0.20261020182949-c5194b17fdad/go.mod h1:Cw0CQ/tVKS9lgJoyKVouZr1qVnoJcYnSHw3QDzIOOjA=
github.com/fluxcd/pkg/ssa v0.80.0 h1:GgakpCFz4pY+unpOscPYlc5eVLUsB7hw9TmWSNr3yp8=
github.com/fluxcd/pkg/ssa v0.80.0/go.mod h1:mUwKP2+KZXm1mal1gSs9SjgcexyqzXbPfHKxdFv5ntM=
github.com/fluxcd/pkg/testserver v0.15.0 h1:6Pao9oK1Cu5ssh9c7QoDnnyfPUtxHbw7gJ+5tSOmkWc=
Expand Down Expand Up @@ -396,6 +398,8 @@ github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiT
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spiffe/go-spiffe/v2 v2.9.0 h1:g5qTg3qIP1HXV+GzuyEfSJFVxUCpEP5Ui1OINVJHPB4=
github.com/spiffe/go-spiffe/v2 v2.9.0/go.mod h1:w2CLWKLMTX/PPYUEUPv3ltH0RXsw5S8suwNF46w9/Aw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
Expand Down
52 changes: 30 additions & 22 deletions internal/controller/helmrelease_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,15 @@ type HelmReleaseReconciler struct {

// Kubernetes configuration

FieldManager string
DisallowedFieldManagers []string
DefaultServiceAccount string
GetClusterConfig func() (*rest.Config, error)
ClientOpts runtimeClient.Options
KubeConfigOpts runtimeClient.KubeConfigOptions
APIReader client.Reader
TokenCache *cache.TokenCache
FieldManager string
DisallowedFieldManagers []string
DefaultServiceAccount string
DefaultKubeConfigServiceAccount string
GetClusterConfig func() (*rest.Config, error)
ClientOpts runtimeClient.Options
KubeConfigOpts runtimeClient.KubeConfigOptions
APIReader client.Reader
TokenCache *cache.TokenCache

// Retry and requeue configuration

Expand All @@ -108,11 +109,12 @@ type HelmReleaseReconciler struct {

// Feature gates

AdditiveCELDependencyCheck bool
AllowExternalArtifact bool
DefaultToRetryOnFailure bool
DirectSourceFetch bool
DisableChartDigestTracking bool
AdditiveCELDependencyCheck bool
AllowExternalArtifact bool
DefaultToRetryOnFailure bool
DirectSourceFetch bool
DisableChartDigestTracking bool
ObjectLevelWorkloadIdentity bool
}

const terminalErrorMessage = "Reconciliation failed terminally due to configuration error"
Expand Down Expand Up @@ -722,17 +724,23 @@ func (r *HelmReleaseReconciler) buildRESTClientGetter(ctx context.Context, obj *
var err error
switch {
case kc.ConfigMapRef != nil && kc.SecretRef == nil:
var opts []auth.Option
var opts []authutils.Option
if r.DefaultKubeConfigServiceAccount != "" {
opts = append(opts, authutils.WithDefaultServiceAccount(r.DefaultKubeConfigServiceAccount))
}
if r.ObjectLevelWorkloadIdentity {
opts = append(opts, authutils.WithObjectLevelWorkloadIdentity())
}
authOpts := []auth.Option{
auth.WithObject(v2.GroupVersion.Group, v2.HelmReleaseKind, obj),
}
if r.TokenCache != nil {
involvedObject := cache.InvolvedObject{
Kind: v2.HelmReleaseKind,
Name: obj.GetName(),
Namespace: obj.GetNamespace(),
Operation: cache.OperationReconcile,
}
opts = append(opts, auth.WithCache(*r.TokenCache, involvedObject))
authOpts = append(authOpts,
auth.WithCache(*r.TokenCache),
auth.WithCacheOperation(cache.OperationReconcile))
}
restConfig, err = authutils.GetRESTConfig(ctx, *kc, obj.GetNamespace(), r.Client, opts...)
opts = append(opts, authutils.WithAuthOptions(authOpts...))
restConfig, err = authutils.GetRESTConfig(ctx, *kc, r.Client, opts...)
case kc.SecretRef != nil && kc.ConfigMapRef == nil:
secretName := types.NamespacedName{
Namespace: obj.GetNamespace(),
Expand Down
105 changes: 96 additions & 9 deletions internal/controller/helmrelease_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3457,13 +3457,16 @@ users:
)

tests := []struct {
name string
env map[string]string
getConfig func() (*rest.Config, error)
spec v2.HelmReleaseSpec
secret *corev1.Secret
want genericclioptions.RESTClientGetter
wantErr string
name string
env map[string]string
getConfig func() (*rest.Config, error)
spec v2.HelmReleaseSpec
secret *corev1.Secret
configMap *corev1.ConfigMap
defaultKubeConfigServiceAccount string
objectLevelWorkloadIdentity bool
want genericclioptions.RESTClientGetter
wantErr string
}{
{
name: "builds in-cluster RESTClientGetter for HelmRelease",
Expand Down Expand Up @@ -3529,6 +3532,85 @@ users:
},
wantErr: "does not contain a 'invalid-key' key",
},
{
name: "error on missing KubeConfig ConfigMap",
spec: v2.HelmReleaseSpec{
KubeConfig: &meta.KubeConfigReference{
ConfigMapRef: &meta.LocalObjectReference{
Name: "kubeconfig",
},
},
},
wantErr: "failed to get configmap",
},
{
name: "error when KubeConfig ConfigMap uses a ServiceAccount without the feature gate",
spec: v2.HelmReleaseSpec{
KubeConfig: &meta.KubeConfigReference{
ConfigMapRef: &meta.LocalObjectReference{
Name: "kubeconfig",
},
},
},
configMap: &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "kubeconfig",
Namespace: namespace,
},
Data: map[string]string{
meta.KubeConfigKeyProvider: "generic",
meta.KubeConfigKeyAddress: "https://1.2.3.4",
meta.KubeConfigKeyServiceAccountName: "auth-sa",
},
},
wantErr: "ObjectLevelWorkloadIdentity feature gate is not enabled",
},
{
name: "error when the default KubeConfig ServiceAccount is missing",
spec: v2.HelmReleaseSpec{
KubeConfig: &meta.KubeConfigReference{
ConfigMapRef: &meta.LocalObjectReference{
Name: "kubeconfig",
},
},
},
configMap: &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "kubeconfig",
Namespace: namespace,
},
Data: map[string]string{
meta.KubeConfigKeyProvider: "generic",
meta.KubeConfigKeyAddress: "https://1.2.3.4",
},
},
defaultKubeConfigServiceAccount: "auth-sa",
objectLevelWorkloadIdentity: true,
wantErr: "failed to get service account 'some-namespace/auth-sa'",
},
{
name: "error when the KubeConfig ConfigMap ServiceAccount is missing",
spec: v2.HelmReleaseSpec{
KubeConfig: &meta.KubeConfigReference{
ConfigMapRef: &meta.LocalObjectReference{
Name: "kubeconfig",
},
},
},
configMap: &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "kubeconfig",
Namespace: namespace,
},
Data: map[string]string{
meta.KubeConfigKeyProvider: "generic",
meta.KubeConfigKeyAddress: "https://1.2.3.4",
meta.KubeConfigKeyServiceAccountName: "auth-sa",
},
},
objectLevelWorkloadIdentity: true,
wantErr: "failed to get service account 'some-namespace/auth-sa'",
},
}

for _, tt := range tests {
Expand All @@ -3543,10 +3625,15 @@ users:
if tt.secret != nil {
c.WithObjects(tt.secret)
}
if tt.configMap != nil {
c.WithObjects(tt.configMap)
}

r := &HelmReleaseReconciler{
Client: c.Build(),
GetClusterConfig: tt.getConfig,
Client: c.Build(),
GetClusterConfig: tt.getConfig,
DefaultKubeConfigServiceAccount: tt.defaultKubeConfigServiceAccount,
ObjectLevelWorkloadIdentity: tt.objectLevelWorkloadIdentity,
}

getter, err := r.buildRESTClientGetter(context.Background(), &v2.HelmRelease{
Expand Down
11 changes: 7 additions & 4 deletions internal/features/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,19 @@ var features = map[string]bool{
// DefaultToRetryOnFailure
// opt-in from v1.5.2
DefaultToRetryOnFailure: false,
// ObjectLevelWorkloadIdentity
// opt-in from Flux v2.6.
auth.FeatureGateObjectLevelWorkloadIdentity: false,
}

func init() {
auth.SetFeatureGates(features)
var retiredFeatures = map[string]string{
// TODO: retire ObjectLevelWorkloadIdentity in Flux 2.11.
}

// FeatureGates contains a list of all supported feature gates and
// their default values.
func FeatureGates() map[string]bool {
return features
func FeatureGates() (map[string]bool, map[string]string) {
return features, retiredFeatures
}

// Enabled verifies whether the feature is enabled or not.
Expand Down
54 changes: 26 additions & 28 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package main

import (
"errors"
"fmt"
"os"
"time"
Expand Down Expand Up @@ -166,12 +167,10 @@ func main() {
os.Exit(1)
}

switch enabled, err := features.Enabled(auth.FeatureGateObjectLevelWorkloadIdentity); {
case err != nil:
objectLevelWorkloadIdentity, err := features.Enabled(auth.FeatureGateObjectLevelWorkloadIdentity)
if err != nil {
setupLog.Error(err, "unable to check feature gate "+auth.FeatureGateObjectLevelWorkloadIdentity)
os.Exit(1)
case enabled:
auth.EnableObjectLevelWorkloadIdentity()
}

switch enabled, err := features.Enabled(features.UseHelm3Defaults); {
Expand All @@ -194,12 +193,9 @@ func main() {
os.Exit(1)
}

if defaultKubeConfigServiceAccount != "" {
auth.SetDefaultKubeConfigServiceAccount(defaultKubeConfigServiceAccount)
}

if auth.InconsistentObjectLevelConfiguration() {
setupLog.Error(auth.ErrInconsistentObjectLevelConfiguration, "invalid configuration")
if defaultKubeConfigServiceAccount != "" && !objectLevelWorkloadIdentity {
setupLog.Error(errors.New("cannot set the default kubeconfig service account when the "+
auth.FeatureGateObjectLevelWorkloadIdentity+" feature gate is not enabled"), "invalid configuration")
os.Exit(1)
}

Expand Down Expand Up @@ -380,24 +376,26 @@ func main() {
watchConfigs := !disableConfigWatchers

if err = (&controller.HelmReleaseReconciler{
Client: mgr.GetClient(),
APIReader: mgr.GetAPIReader(),
Recorder: eventRecorder,
Metrics: metricsH,
GetClusterConfig: ctrl.GetConfig,
ClientOpts: clientOptions,
KubeConfigOpts: kubeConfigOpts,
FieldManager: controllerName,
DefaultToRetryOnFailure: defaultToRetryOnFailure,
DisableChartDigestTracking: disableChartDigestTracking,
AdditiveCELDependencyCheck: additiveCELDependencyCheck,
DirectSourceFetch: directSourceFetch,
TokenCache: tokenCache,
DependencyRequeueInterval: requeueDependency,
ArtifactFetchRetries: httpRetry,
ArtifactFetchTimeout: httpTimeout,
AllowExternalArtifact: allowExternalArtifact,
DisallowedFieldManagers: disallowedFieldManagers,
Client: mgr.GetClient(),
APIReader: mgr.GetAPIReader(),
Recorder: eventRecorder,
Metrics: metricsH,
GetClusterConfig: ctrl.GetConfig,
ClientOpts: clientOptions,
KubeConfigOpts: kubeConfigOpts,
FieldManager: controllerName,
DefaultKubeConfigServiceAccount: defaultKubeConfigServiceAccount,
ObjectLevelWorkloadIdentity: objectLevelWorkloadIdentity,
DefaultToRetryOnFailure: defaultToRetryOnFailure,
DisableChartDigestTracking: disableChartDigestTracking,
AdditiveCELDependencyCheck: additiveCELDependencyCheck,
DirectSourceFetch: directSourceFetch,
TokenCache: tokenCache,
DependencyRequeueInterval: requeueDependency,
ArtifactFetchRetries: httpRetry,
ArtifactFetchTimeout: httpTimeout,
AllowExternalArtifact: allowExternalArtifact,
DisallowedFieldManagers: disallowedFieldManagers,
}).SetupWithManager(ctx, mgr, controller.HelmReleaseReconcilerOptions{
RateLimiter: helper.GetRateLimiter(rateLimiterOptions),
WatchConfigs: watchConfigs,
Expand Down
Loading