api: Add the `readyExpr` field to `dependsOn`

Extend the readiness evaluation of dependencies with CEL expressions

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
This commit is contained in:
Stefan Prodan 2025-07-22 21:09:30 +03:00
parent fc12477df0
commit 4ffe621c6b
No known key found for this signature in database
GPG Key ID: 3299AEB0E4085BAF
6 changed files with 137 additions and 23 deletions

View File

@ -100,11 +100,11 @@ type HelmReleaseSpec struct {
// +optional
StorageNamespace string `json:"storageNamespace,omitempty"`
// DependsOn may contain a meta.NamespacedObjectReference slice with
// DependsOn may contain a DependencyReference slice with
// references to HelmRelease resources that must be ready before this HelmRelease
// can be reconciled.
// +optional
DependsOn []meta.NamespacedObjectReference `json:"dependsOn,omitempty"`
DependsOn []DependencyReference `json:"dependsOn,omitempty"`
// Timeout is the time to wait for any individual Kubernetes operation (like Jobs
// for hooks) during the performance of a Helm action. Defaults to '5m0s'.
@ -1265,9 +1265,19 @@ func (in HelmRelease) UsePersistentClient() bool {
return *in.Spec.PersistentClient
}
// GetDependsOn returns the list of dependencies across-namespaces.
// GetDependsOn returns the dependencies as a list of meta.NamespacedObjectReference.
//
// This function makes the HelmRelease type conformant with the meta.ObjectWithDependencies interface
// and allows the controller-runtime to index HelmReleases by their dependencies.
func (in HelmRelease) GetDependsOn() []meta.NamespacedObjectReference {
return in.Spec.DependsOn
deps := make([]meta.NamespacedObjectReference, len(in.Spec.DependsOn))
for i := range in.Spec.DependsOn {
deps[i] = meta.NamespacedObjectReference{
Name: in.Spec.DependsOn[i].Name,
Namespace: in.Spec.DependsOn[i].Namespace,
}
}
return deps
}
// GetConditions returns the status conditions of the object.

View File

@ -68,3 +68,23 @@ type CrossNamespaceSourceReference struct {
// +optional
Namespace string `json:"namespace,omitempty"`
}
// DependencyReference defines a HelmRelease dependency on another HelmRelease resource.
type DependencyReference struct {
// Name of the referent.
// +required
Name string `json:"name"`
// Namespace of the referent, defaults to the namespace of the HelmRelease
// resource object that contains the reference.
// +optional
Namespace string `json:"namespace,omitempty"`
// ReadyExpr is a CEL expression that can be used to assess the readiness
// of a dependency. When specified, the built-in readiness check
// is replaced by the logic defined in the CEL expression.
// To make the CEL expression additive to the built-in readiness check,
// the feature gate `AdditiveCELDependencyCheck` must be set to `true`.
// +optional
ReadyExpr string `json:"readyExpr,omitempty"`
}

View File

@ -87,6 +87,21 @@ func (in *CrossNamespaceSourceReference) DeepCopy() *CrossNamespaceSourceReferen
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DependencyReference) DeepCopyInto(out *DependencyReference) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DependencyReference.
func (in *DependencyReference) DeepCopy() *DependencyReference {
if in == nil {
return nil
}
out := new(DependencyReference)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DriftDetection) DeepCopyInto(out *DriftDetection) {
*out = *in
@ -305,7 +320,7 @@ func (in *HelmReleaseSpec) DeepCopyInto(out *HelmReleaseSpec) {
}
if in.DependsOn != nil {
in, out := &in.DependsOn, &out.DependsOn
*out = make([]meta.NamespacedObjectReference, len(*in))
*out = make([]DependencyReference, len(*in))
copy(*out, *in)
}
if in.Timeout != nil {

View File

@ -239,20 +239,28 @@ spec:
type: object
dependsOn:
description: |-
DependsOn may contain a meta.NamespacedObjectReference slice with
DependsOn may contain a DependencyReference slice with
references to HelmRelease resources that must be ready before this HelmRelease
can be reconciled.
items:
description: |-
NamespacedObjectReference contains enough information to locate the referenced Kubernetes resource object in any
namespace.
description: DependencyReference defines a HelmRelease dependency
on another HelmRelease resource.
properties:
name:
description: Name of the referent.
type: string
namespace:
description: Namespace of the referent, when not specified it
acts as LocalObjectReference.
description: |-
Namespace of the referent, defaults to the namespace of the HelmRelease
resource object that contains the reference.
type: string
readyExpr:
description: |-
ReadyExpr is a CEL expression that can be used to assess the readiness
of a dependency. When specified, the built-in readiness check
is replaced by the logic defined in the CEL expression.
To make the CEL expression additive to the built-in readiness check,
the feature gate `AdditiveCELDependencyCheck` must be set to `true`.
type: string
required:
- name

View File

@ -187,14 +187,14 @@ Defaults to the namespace of the HelmRelease.</p>
<td>
<code>dependsOn</code><br>
<em>
<a href="https://godoc.org/github.com/fluxcd/pkg/apis/meta#NamespacedObjectReference">
[]github.com/fluxcd/pkg/apis/meta.NamespacedObjectReference
<a href="#helm.toolkit.fluxcd.io/v2.DependencyReference">
[]DependencyReference
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>DependsOn may contain a meta.NamespacedObjectReference slice with
<p>DependsOn may contain a DependencyReference slice with
references to HelmRelease resources that must be ready before this HelmRelease
can be reconciled.</p>
</td>
@ -615,6 +615,67 @@ resource object that contains the reference.</p>
</table>
</div>
</div>
<h3 id="helm.toolkit.fluxcd.io/v2.DependencyReference">DependencyReference
</h3>
<p>
(<em>Appears on:</em>
<a href="#helm.toolkit.fluxcd.io/v2.HelmReleaseSpec">HelmReleaseSpec</a>)
</p>
<p>DependencyReference defines a HelmRelease dependency on another HelmRelease resource.</p>
<div class="md-typeset__scrollwrap">
<div class="md-typeset__table">
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>name</code><br>
<em>
string
</em>
</td>
<td>
<p>Name of the referent.</p>
</td>
</tr>
<tr>
<td>
<code>namespace</code><br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>Namespace of the referent, defaults to the namespace of the HelmRelease
resource object that contains the reference.</p>
</td>
</tr>
<tr>
<td>
<code>readyExpr</code><br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>ReadyExpr is a CEL expression that can be used to assess the readiness
of a dependency. When specified, the built-in readiness check
is replaced by the logic defined in the CEL expression.
To make the CEL expression additive to the built-in readiness check,
the feature gate <code>AdditiveCELDependencyCheck</code> must be set to <code>true</code>.</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<h3 id="helm.toolkit.fluxcd.io/v2.DriftDetection">DriftDetection
</h3>
<p>
@ -1258,14 +1319,14 @@ Defaults to the namespace of the HelmRelease.</p>
<td>
<code>dependsOn</code><br>
<em>
<a href="https://godoc.org/github.com/fluxcd/pkg/apis/meta#NamespacedObjectReference">
[]github.com/fluxcd/pkg/apis/meta.NamespacedObjectReference
<a href="#helm.toolkit.fluxcd.io/v2.DependencyReference">
[]DependencyReference
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>DependsOn may contain a meta.NamespacedObjectReference slice with
<p>DependsOn may contain a DependencyReference slice with
references to HelmRelease resources that must be ready before this HelmRelease
can be reconciled.</p>
</td>

View File

@ -93,7 +93,7 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) {
Namespace: "mock",
},
Spec: v2.HelmReleaseSpec{
DependsOn: []meta.NamespacedObjectReference{
DependsOn: []v2.DependencyReference{
{
Name: "dependency",
},
@ -2818,7 +2818,7 @@ func TestHelmReleaseReconciler_checkDependencies(t *testing.T) {
Namespace: "some-namespace",
},
Spec: v2.HelmReleaseSpec{
DependsOn: []meta.NamespacedObjectReference{
DependsOn: []v2.DependencyReference{
{
Name: "dependency-1",
},
@ -2869,7 +2869,7 @@ func TestHelmReleaseReconciler_checkDependencies(t *testing.T) {
Namespace: "some-namespace",
},
Spec: v2.HelmReleaseSpec{
DependsOn: []meta.NamespacedObjectReference{
DependsOn: []v2.DependencyReference{
{
Name: "dependency-1",
},
@ -2904,7 +2904,7 @@ func TestHelmReleaseReconciler_checkDependencies(t *testing.T) {
Namespace: "some-namespace",
},
Spec: v2.HelmReleaseSpec{
DependsOn: []meta.NamespacedObjectReference{
DependsOn: []v2.DependencyReference{
{
Name: "dependency-1",
},
@ -2939,7 +2939,7 @@ func TestHelmReleaseReconciler_checkDependencies(t *testing.T) {
Namespace: "some-namespace",
},
Spec: v2.HelmReleaseSpec{
DependsOn: []meta.NamespacedObjectReference{
DependsOn: []v2.DependencyReference{
{
Name: "dependency-1",
},
@ -2971,7 +2971,7 @@ func TestHelmReleaseReconciler_checkDependencies(t *testing.T) {
Namespace: "some-namespace",
},
Spec: v2.HelmReleaseSpec{
DependsOn: []meta.NamespacedObjectReference{
DependsOn: []v2.DependencyReference{
{
Name: "dependency-1",
},