Skip to content

Commit 83e4fdf

Browse files
committed
pkg/scaffold: add status conditions to scaffolds
1 parent 8a3fb4a commit 83e4fdf

File tree

6 files changed

+139
-98
lines changed

6 files changed

+139
-98
lines changed

hack/tests/e2e-helm.sh

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ test_operator() {
3636
# create CR
3737
kubectl create -f deploy/crds/helm_v1alpha1_nginx_cr.yaml
3838
trap_add 'kubectl delete --ignore-not-found -f ${OPERATORDIR}/deploy/crds/helm_v1alpha1_nginx_cr.yaml' EXIT
39-
if ! timeout 1m bash -c -- 'until kubectl get nginxes.helm.example.com example-nginx -o jsonpath="{..status.conditions[0].release.info.status.code}" | grep 1; do sleep 1; done';
39+
if ! timeout 1m bash -c -- 'until kubectl get nginxes.helm.example.com example-nginx -o jsonpath="{..status.conditions[1].release.info.status.code}" | grep 1; do sleep 1; done';
4040
then
4141
kubectl logs deployment/nginx-operator
4242
exit 1
4343
fi
4444

45-
release_name=$(kubectl get nginxes.helm.example.com example-nginx -o jsonpath="{..status.conditions[0].release.name}")
45+
release_name=$(kubectl get nginxes.helm.example.com example-nginx -o jsonpath="{..status.conditions[1].release.name}")
4646
nginx_deployment=$(kubectl get deployment -l "app.kubernetes.io/instance=${release_name}" -o jsonpath="{..metadata.name}")
4747

4848
if ! timeout 1m kubectl rollout status deployment/${nginx_deployment};

pkg/helm/controller/reconcile.go

+42-62
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919
"fmt"
2020
"time"
2121

22-
corev1 "k8s.io/api/core/v1"
2322
apierrors "k8s.io/apimachinery/pkg/api/errors"
2423
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2524
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -30,7 +29,6 @@ import (
3029
"github.com/operator-framework/operator-sdk/internal/util/diffutil"
3130
"github.com/operator-framework/operator-sdk/pkg/helm/internal/types"
3231
"github.com/operator-framework/operator-sdk/pkg/helm/release"
33-
osdkstatus "github.com/operator-framework/operator-sdk/pkg/status"
3432
)
3533

3634
var _ reconcile.Reconciler = &HelmOperatorReconciler{}
@@ -93,22 +91,18 @@ func (r HelmOperatorReconciler) Reconcile(request reconcile.Request) (reconcile.
9391
return reconcile.Result{Requeue: true}, err
9492
}
9593

96-
status.SetCondition(&types.HelmAppCondition{
97-
SimpleCondition: osdkstatus.SimpleCondition{
98-
Type: types.ConditionInitialized,
99-
Status: corev1.ConditionTrue,
100-
},
94+
status.SetCondition(types.HelmAppCondition{
95+
Type: types.ConditionInitialized,
96+
Status: types.StatusTrue,
10197
})
10298

10399
if err := manager.Sync(context.TODO()); err != nil {
104100
log.Error(err, "Failed to sync release")
105-
status.SetCondition(&types.HelmAppCondition{
106-
SimpleCondition: osdkstatus.SimpleCondition{
107-
Type: types.ConditionIrreconcilable,
108-
Status: corev1.ConditionTrue,
109-
Reason: types.ReasonReconcileError,
110-
Message: err.Error(),
111-
},
101+
status.SetCondition(types.HelmAppCondition{
102+
Type: types.ConditionIrreconcilable,
103+
Status: types.StatusTrue,
104+
Reason: types.ReasonReconcileError,
105+
Message: err.Error(),
112106
})
113107
_ = r.updateResourceStatus(o, status)
114108
return reconcile.Result{}, err
@@ -124,13 +118,11 @@ func (r HelmOperatorReconciler) Reconcile(request reconcile.Request) (reconcile.
124118
uninstalledRelease, err := manager.UninstallRelease(context.TODO())
125119
if err != nil && err != release.ErrNotFound {
126120
log.Error(err, "Failed to uninstall release")
127-
status.SetCondition(&types.HelmAppCondition{
128-
SimpleCondition: osdkstatus.SimpleCondition{
129-
Type: types.ConditionReleaseFailed,
130-
Status: corev1.ConditionTrue,
131-
Reason: types.ReasonUninstallError,
132-
Message: err.Error(),
133-
},
121+
status.SetCondition(types.HelmAppCondition{
122+
Type: types.ConditionReleaseFailed,
123+
Status: types.StatusTrue,
124+
Reason: types.ReasonUninstallError,
125+
Message: err.Error(),
134126
})
135127
_ = r.updateResourceStatus(o, status)
136128
return reconcile.Result{}, err
@@ -144,12 +136,10 @@ func (r HelmOperatorReconciler) Reconcile(request reconcile.Request) (reconcile.
144136
if log.Enabled() {
145137
fmt.Println(diffutil.Diff(uninstalledRelease.GetManifest(), ""))
146138
}
147-
status.SetCondition(&types.HelmAppCondition{
148-
SimpleCondition: osdkstatus.SimpleCondition{
149-
Type: types.ConditionDeployed,
150-
Status: corev1.ConditionFalse,
151-
Reason: types.ReasonUninstallSuccessful,
152-
},
139+
status.SetCondition(types.HelmAppCondition{
140+
Type: types.ConditionDeployed,
141+
Status: types.StatusFalse,
142+
Reason: types.ReasonUninstallSuccessful,
153143
})
154144
}
155145
if err := r.updateResourceStatus(o, status); err != nil {
@@ -173,13 +163,11 @@ func (r HelmOperatorReconciler) Reconcile(request reconcile.Request) (reconcile.
173163
installedRelease, err := manager.InstallRelease(context.TODO())
174164
if err != nil {
175165
log.Error(err, "Failed to install release")
176-
status.SetCondition(&types.HelmAppCondition{
177-
SimpleCondition: osdkstatus.SimpleCondition{
178-
Type: types.ConditionReleaseFailed,
179-
Status: corev1.ConditionTrue,
180-
Reason: types.ReasonInstallError,
181-
Message: err.Error(),
182-
},
166+
status.SetCondition(types.HelmAppCondition{
167+
Type: types.ConditionReleaseFailed,
168+
Status: types.StatusTrue,
169+
Reason: types.ReasonInstallError,
170+
Message: err.Error(),
183171
Release: installedRelease,
184172
})
185173
_ = r.updateResourceStatus(o, status)
@@ -199,13 +187,11 @@ func (r HelmOperatorReconciler) Reconcile(request reconcile.Request) (reconcile.
199187
fmt.Println(diffutil.Diff("", installedRelease.GetManifest()))
200188
}
201189
log.V(1).Info("Config values", "values", installedRelease.GetConfig())
202-
status.SetCondition(&types.HelmAppCondition{
203-
SimpleCondition: osdkstatus.SimpleCondition{
204-
Type: types.ConditionDeployed,
205-
Status: corev1.ConditionTrue,
206-
Reason: types.ReasonInstallSuccessful,
207-
Message: installedRelease.GetInfo().GetStatus().GetNotes(),
208-
},
190+
status.SetCondition(types.HelmAppCondition{
191+
Type: types.ConditionDeployed,
192+
Status: types.StatusTrue,
193+
Reason: types.ReasonInstallSuccessful,
194+
Message: installedRelease.GetInfo().GetStatus().GetNotes(),
209195
Release: installedRelease,
210196
})
211197
err = r.updateResourceStatus(o, status)
@@ -216,13 +202,11 @@ func (r HelmOperatorReconciler) Reconcile(request reconcile.Request) (reconcile.
216202
previousRelease, updatedRelease, err := manager.UpdateRelease(context.TODO())
217203
if err != nil {
218204
log.Error(err, "Failed to update release")
219-
status.SetCondition(&types.HelmAppCondition{
220-
SimpleCondition: osdkstatus.SimpleCondition{
221-
Type: types.ConditionReleaseFailed,
222-
Status: corev1.ConditionTrue,
223-
Reason: types.ReasonUpdateError,
224-
Message: err.Error(),
225-
},
205+
status.SetCondition(types.HelmAppCondition{
206+
Type: types.ConditionReleaseFailed,
207+
Status: types.StatusTrue,
208+
Reason: types.ReasonUpdateError,
209+
Message: err.Error(),
226210
Release: updatedRelease,
227211
})
228212
_ = r.updateResourceStatus(o, status)
@@ -242,13 +226,11 @@ func (r HelmOperatorReconciler) Reconcile(request reconcile.Request) (reconcile.
242226
fmt.Println(diffutil.Diff(previousRelease.GetManifest(), updatedRelease.GetManifest()))
243227
}
244228
log.V(1).Info("Config values", "values", updatedRelease.GetConfig())
245-
status.SetCondition(&types.HelmAppCondition{
246-
SimpleCondition: osdkstatus.SimpleCondition{
247-
Type: types.ConditionDeployed,
248-
Status: corev1.ConditionTrue,
249-
Reason: types.ReasonUpdateSuccessful,
250-
Message: updatedRelease.GetInfo().GetStatus().GetNotes(),
251-
},
229+
status.SetCondition(types.HelmAppCondition{
230+
Type: types.ConditionDeployed,
231+
Status: types.StatusTrue,
232+
Reason: types.ReasonUpdateSuccessful,
233+
Message: updatedRelease.GetInfo().GetStatus().GetNotes(),
252234
Release: updatedRelease,
253235
})
254236
err = r.updateResourceStatus(o, status)
@@ -258,13 +240,11 @@ func (r HelmOperatorReconciler) Reconcile(request reconcile.Request) (reconcile.
258240
expectedRelease, err := manager.ReconcileRelease(context.TODO())
259241
if err != nil {
260242
log.Error(err, "Failed to reconcile release")
261-
status.SetCondition(&types.HelmAppCondition{
262-
SimpleCondition: osdkstatus.SimpleCondition{
263-
Type: types.ConditionIrreconcilable,
264-
Status: corev1.ConditionTrue,
265-
Reason: types.ReasonReconcileError,
266-
Message: err.Error(),
267-
},
243+
status.SetCondition(types.HelmAppCondition{
244+
Type: types.ConditionIrreconcilable,
245+
Status: types.StatusTrue,
246+
Reason: types.ReasonReconcileError,
247+
Message: err.Error(),
268248
})
269249
_ = r.updateResourceStatus(o, status)
270250
return reconcile.Result{}, err

pkg/helm/internal/types/types.go

+55-25
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ package types
1717
import (
1818
"encoding/json"
1919

20-
"github.com/operator-framework/operator-sdk/pkg/status"
2120
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2221
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2322
"k8s.io/apimachinery/pkg/runtime"
@@ -39,31 +38,41 @@ type HelmApp struct {
3938

4039
type HelmAppSpec map[string]interface{}
4140

41+
type HelmAppConditionType string
4242
type ConditionStatus string
4343
type HelmAppConditionReason string
4444

4545
type HelmAppCondition struct {
46-
status.SimpleCondition
47-
Release *release.Release `json:"release,omitempty"`
46+
Type HelmAppConditionType `json:"type"`
47+
Status ConditionStatus `json:"status"`
48+
Reason HelmAppConditionReason `json:"reason,omitempty"`
49+
Message string `json:"message,omitempty"`
50+
Release *release.Release `json:"release,omitempty"`
51+
52+
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
4853
}
4954

5055
const (
51-
ConditionInitialized status.ConditionType = "Initialized"
52-
ConditionDeployed status.ConditionType = "Deployed"
53-
ConditionReleaseFailed status.ConditionType = "ReleaseFailed"
54-
ConditionIrreconcilable status.ConditionType = "Irreconcilable"
55-
56-
ReasonInstallSuccessful status.ConditionReason = "InstallSuccessful"
57-
ReasonUpdateSuccessful status.ConditionReason = "UpdateSuccessful"
58-
ReasonUninstallSuccessful status.ConditionReason = "UninstallSuccessful"
59-
ReasonInstallError status.ConditionReason = "InstallError"
60-
ReasonUpdateError status.ConditionReason = "UpdateError"
61-
ReasonReconcileError status.ConditionReason = "ReconcileError"
62-
ReasonUninstallError status.ConditionReason = "UninstallError"
56+
ConditionInitialized HelmAppConditionType = "Initialized"
57+
ConditionDeployed HelmAppConditionType = "Deployed"
58+
ConditionReleaseFailed HelmAppConditionType = "ReleaseFailed"
59+
ConditionIrreconcilable HelmAppConditionType = "Irreconcilable"
60+
61+
StatusTrue ConditionStatus = "True"
62+
StatusFalse ConditionStatus = "False"
63+
StatusUnknown ConditionStatus = "Unknown"
64+
65+
ReasonInstallSuccessful HelmAppConditionReason = "InstallSuccessful"
66+
ReasonUpdateSuccessful HelmAppConditionReason = "UpdateSuccessful"
67+
ReasonUninstallSuccessful HelmAppConditionReason = "UninstallSuccessful"
68+
ReasonInstallError HelmAppConditionReason = "InstallError"
69+
ReasonUpdateError HelmAppConditionReason = "UpdateError"
70+
ReasonReconcileError HelmAppConditionReason = "ReconcileError"
71+
ReasonUninstallError HelmAppConditionReason = "UninstallError"
6372
)
6473

6574
type HelmAppStatus struct {
66-
Conditions status.Conditions `json:"conditions,omitempty"`
75+
Conditions []HelmAppCondition `json:"conditions"`
6776
}
6877

6978
func (s *HelmAppStatus) ToMap() (map[string]interface{}, error) {
@@ -81,17 +90,38 @@ func (s *HelmAppStatus) ToMap() (map[string]interface{}, error) {
8190
// SetCondition sets a condition on the status object. If the condition already
8291
// exists, it will be replaced. SetCondition does not update the resource in
8392
// the cluster.
84-
func (s *HelmAppStatus) SetCondition(condition *HelmAppCondition) *HelmAppStatus {
85-
s.Conditions.SetCondition(condition)
93+
func (s *HelmAppStatus) SetCondition(condition HelmAppCondition) *HelmAppStatus {
94+
now := metav1.Now()
95+
for i := range s.Conditions {
96+
if s.Conditions[i].Type == condition.Type {
97+
if s.Conditions[i].Status != condition.Status {
98+
condition.LastTransitionTime = now
99+
} else {
100+
condition.LastTransitionTime = s.Conditions[i].LastTransitionTime
101+
}
102+
s.Conditions[i] = condition
103+
return s
104+
}
105+
}
106+
107+
// If the condition does not exist,
108+
// initialize the lastTransitionTime
109+
condition.LastTransitionTime = now
110+
s.Conditions = append(s.Conditions, condition)
86111
return s
87112
}
88113

89114
// RemoveCondition removes the condition with the passed condition type from
90115
// the status object. If the condition is not already present, the returned
91116
// status object is returned unchanged. RemoveCondition does not update the
92117
// resource in the cluster.
93-
func (s *HelmAppStatus) RemoveCondition(conditionType status.ConditionType) *HelmAppStatus {
94-
s.Conditions.RemoveCondition(status.ConditionType(conditionType))
118+
func (s *HelmAppStatus) RemoveCondition(conditionType HelmAppConditionType) *HelmAppStatus {
119+
for i := range s.Conditions {
120+
if s.Conditions[i].Type == conditionType {
121+
s.Conditions = append(s.Conditions[:i], s.Conditions[i+1:]...)
122+
return s
123+
}
124+
}
95125
return s
96126
}
97127

@@ -101,12 +131,12 @@ func StatusFor(cr *unstructured.Unstructured) *HelmAppStatus {
101131
case *HelmAppStatus:
102132
return s
103133
case map[string]interface{}:
104-
var helmAppStatus *HelmAppStatus
105-
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(s, helmAppStatus); err != nil {
106-
return &HelmAppStatus{Conditions: *status.NewConditions()}
134+
var status *HelmAppStatus
135+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(s, &status); err != nil {
136+
return &HelmAppStatus{}
107137
}
108-
return helmAppStatus
138+
return status
109139
default:
110-
return &HelmAppStatus{Conditions: *status.NewConditions()}
140+
return &HelmAppStatus{}
111141
}
112142
}

pkg/helm/release/manager.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func (m manager) IsUpdateRequired() bool {
9494
// Sync ensures the Helm storage backend is in sync with the status of the
9595
// custom resource.
9696
func (m *manager) Sync(ctx context.Context) error {
97-
if err := m.syncReleaseStatus(m.status); err != nil {
97+
if err := m.syncReleaseStatus(*m.status); err != nil {
9898
return fmt.Errorf("failed to sync release status to storage backend: %s", err)
9999
}
100100

@@ -147,17 +147,17 @@ func (m *manager) Sync(ctx context.Context) error {
147147
return nil
148148
}
149149

150-
func (m manager) syncReleaseStatus(status *types.HelmAppStatus) error {
151-
deployedCondition := status.Conditions.GetCondition(types.ConditionDeployed)
152-
if deployedCondition == nil {
153-
return nil
150+
func (m manager) syncReleaseStatus(status types.HelmAppStatus) error {
151+
var release *rpb.Release
152+
for _, condition := range status.Conditions {
153+
if condition.Type == types.ConditionDeployed && condition.Status == types.StatusTrue {
154+
release = condition.Release
155+
break
156+
}
154157
}
155-
156-
haDeployedCondition := deployedCondition.(*types.HelmAppCondition)
157-
if !haDeployedCondition.IsTrue() {
158+
if release == nil {
158159
return nil
159160
}
160-
release := haDeployedCondition.Release
161161

162162
name := release.GetName()
163163
version := release.GetVersion()

pkg/scaffold/controller_kind.go

+28
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,37 @@ func (r *Reconcile{{ .Resource.Kind }}) Reconcile(request reconcile.Request) (re
159159
return reconcile.Result{}, err
160160
}
161161
162+
instance.Status.Conditions.SetCondition(&status.SimpleCondition{
163+
Type: status.ConditionType("Ready"),
164+
Status: corev1.ConditionTrue,
165+
})
166+
err := r.client.Status().Update(context.TODO(), instance)
167+
if err != nil {
168+
return reconcile.Result{}, err
169+
}
170+
162171
// Pod created successfully - don't requeue
163172
return reconcile.Result{}, nil
164173
} else if err != nil {
174+
instance.Status.Conditions.SetCondition(&status.SimpleCondition{
175+
Type: status.ConditionType("Ready"),
176+
Status: corev1.ConditionFalse,
177+
Message: err.Error(),
178+
})
179+
err := r.client.Status().Update(context.TODO(), instance)
180+
if err != nil {
181+
return reconcile.Result{}, err
182+
}
183+
184+
return reconcile.Result{}, err
185+
}
186+
187+
instance.Status.Conditions.SetCondition(&status.SimpleCondition{
188+
Type: status.ConditionType("Ready"),
189+
Status: corev1.ConditionTrue,
190+
})
191+
err = r.client.Status().Update(context.TODO(), instance)
192+
if err != nil {
165193
return reconcile.Result{}, err
166194
}
167195

0 commit comments

Comments
 (0)