diff --git a/api/v2/helmrelease_types.go b/api/v2/helmrelease_types.go index 73cea73a4..19ec1518f 100644 --- a/api/v2/helmrelease_types.go +++ b/api/v2/helmrelease_types.go @@ -101,7 +101,7 @@ type HelmReleaseSpec struct { StorageNamespace string `json:"storageNamespace,omitempty"` // DependsOn may contain a DependencyReference slice with - // references to HelmRelease resources that must be ready before this HelmRelease + // references to Kubernetes resources that must be ready before this HelmRelease // can be reconciled. // +optional DependsOn []DependencyReference `json:"dependsOn,omitempty"` diff --git a/api/v2/reference_types.go b/api/v2/reference_types.go index 9f3ee4a1c..d6958394e 100644 --- a/api/v2/reference_types.go +++ b/api/v2/reference_types.go @@ -69,17 +69,32 @@ type CrossNamespaceSourceReference struct { Namespace string `json:"namespace,omitempty"` } -// DependencyReference defines a HelmRelease dependency on another HelmRelease resource. +// DependencyReference defines a HelmRelease dependency on a Kubernetes resource. +// When the dependency is a HelmRelease, defaults are applied during reconciliation. type DependencyReference struct { - // Name of the referent. + // APIVersion of the resource to depend on, defaults to the HelmRelease API + // group version when the dependency is a HelmRelease. + // +optional + APIVersion string `json:"apiVersion,omitempty"` + + // Kind of the resource to depend on, defaults to HelmRelease. + // +optional + Kind string `json:"kind,omitempty"` + + // Name of the resource to depend on. // +required Name string `json:"name"` - // Namespace of the referent, defaults to the namespace of the HelmRelease - // resource object that contains the reference. + // Namespace of the resource to depend on, defaults to the namespace of the + // HelmRelease resource object that contains the reference. // +optional Namespace string `json:"namespace,omitempty"` + // Ready checks if the resource Ready status condition is true, defaults to + // true when the dependency is a HelmRelease. + // +optional + Ready *bool `json:"ready,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. diff --git a/api/v2/zz_generated.deepcopy.go b/api/v2/zz_generated.deepcopy.go index f371a2181..e97dc01b9 100644 --- a/api/v2/zz_generated.deepcopy.go +++ b/api/v2/zz_generated.deepcopy.go @@ -90,6 +90,11 @@ func (in *CrossNamespaceSourceReference) DeepCopy() *CrossNamespaceSourceReferen // 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 + if in.Ready != nil { + in, out := &in.Ready, &out.Ready + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DependencyReference. @@ -321,7 +326,9 @@ func (in *HelmReleaseSpec) DeepCopyInto(out *HelmReleaseSpec) { if in.DependsOn != nil { in, out := &in.DependsOn, &out.DependsOn *out = make([]DependencyReference, len(*in)) - copy(*out, *in) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } if in.Timeout != nil { in, out := &in.Timeout, &out.Timeout diff --git a/config/crd/bases/helm.toolkit.fluxcd.io_helmreleases.yaml b/config/crd/bases/helm.toolkit.fluxcd.io_helmreleases.yaml index bf06f7d30..a7a5db823 100644 --- a/config/crd/bases/helm.toolkit.fluxcd.io_helmreleases.yaml +++ b/config/crd/bases/helm.toolkit.fluxcd.io_helmreleases.yaml @@ -241,20 +241,35 @@ spec: dependsOn: description: |- DependsOn may contain a DependencyReference slice with - references to HelmRelease resources that must be ready before this HelmRelease + references to Kubernetes resources that must be ready before this HelmRelease can be reconciled. items: - description: DependencyReference defines a HelmRelease dependency - on another HelmRelease resource. + description: |- + DependencyReference defines a HelmRelease dependency on a Kubernetes resource. + When the dependency is a HelmRelease, defaults are applied during reconciliation. properties: + apiVersion: + description: |- + APIVersion of the resource to depend on, defaults to the HelmRelease API + group version when the dependency is a HelmRelease. + type: string + kind: + description: Kind of the resource to depend on, defaults to + HelmRelease. + type: string name: - description: Name of the referent. + description: Name of the resource to depend on. type: string namespace: description: |- - Namespace of the referent, defaults to the namespace of the HelmRelease - resource object that contains the reference. + Namespace of the resource to depend on, defaults to the namespace of the + HelmRelease resource object that contains the reference. type: string + ready: + description: |- + Ready checks if the resource Ready status condition is true, defaults to + true when the dependency is a HelmRelease. + type: boolean readyExpr: description: |- ReadyExpr is a CEL expression that can be used to assess the readiness diff --git a/docs/api/v2/helm.md b/docs/api/v2/helm.md index 639745c4f..b8f99936f 100644 --- a/docs/api/v2/helm.md +++ b/docs/api/v2/helm.md @@ -195,7 +195,7 @@ Defaults to the namespace of the HelmRelease.

(Optional)

DependsOn may contain a DependencyReference slice with -references to HelmRelease resources that must be ready before this HelmRelease +references to Kubernetes resources that must be ready before this HelmRelease can be reconciled.

@@ -675,7 +675,8 @@ resource object that contains the reference.

(Appears on: HelmReleaseSpec)

-

DependencyReference defines a HelmRelease dependency on another HelmRelease resource.

+

DependencyReference defines a HelmRelease dependency on a Kubernetes resource. +When the dependency is a HelmRelease, defaults are applied during reconciliation.

@@ -688,13 +689,38 @@ resource object that contains the reference.

+ + + + + + + + @@ -706,8 +732,21 @@ string + + + + @@ -1381,7 +1420,7 @@ Defaults to the namespace of the HelmRelease.

diff --git a/internal/controller/helmrelease_controller.go b/internal/controller/helmrelease_controller.go index e0c3315da..7af14e5df 100644 --- a/internal/controller/helmrelease_controller.go +++ b/internal/controller/helmrelease_controller.go @@ -27,13 +27,16 @@ import ( "github.com/fluxcd/cli-utils/pkg/kstatus/polling" "github.com/fluxcd/cli-utils/pkg/kstatus/polling/clusterreader" "github.com/fluxcd/cli-utils/pkg/kstatus/polling/engine" + "github.com/fluxcd/cli-utils/pkg/kstatus/status" celtypes "github.com/google/cel-go/common/types" chart "helm.sh/helm/v4/pkg/chart/v2" corev1 "k8s.io/api/core/v1" apiequality "k8s.io/apimachinery/pkg/api/equality" apierrors "k8s.io/apimachinery/pkg/api/errors" apimeta "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" "k8s.io/apimachinery/pkg/types" apierrutil "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/wait" @@ -45,6 +48,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/reconcile" + objectutils "github.com/fluxcd/cli-utils/pkg/object" aclv1 "github.com/fluxcd/pkg/apis/acl" "github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/auth" @@ -61,6 +65,7 @@ import ( "github.com/fluxcd/pkg/runtime/object" "github.com/fluxcd/pkg/runtime/patch" "github.com/fluxcd/pkg/ssa" + ssautil "github.com/fluxcd/pkg/ssa/utils" sourcev1 "github.com/fluxcd/source-controller/api/v1" @@ -617,29 +622,62 @@ func (r *HelmReleaseReconciler) checkDependencies(ctx context.Context, obj *v2.H } for _, depRef := range obj.Spec.DependsOn { - depName := types.NamespacedName{ - Namespace: depRef.Namespace, + // Default the dependency Kind to HelmRelease if unset. + if depRef.Kind == "" { + depRef.Kind = v2.HelmReleaseKind + } + + // Apply HelmRelease defaults if the dependency is a HelmRelease. + if depRef.Kind == v2.HelmReleaseKind { + // Default APIVersion to HelmRelease if unset. + if depRef.APIVersion == "" { + depRef.APIVersion = v2.GroupVersion.String() + } + // Default namespace to the dependent's namespace if unset. + if depRef.Namespace == "" { + depRef.Namespace = obj.GetNamespace() + } + // Default readiness check to true if unset. + if depRef.Ready == nil { + depRef.Ready = new(true) + } + } + + depMd := objectutils.ObjMetadata{ + GroupKind: schema.GroupKind{Kind: depRef.Kind}, Name: depRef.Name, + Namespace: depRef.Namespace, } - if depName.Namespace == "" { - depName.Namespace = obj.GetNamespace() + depObj := &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": depRef.APIVersion, + "kind": depRef.Kind, + "metadata": map[string]any{ + "name": depRef.Name, + "namespace": depRef.Namespace, + }, + }, } // Check if the dependency exists by querying // the API server bypassing the cache. - dep := &v2.HelmRelease{} - if err := r.APIReader.Get(ctx, depName, dep); err != nil { - return fmt.Errorf("unable to get '%s' dependency: %w", depName, err) + if err := r.APIReader.Get(ctx, client.ObjectKeyFromObject(depObj), depObj); err != nil { + return fmt.Errorf("unable to get '%s/%s' dependency: %w", depRef.APIVersion, ssautil.FmtObjMetadata(depMd), err) + } + + // Skip all readiness checks if unset or set to false. + if depRef.Ready == nil || !*depRef.Ready { + continue } // Evaluate the CEL expression (if specified) to determine if the dependency is ready. if depRef.ReadyExpr != "" { - ready, err := r.evalReadyExpr(ctx, depRef.ReadyExpr, objMap, dep) + ready, err := r.evalReadyExpr(ctx, depRef.ReadyExpr, objMap, depObj) if err != nil { return err } if !ready { - return fmt.Errorf("dependency '%s' is not ready according to readyExpr eval", depName) + return fmt.Errorf("dependency '%s/%s' is not ready according to readyExpr eval", depRef.APIVersion, ssautil.FmtObjMetadata(depMd)) } } @@ -651,10 +689,30 @@ func (r *HelmReleaseReconciler) checkDependencies(ctx context.Context, obj *v2.H // Check if the dependency observed generation is up to date // and if the dependency is in a ready state. - if dep.Generation != dep.Status.ObservedGeneration || !conditions.IsTrue(dep, meta.ReadyCondition) { - return fmt.Errorf("dependency '%s' is not ready", depName) + stat, err := status.Compute(depObj) + if err != nil { + return fmt.Errorf("dependency '%s/%s' is not ready: %w", depRef.APIVersion, ssautil.FmtObjMetadata(depMd), err) + } + if stat.Status != status.CurrentStatus { + return fmt.Errorf("dependency '%s/%s' is not ready: status %s", depRef.APIVersion, ssautil.FmtObjMetadata(depMd), stat.Status) + } + + // This check only applies to HelmRelease dependencies. + // Additionally check HelmRelease dependencies for readiness. + // kstatus.Compute() tolerates missing conditions, but HelmReleases are expected to have a Ready condition. + if depRef.Kind != v2.HelmReleaseKind { + continue + } + + var dep v2.HelmRelease + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(depObj.Object, &dep); err != nil { + return fmt.Errorf("failed to convert unstructured to HelmRelease: %w", err) + } + if !apimeta.IsStatusConditionTrue(dep.Status.Conditions, meta.ReadyCondition) { + return fmt.Errorf("dependency '%s/%s' is not ready", depRef.APIVersion, ssautil.FmtObjMetadata(depMd)) } } + return nil } @@ -663,7 +721,7 @@ func (r *HelmReleaseReconciler) evalReadyExpr( ctx context.Context, expr string, selfMap map[string]any, - dep *v2.HelmRelease, + dep *unstructured.Unstructured, ) (bool, error) { const ( selfName = "self" @@ -675,7 +733,7 @@ func (r *HelmReleaseReconciler) evalReadyExpr( cel.WithOutputType(celtypes.BoolType), cel.WithStructVariables(selfName, depName)) if err != nil { - return false, reconcile.TerminalError(fmt.Errorf("failed to evaluate dependency %s: %w", dep.Name, err)) + return false, reconcile.TerminalError(fmt.Errorf("failed to evaluate dependency %s: %w", dep.GetName(), err)) } depMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(dep) diff --git a/internal/controller/helmrelease_controller_test.go b/internal/controller/helmrelease_controller_test.go index 9e48fda8c..4b9a4b1aa 100644 --- a/internal/controller/helmrelease_controller_test.go +++ b/internal/controller/helmrelease_controller_test.go @@ -117,7 +117,7 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) { g.Expect(obj.Status.Conditions).To(conditions.MatchConditions([]metav1.Condition{ *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, ""), - *conditions.FalseCondition(meta.ReadyCondition, meta.DependencyNotReadyReason, "dependency 'mock/dependency' is not ready"), + *conditions.FalseCondition(meta.ReadyCondition, meta.DependencyNotReadyReason, "dependency 'helm.toolkit.fluxcd.io/v2/HelmRelease/mock/dependency' is not ready"), })) }) @@ -2817,6 +2817,20 @@ func TestHelmReleaseReconciler_checkDependencies(t *testing.T) { Name: "dependency-2", Namespace: "some-other-namespace", }, + { + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Pod", + Name: "dependency-3", + Namespace: "some-namespace", + Ready: new(true), + }, + { + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Secret", + Name: "dependency-4", + Namespace: "some-other-namespace", + Ready: new(true), + }, }, }, }, @@ -2855,6 +2869,35 @@ func TestHelmReleaseReconciler_checkDependencies(t *testing.T) { }, }, }, + &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Pod", + }, + ObjectMeta: metav1.ObjectMeta{ + Generation: 1, + Name: "dependency-3", + Namespace: "some-namespace", + }, + Status: corev1.PodStatus{ + ObservedGeneration: 1, + Conditions: []corev1.PodCondition{ + {Type: corev1.PodReady, Status: corev1.ConditionTrue}, + }, + Phase: corev1.PodRunning, + }, + }, + &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Generation: 1, + Name: "dependency-4", + Namespace: "some-other-namespace", + }, + }, }, expect: func(g *WithT, err error) { g.Expect(err).ToNot(HaveOccurred()) @@ -2891,6 +2934,14 @@ dep.status.conditions.filter(e, e.type == 'Ready').all(e, e.status == 'True') && dep.metadata.generation == dep.status.observedGeneration `, }, + { + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Secret", + Name: "dependency-3", + Namespace: "some-other-namespace", + Ready: new(true), + ReadyExpr: "dep.metadata.generation == 2 && !has(dep.data)", + }, }, }, }, @@ -2930,6 +2981,17 @@ dep.metadata.generation == dep.status.observedGeneration }, }, }, + &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Generation: 2, + Name: "dependency-3", + Namespace: "some-other-namespace", + }, + }, }, expect: func(g *WithT, err error) { g.Expect(err).ToNot(HaveOccurred()) @@ -2978,6 +3040,54 @@ dep.metadata.generation == dep.status.observedGeneration g.Expect(err.Error()).To(ContainSubstring("is not ready")) }, }, + { + name: "error on other dependency with ObservedGeneration < Generation", + obj: &v2.HelmRelease{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v2.GroupVersion.String(), + Kind: v2.HelmReleaseKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "dependant", + Namespace: "some-namespace", + }, + Spec: v2.HelmReleaseSpec{ + DependsOn: []v2.DependencyReference{ + { + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Pod", + Name: "dependency-1", + Namespace: "some-namespace", + Ready: new(true), + }, + }, + }, + }, + objects: []client.Object{ + &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Pod", + }, + ObjectMeta: metav1.ObjectMeta{ + Generation: 2, + Name: "dependency-1", + Namespace: "some-namespace", + }, + Status: corev1.PodStatus{ + ObservedGeneration: 1, + Conditions: []corev1.PodCondition{ + {Type: corev1.PodReady, Status: corev1.ConditionTrue}, + }, + Phase: corev1.PodRunning, + }, + }, + }, + expect: func(g *WithT, err error) { + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("is not ready")) + }, + }, { name: "error on dependency with ObservedGeneration = Generation and ReadyCondition = False", obj: &v2.HelmRelease{ @@ -3021,6 +3131,54 @@ dep.metadata.generation == dep.status.observedGeneration g.Expect(err.Error()).To(ContainSubstring("is not ready")) }, }, + { + name: "error on other dependency with ObservedGeneration = Generation and ReadyCondition = False", + obj: &v2.HelmRelease{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v2.GroupVersion.String(), + Kind: v2.HelmReleaseKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "dependant", + Namespace: "some-namespace", + }, + Spec: v2.HelmReleaseSpec{ + DependsOn: []v2.DependencyReference{ + { + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Pod", + Name: "dependency-1", + Namespace: "some-namespace", + Ready: new(true), + }, + }, + }, + }, + objects: []client.Object{ + &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Pod", + }, + ObjectMeta: metav1.ObjectMeta{ + Generation: 1, + Name: "dependency-1", + Namespace: "some-namespace", + }, + Status: corev1.PodStatus{ + ObservedGeneration: 1, + Conditions: []corev1.PodCondition{ + {Type: corev1.PodReady, Status: corev1.ConditionFalse}, + }, + Phase: corev1.PodRunning, + }, + }, + }, + expect: func(g *WithT, err error) { + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("is not ready")) + }, + }, { name: "error on dependency without conditions", obj: &v2.HelmRelease{ @@ -3061,6 +3219,44 @@ dep.metadata.generation == dep.status.observedGeneration g.Expect(err.Error()).To(ContainSubstring("is not ready")) }, }, + { + name: "error on other dependency without conditions", + obj: &v2.HelmRelease{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v2.GroupVersion.String(), + Kind: v2.HelmReleaseKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "dependant", + Namespace: "some-namespace", + }, + Spec: v2.HelmReleaseSpec{ + DependsOn: []v2.DependencyReference{ + { + APIVersion: apiextensionsv1.SchemeGroupVersion.String(), + Kind: "CustomResourceDefinition", + Name: "helmreleases.helm.toolkit.fluxcd.io", + Ready: new(true), + }, + }, + }, + }, + objects: []client.Object{ + &apiextensionsv1.CustomResourceDefinition{ + TypeMeta: metav1.TypeMeta{ + APIVersion: apiextensionsv1.SchemeGroupVersion.String(), + Kind: "CustomResourceDefinition", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "helmreleases.helm.toolkit.fluxcd.io", + }, + }, + }, + expect: func(g *WithT, err error) { + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("is not ready")) + }, + }, { name: "error on missing dependency", obj: &v2.HelmRelease{ @@ -3085,6 +3281,65 @@ dep.metadata.generation == dep.status.observedGeneration g.Expect(apierrors.IsNotFound(err)).To(BeTrue()) }, }, + { + name: "error on other missing dependency", + obj: &v2.HelmRelease{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v2.GroupVersion.String(), + Kind: v2.HelmReleaseKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "dependant", + Namespace: "some-namespace", + }, + Spec: v2.HelmReleaseSpec{ + DependsOn: []v2.DependencyReference{ + { + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Secret", + Name: "dependency-1", + Namespace: "some-namespace", + }, + }, + }, + }, + expect: func(g *WithT, err error) { + g.Expect(err).To(HaveOccurred()) + g.Expect(apierrors.IsNotFound(err)).To(BeTrue()) + }, + }, + { + name: "error on multiple missing dependencies", + obj: &v2.HelmRelease{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v2.GroupVersion.String(), + Kind: v2.HelmReleaseKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "dependant", + Namespace: "some-namespace", + }, + Spec: v2.HelmReleaseSpec{ + DependsOn: []v2.DependencyReference{ + { + APIVersion: apiextensionsv1.SchemeGroupVersion.String(), + Kind: "CustomResourceDefinition", + Name: "kustomizations.kustomize.toolkit.fluxcd.io", + }, + { + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Secret", + Name: "dependency-1", + Namespace: "some-namespace", + }, + }, + }, + }, + expect: func(g *WithT, err error) { + g.Expect(err).To(HaveOccurred()) + g.Expect(apierrors.IsNotFound(err)).To(BeTrue()) + }, + }, { name: "error on dependency with readyExpr", obj: &v2.HelmRelease{ @@ -3126,6 +3381,48 @@ dep.metadata.generation == dep.status.observedGeneration g.Expect(err.Error()).To(ContainSubstring("is not ready according to readyExpr eval")) }, }, + { + name: "error on other dependency with readyExpr", + obj: &v2.HelmRelease{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v2.GroupVersion.String(), + Kind: v2.HelmReleaseKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "dependant", + Namespace: "some-namespace", + }, + Spec: v2.HelmReleaseSpec{ + DependsOn: []v2.DependencyReference{ + { + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Secret", + Name: "dependency-1", + Namespace: "some-namespace", + Ready: new(true), + ReadyExpr: "self.metadata.name == dep.metadata.name", + }, + }, + }, + }, + objects: []client.Object{ + &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Generation: 1, + Name: "dependency-1", + Namespace: "some-namespace", + }, + }, + }, + expect: func(g *WithT, err error) { + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("is not ready according to readyExpr eval")) + }, + }, { name: "terminal error on dependency with invalid readyExpr", obj: &v2.HelmRelease{
+apiVersion
+ +string + +
+(Optional) +

APIVersion of the resource to depend on, defaults to the HelmRelease API +group version when the dependency is a HelmRelease.

+
+kind
+ +string + +
+(Optional) +

Kind of the resource to depend on, defaults to HelmRelease.

+
name
string
-

Name of the referent.

+

Name of the resource to depend on.

(Optional) -

Namespace of the referent, defaults to the namespace of the HelmRelease -resource object that contains the reference.

+

Namespace of the resource to depend on, defaults to the namespace of the +HelmRelease resource object that contains the reference.

+
+ready
+ +bool + +
+(Optional) +

Ready checks if the resource Ready status condition is true, defaults to +true when the dependency is a HelmRelease.

(Optional)

DependsOn may contain a DependencyReference slice with -references to HelmRelease resources that must be ready before this HelmRelease +references to Kubernetes resources that must be ready before this HelmRelease can be reconciled.