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
31 changes: 31 additions & 0 deletions api/v2/helmrelease_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,15 @@ type HelmReleaseSpec struct {
// +optional
PostRenderers []PostRenderer `json:"postRenderers,omitempty"`

// PostRenderStrategy defines the strategy for sending hooks to post-renderers.
// Valid values are 'nohooks' (hooks not sent to post-renderers, Helm 3 behavior),
// 'combined' (hooks and templates sent together, Helm 4 default), and 'separate'
// (hooks and templates sent in separate streams, Helm 4.2 opt-in).
// Defaults to 'combined', or 'nohooks' when the UseHelm3Defaults feature gate is enabled.
// +kubebuilder:validation:Enum=nohooks;combined;separate
// +optional
PostRenderStrategy PostRenderStrategy `json:"postRenderStrategy,omitempty"`

// WaitStrategy defines Helm's wait strategy for waiting for applied
// resources to become ready.
// +optional
Expand Down Expand Up @@ -235,6 +244,23 @@ type PostRenderer struct {
Kustomize *Kustomize `json:"kustomize,omitempty"`
}

// PostRenderStrategy represents the strategy for sending hooks to post-renderers.
type PostRenderStrategy string

const (
// PostRenderStrategyNoHooks is the Helm 3 behavior where hooks are not sent
// to post-renderers.
PostRenderStrategyNoHooks PostRenderStrategy = "nohooks"

// PostRenderStrategyCombined is the Helm 4 default behavior where both hooks
// and templates are sent to post-renderers in the same stream.
PostRenderStrategyCombined PostRenderStrategy = "combined"

// PostRenderStrategySeparate is the Helm 4.2 opt-in behavior where hooks and
// templates are sent to post-renderers in separate streams.
PostRenderStrategySeparate PostRenderStrategy = "separate"
)

// DriftDetectionMode represents the modes in which a controller can detect and
// handle differences between the manifest in the Helm storage and the resources
// currently existing in the cluster.
Expand Down Expand Up @@ -471,6 +497,11 @@ func (in *HelmRelease) GetWaitStrategy() WaitStrategyName {
return ""
}

// GetPostRenderStrategy returns the post-render strategy for the Helm actions.
func (in *HelmRelease) GetPostRenderStrategy() PostRenderStrategy {
return in.Spec.PostRenderStrategy
}

// Remediation defines a consistent interface for InstallRemediation and
// UpgradeRemediation.
// +kubebuilder:object:generate=false
Expand Down
12 changes: 12 additions & 0 deletions config/crd/bases/helm.toolkit.fluxcd.io_helmreleases.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,18 @@ spec:

If not set, it defaults to true.
type: boolean
postRenderStrategy:
description: |-
PostRenderStrategy defines the strategy for sending hooks to post-renderers.
Valid values are 'nohooks' (hooks not sent to post-renderers, Helm 3 behavior),
'combined' (hooks and templates sent together, Helm 4 default), and 'separate'
(hooks and templates sent in separate streams, Helm 4.2 opt-in).
Defaults to 'combined', or 'nohooks' when the UseHelm3Defaults feature gate is enabled.
enum:
- nohooks
- combined
- separate
type: string
postRenderers:
description: |-
PostRenderers holds an array of Helm PostRenderers, which will be applied in order
Expand Down
43 changes: 43 additions & 0 deletions docs/api/v2/helm.md
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,24 @@ of their definition.</p>
</tr>
<tr>
<td>
<code>postRenderStrategy</code><br>
<em>
<a href="#helm.toolkit.fluxcd.io/v2.PostRenderStrategy">
PostRenderStrategy
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>PostRenderStrategy defines the strategy for sending hooks to post-renderers.
Valid values are &lsquo;nohooks&rsquo; (hooks not sent to post-renderers, Helm 3 behavior),
&lsquo;combined&rsquo; (hooks and templates sent together, Helm 4 default), and &lsquo;separate&rsquo;
(hooks and templates sent in separate streams, Helm 4.2 opt-in).
Defaults to &lsquo;combined&rsquo;, or &lsquo;nohooks&rsquo; when the UseHelm3Defaults feature gate is enabled.</p>
</td>
</tr>
<tr>
<td>
<code>waitStrategy</code><br>
<em>
<a href="#helm.toolkit.fluxcd.io/v2.WaitStrategy">
Expand Down Expand Up @@ -1575,6 +1593,24 @@ of their definition.</p>
</tr>
<tr>
<td>
<code>postRenderStrategy</code><br>
<em>
<a href="#helm.toolkit.fluxcd.io/v2.PostRenderStrategy">
PostRenderStrategy
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>PostRenderStrategy defines the strategy for sending hooks to post-renderers.
Valid values are &lsquo;nohooks&rsquo; (hooks not sent to post-renderers, Helm 3 behavior),
&lsquo;combined&rsquo; (hooks and templates sent together, Helm 4 default), and &lsquo;separate&rsquo;
(hooks and templates sent in separate streams, Helm 4.2 opt-in).
Defaults to &lsquo;combined&rsquo;, or &lsquo;nohooks&rsquo; when the UseHelm3Defaults feature gate is enabled.</p>
</td>
</tr>
<tr>
<td>
<code>waitStrategy</code><br>
<em>
<a href="#helm.toolkit.fluxcd.io/v2.WaitStrategy">
Expand Down Expand Up @@ -2370,6 +2406,13 @@ patch, but this operator is simpler to specify.</p>
</table>
</div>
</div>
<h3 id="helm.toolkit.fluxcd.io/v2.PostRenderStrategy">PostRenderStrategy
(<code>string</code> alias)</h3>
<p>
(<em>Appears on:</em>
<a href="#helm.toolkit.fluxcd.io/v2.HelmReleaseSpec">HelmReleaseSpec</a>)
</p>
<p>PostRenderStrategy represents the strategy for sending hooks to post-renderers.</p>
<h3 id="helm.toolkit.fluxcd.io/v2.PostRenderer">PostRenderer
</h3>
<p>
Expand Down
16 changes: 16 additions & 0 deletions docs/spec/v2/helmreleases.md
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,22 @@ spec:
replicaCount: 2
```

### Post-render strategy

`.spec.postRenderStrategy` is an optional field to configure the strategy for sending
hooks to post-renderers. Valid values are:

- `nohooks`: Hooks are not sent to post-renderers (Helm 3 behavior).
- `combined`: Hooks and templates are sent together to post-renderers in the same stream (Helm 4 default).
- `separate`: Hooks and templates are sent to post-renderers in separate streams (Helm 4.2 opt-in).

Defaults to `combined`, or `nohooks` when the `UseHelm3Defaults` feature gate is enabled.

```yaml
spec:
postRenderStrategy: combined
```

### Install configuration

`.spec.install` is an optional field to specify the configuration for the
Expand Down
3 changes: 1 addition & 2 deletions internal/action/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"context"
"fmt"

"helm.sh/helm/v4/pkg/action"
helmaction "helm.sh/helm/v4/pkg/action"
helmchartutil "helm.sh/helm/v4/pkg/chart/common"
helmchart "helm.sh/helm/v4/pkg/chart/v2"
Expand Down Expand Up @@ -109,7 +108,7 @@ func newInstall(config *helmaction.Configuration, obj *v2.HelmRelease, opts []In
}

install.PostRenderer = postrender.BuildPostRenderers(obj)
install.PostRenderStrategy = action.PostRenderStrategyNoHooks
install.PostRenderStrategy = toHelmPostRenderStrategy(obj.GetPostRenderStrategy())

for _, opt := range opts {
opt(install)
Expand Down
97 changes: 97 additions & 0 deletions internal/action/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"time"

. "github.com/onsi/gomega"
"helm.sh/helm/v4/pkg/action"
helmaction "helm.sh/helm/v4/pkg/action"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

Expand Down Expand Up @@ -208,4 +209,100 @@ func Test_newInstall(t *testing.T) {
g.Expect(got).ToNot(BeNil())
g.Expect(got.ServerSideApply).To(BeFalse())
})

t.Run("post render strategy defaults to combined with Helm4 defaults", func(t *testing.T) {
g := NewWithT(t)

// Save and restore UseHelm3Defaults
oldUseHelm3Defaults := UseHelm3Defaults
t.Cleanup(func() { UseHelm3Defaults = oldUseHelm3Defaults })
UseHelm3Defaults = false

obj := &v2.HelmRelease{
ObjectMeta: metav1.ObjectMeta{
Name: "install",
Namespace: "install-ns",
},
Spec: v2.HelmReleaseSpec{},
}

got := newInstall(&helmaction.Configuration{}, obj, nil)
g.Expect(got).ToNot(BeNil())
g.Expect(got.PostRenderStrategy).To(Equal(action.PostRenderStrategyCombined))
})

t.Run("post render strategy defaults to nohooks with UseHelm3Defaults", func(t *testing.T) {
g := NewWithT(t)

// Save and restore UseHelm3Defaults
oldUseHelm3Defaults := UseHelm3Defaults
t.Cleanup(func() { UseHelm3Defaults = oldUseHelm3Defaults })
UseHelm3Defaults = true

obj := &v2.HelmRelease{
ObjectMeta: metav1.ObjectMeta{
Name: "install",
Namespace: "install-ns",
},
Spec: v2.HelmReleaseSpec{},
}

got := newInstall(&helmaction.Configuration{}, obj, nil)
g.Expect(got).ToNot(BeNil())
g.Expect(got.PostRenderStrategy).To(Equal(action.PostRenderStrategyNoHooks))
})

t.Run("post render strategy combined", func(t *testing.T) {
g := NewWithT(t)

obj := &v2.HelmRelease{
ObjectMeta: metav1.ObjectMeta{
Name: "install",
Namespace: "install-ns",
},
Spec: v2.HelmReleaseSpec{
PostRenderStrategy: v2.PostRenderStrategyCombined,
},
}

got := newInstall(&helmaction.Configuration{}, obj, nil)
g.Expect(got).ToNot(BeNil())
g.Expect(got.PostRenderStrategy).To(Equal(action.PostRenderStrategyCombined))
})

t.Run("post render strategy separate", func(t *testing.T) {
g := NewWithT(t)

obj := &v2.HelmRelease{
ObjectMeta: metav1.ObjectMeta{
Name: "install",
Namespace: "install-ns",
},
Spec: v2.HelmReleaseSpec{
PostRenderStrategy: v2.PostRenderStrategySeparate,
},
}

got := newInstall(&helmaction.Configuration{}, obj, nil)
g.Expect(got).ToNot(BeNil())
g.Expect(got.PostRenderStrategy).To(Equal(action.PostRenderStrategySeparate))
})

t.Run("post render strategy nohooks", func(t *testing.T) {
g := NewWithT(t)

obj := &v2.HelmRelease{
ObjectMeta: metav1.ObjectMeta{
Name: "install",
Namespace: "install-ns",
},
Spec: v2.HelmReleaseSpec{
PostRenderStrategy: v2.PostRenderStrategyNoHooks,
},
}

got := newInstall(&helmaction.Configuration{}, obj, nil)
g.Expect(got).ToNot(BeNil())
g.Expect(got.PostRenderStrategy).To(Equal(action.PostRenderStrategyNoHooks))
})
}
36 changes: 36 additions & 0 deletions internal/action/post_render.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
Copyright 2026 The Flux 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 action

import (
"helm.sh/helm/v4/pkg/action"

v2 "github.com/fluxcd/helm-controller/api/v2"
)

// toHelmPostRenderStrategy converts the API PostRenderStrategy to the Helm SDK value.
// If the strategy is not set, it defaults to PostRenderStrategyCombined (Helm 4 default),
// or PostRenderStrategyNoHooks when UseHelm3Defaults is enabled.
func toHelmPostRenderStrategy(strategy v2.PostRenderStrategy) action.PostRenderStrategy {
if strategy == "" {
if UseHelm3Defaults {
return action.PostRenderStrategyNoHooks
}
return action.PostRenderStrategyCombined
}
return action.PostRenderStrategy(strategy)
}
Comment thread
vocarista marked this conversation as resolved.
3 changes: 1 addition & 2 deletions internal/action/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"errors"
"fmt"

"helm.sh/helm/v4/pkg/action"
helmaction "helm.sh/helm/v4/pkg/action"
helmchartutil "helm.sh/helm/v4/pkg/chart/common"
helmchart "helm.sh/helm/v4/pkg/chart/v2"
Expand Down Expand Up @@ -127,7 +126,7 @@ func newUpgrade(config *helmaction.Configuration, obj *v2.HelmRelease, opts []Up
}

upgrade.PostRenderer = postrender.BuildPostRenderers(obj)
upgrade.PostRenderStrategy = action.PostRenderStrategyNoHooks
upgrade.PostRenderStrategy = toHelmPostRenderStrategy(obj.GetPostRenderStrategy())

for _, opt := range opts {
opt(upgrade)
Expand Down
Loading
Loading