Skip to content

Commit 55909ee

Browse files
committed
Refactors Kstatus function
1 parent 02727e7 commit 55909ee

File tree

5 files changed

+185
-104
lines changed

5 files changed

+185
-104
lines changed

pkg/patterns/addon/pkg/status/aggregate.go

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -74,42 +74,32 @@ func (a *aggregator) Reconciled(ctx context.Context, src declarative.Declarative
7474

7575
unstruct, ok := src.(*unstructured.Unstructured)
7676
instance, commonOkay := src.(addonv1alpha1.CommonObject)
77-
78-
unstructStatus := make(map[string]interface{})
79-
var status addonv1alpha1.CommonStatus
80-
81-
if ok {
82-
unstructStatus["Healthy"] = true
83-
} else if commonOkay {
84-
status = addonv1alpha1.CommonStatus{Healthy: true}
85-
} else {
86-
return fmt.Errorf("object %T was not an addonv1alpha1.CommonObject", src)
87-
}
77+
changed := false
8878

8979
if commonOkay {
80+
var status = instance.GetCommonStatus()
9081
status.Errors = statusErrors
9182
status.Healthy = statusHealthy
9283

9384
if !reflect.DeepEqual(status, instance.GetCommonStatus()) {
94-
instance.SetCommonStatus(status)
85+
status.Healthy = statusHealthy
9586

9687
log.WithValues("name", instance.GetName()).WithValues("status", status).Info("updating status")
97-
98-
err := a.client.Status().Update(ctx, instance)
99-
if err != nil {
100-
log.Error(err, "updating status")
101-
return err
102-
}
88+
changed = true
10389
}
104-
} else {
105-
unstructStatus["Healthy"] = true
106-
unstructStatus["Errors"] = statusErrors
90+
} else if ok {
91+
unstructStatus := make(map[string]interface{})
92+
10793
s, _, err := unstructured.NestedMap(unstruct.Object, "status")
10894
if err != nil {
10995
log.Error(err, "getting status")
11096
return fmt.Errorf("unable to get status from unstructured: %v", err)
11197
}
112-
if !reflect.DeepEqual(status, s) {
98+
99+
unstructStatus = s
100+
unstructStatus["Healthy"] = statusHealthy
101+
unstructStatus["Errors"] = statusErrors
102+
if !reflect.DeepEqual(unstruct, s) {
113103
err = unstructured.SetNestedField(unstruct.Object, statusHealthy, "status", "healthy")
114104
if err != nil {
115105
log.Error(err, "updating status")
@@ -121,13 +111,18 @@ func (a *aggregator) Reconciled(ctx context.Context, src declarative.Declarative
121111
log.Error(err, "updating status")
122112
return fmt.Errorf("unable to set status in unstructured: %v", err)
123113
}
114+
changed = true
115+
}
116+
} else {
117+
return fmt.Errorf("instance %T was not an addonsv1alpha1.CommonObject or unstructured.Unstructured", src)
118+
}
124119

125-
log.WithValues("name", unstruct.GetName()).WithValues("status", status).Info("updating status")
126-
err = a.client.Status().Update(ctx, src)
127-
if err != nil {
128-
log.Error(err, "updating status")
129-
return err
130-
}
120+
if changed == true {
121+
log.WithValues("name", src.GetName()).WithValues("status", statusHealthy).Info("updating status")
122+
err := a.client.Status().Update(ctx, src)
123+
if err != nil {
124+
log.Error(err, "updating status")
125+
return err
131126
}
132127
}
133128

pkg/patterns/addon/pkg/status/basic.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"sigs.k8s.io/kubebuilder-declarative-pattern/pkg/patterns/declarative"
2222
)
2323

24+
// Deprecated: This function exists for backward compatibility, please use NewKstatusCheck
25+
2426
// NewBasic provides an implementation of declarative.Status that
2527
// performs no preflight checks.
2628
func NewBasic(client client.Client) declarative.Status {
@@ -44,3 +46,9 @@ func NewBasicVersionChecks(client client.Client, version string) (declarative.St
4446
// no preflight checks
4547
}, nil
4648
}
49+
50+
func NewKstatusCheck(client client.Client, d *declarative.Reconciler) declarative.Status{
51+
return &declarative.StatusBuilder{
52+
ReconciledImpl: NewKstatusAgregator(client, d),
53+
}
54+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package status
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"sigs.k8s.io/controller-runtime/pkg/log"
8+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
9+
"sigs.k8s.io/cli-utils/pkg/kstatus/status"
10+
"sigs.k8s.io/controller-runtime/pkg/client"
11+
addonsv1alpha1 "sigs.k8s.io/kubebuilder-declarative-pattern/pkg/patterns/addon/pkg/apis/v1alpha1"
12+
"sigs.k8s.io/kubebuilder-declarative-pattern/pkg/patterns/declarative"
13+
"sigs.k8s.io/kubebuilder-declarative-pattern/pkg/patterns/declarative/pkg/manifest"
14+
)
15+
16+
type kstatusAggregator struct {
17+
client client.Client
18+
reconciler *declarative.Reconciler
19+
}
20+
21+
func NewKstatusAgregator(c client.Client, reconciler *declarative.Reconciler) *kstatusAggregator{
22+
return &kstatusAggregator{client: c, reconciler: reconciler}
23+
}
24+
25+
func(k *kstatusAggregator) Reconciled(ctx context.Context, src declarative.DeclarativeObject,
26+
objs *manifest.Objects) error{
27+
log := log.Log
28+
29+
statusMap := make(map[status.Status]bool)
30+
for _, object := range objs.Items {
31+
32+
unstruct, err := declarative.GetObjectFromCluster(object, k.reconciler)
33+
if err != nil {
34+
log.WithValues("object", object.Kind+"/"+object.Name).Error(err, "Unable to get status of object")
35+
return err
36+
}
37+
38+
res, err := status.Compute(unstruct)
39+
if err != nil {
40+
log.WithValues("kind", object.Kind).WithValues("name", object.Name).WithValues("status", res.Status).WithValues(
41+
"message", res.Message).Info("Got status of resource:")
42+
statusMap[status.NotFoundStatus] = true
43+
}
44+
if res != nil {
45+
log.WithValues("kind", object.Kind).WithValues("name", object.Name).WithValues("status", res.Status).WithValues("message", res.Message).Info("Got status of resource:")
46+
statusMap[res.Status] = true
47+
}
48+
}
49+
50+
addonObject, ok := src.(addonsv1alpha1.CommonObject)
51+
unstruct, unstructOk := src.(*unstructured.Unstructured)
52+
aggregatedPhase := string(aggregateStatus(statusMap))
53+
changed := false
54+
if ok {
55+
addonStatus := addonObject.GetCommonStatus()
56+
if addonStatus.Phase != aggregatedPhase {
57+
addonStatus.Phase = aggregatedPhase
58+
addonObject.SetCommonStatus(addonStatus)
59+
changed = true
60+
}
61+
} else if unstructOk {
62+
statusPhase, _, err := unstructured.NestedString(unstruct.Object, "status", "phase")
63+
if err != nil {
64+
log.Error(err, "error retrieving status")
65+
return err
66+
}
67+
68+
if statusPhase != aggregatedPhase {
69+
err := unstructured.SetNestedField(unstruct.Object, aggregatedPhase, "status", "phase")
70+
if err != nil {
71+
log.Error(err, "error retrieving status")
72+
return err
73+
}
74+
changed = true
75+
}
76+
} else {
77+
return fmt.Errorf("instance %T was not an addonsv1alpha1.CommonObject or unstructured." +
78+
"Unstructured",
79+
src)
80+
}
81+
82+
if changed == true {
83+
log.WithValues("name", src.GetName()).WithValues("phase", aggregatedPhase).Info("updating status")
84+
err := k.client.Status().Update(ctx, src)
85+
if err != nil {
86+
log.Error(err, "error updating status")
87+
return fmt.Errorf("error error status: %v", err)
88+
}
89+
}
90+
91+
return nil
92+
}
93+
94+
func aggregateStatus(m map[status.Status]bool) status.Status {
95+
inProgress := m[status.InProgressStatus]
96+
terminating := m[status.TerminatingStatus]
97+
98+
failed := m[status.FailedStatus]
99+
100+
if inProgress || terminating {
101+
return status.InProgressStatus
102+
}
103+
104+
if failed {
105+
return status.FailedStatus
106+
}
107+
108+
return status.CurrentStatus
109+
}

pkg/patterns/addon/pkg/status/version.go

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ package status
33
import (
44
"context"
55
"fmt"
6+
"reflect"
67

78
"github.com/blang/semver"
9+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
810
"sigs.k8s.io/controller-runtime/pkg/client"
911
"sigs.k8s.io/controller-runtime/pkg/log"
1012
addonsv1alpha1 "sigs.k8s.io/kubebuilder-declarative-pattern/pkg/patterns/addon/pkg/apis/v1alpha1"
@@ -58,19 +60,49 @@ func (p *versionCheck) VersionCheck(
5860
return true, nil
5961
}
6062

61-
addonObject, ok := src.(addonsv1alpha1.CommonObject)
62-
if !ok {
63-
return false, fmt.Errorf("object %T was not an addonsv1alpha1.CommonObject", src)
63+
errors := []string{
64+
fmt.Sprintf("manifest needs operator version >= %v, this operator is version %v", minOperatorVersion.String(),
65+
p.operatorVersion.String()),
6466
}
67+
unstruct, ok := src.(*unstructured.Unstructured)
68+
addonObject, commonOkay := src.(addonsv1alpha1.CommonObject)
6569

66-
status := addonsv1alpha1.CommonStatus{
67-
Healthy: false,
68-
Errors: []string{
69-
fmt.Sprintf("manifest needs operator version >= %v, this operator is version %v", minOperatorVersion.String(), p.operatorVersion.String()),
70-
},
70+
if ok {
71+
unstructStatus := make(map[string]interface{})
72+
73+
s, _, err := unstructured.NestedMap(unstruct.Object, "status")
74+
if err != nil {
75+
log.Error(err, "getting status")
76+
return false, fmt.Errorf("unable to get status from unstructured: %v", err)
77+
}
78+
79+
unstructStatus = s
80+
unstructStatus["Healthy"] = false
81+
unstructStatus["Errors"] = errors
82+
83+
if !reflect.DeepEqual(unstruct, s) {
84+
err = unstructured.SetNestedField(unstruct.Object, false, "status", "healthy")
85+
if err != nil {
86+
log.Error(err, "unable to updating status in unstructured")
87+
}
88+
89+
err = unstructured.SetNestedStringSlice(unstruct.Object, errors,"status", "errors")
90+
if err != nil {
91+
log.Error(err, "unable to updating status in unstructured")
92+
}
93+
}
94+
} else if commonOkay {
95+
status := addonObject.GetCommonStatus()
96+
status.Healthy = false
97+
status.Errors = errors
98+
if !reflect.DeepEqual(status, addonObject.GetCommonStatus()) {
99+
status.Healthy = false
100+
status.Errors = errors
101+
addonObject.SetCommonStatus(status)
102+
}
103+
} else {
104+
return false, fmt.Errorf("instance %T was not an addonsv1alpha1.CommonObject or unstructured.Unstructured", src)
71105
}
72-
log.WithValues("name", addonObject.GetName()).WithValues("status", status).Info("updating status")
73-
addonObject.SetCommonStatus(status)
74106

75107
return false, fmt.Errorf("operator not qualified, manifest needs operator version >= %v", minOperatorVersion.String())
76108
}

pkg/patterns/declarative/reconciler.go

Lines changed: 3 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,11 @@ import (
2727

2828
apierrors "k8s.io/apimachinery/pkg/api/errors"
2929

30-
"k8s.io/client-go/dynamic"
31-
addonsv1alpha1 "sigs.k8s.io/kubebuilder-declarative-pattern/pkg/patterns/addon/pkg/apis/v1alpha1"
32-
3330
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3431
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
3532
"k8s.io/apimachinery/pkg/runtime"
3633
"k8s.io/apimachinery/pkg/types"
34+
"k8s.io/client-go/dynamic"
3735
"k8s.io/client-go/rest"
3836
recorder "k8s.io/client-go/tools/record"
3937
"sigs.k8s.io/cli-utils/pkg/kstatus/status"
@@ -208,7 +206,7 @@ func (r *Reconciler) reconcileExists(ctx context.Context, name types.NamespacedN
208206
var newItems []*manifest.Object
209207
for _, obj := range objects.Items {
210208

211-
unstruct, err := getObjectFromCluster(obj, r)
209+
unstruct, err := GetObjectFromCluster(obj, r)
212210
if err != nil && !apierrors.IsNotFound(err) {
213211
log.WithValues("name", obj.Name).Error(err, "Unable to get resource")
214212
}
@@ -269,67 +267,6 @@ func (r *Reconciler) reconcileExists(ctx context.Context, name types.NamespacedN
269267
return reconcile.Result{}, fmt.Errorf("error applying manifest: %v", err)
270268
}
271269

272-
statusMap := make(map[status.Status]bool)
273-
for _, object := range objects.Items {
274-
275-
unstruct, err := getObjectFromCluster(object, r)
276-
if err != nil {
277-
return reconcile.Result{}, err
278-
}
279-
280-
res, err := status.Compute(unstruct)
281-
if err != nil {
282-
log.WithValues("unstuctured", unstruct).Error(err, "Unable to get status of unstructured")
283-
statusMap[status.NotFoundStatus] = true
284-
}
285-
if res != nil {
286-
log.WithValues("kind", object.Kind).WithValues("name", object.Name).WithValues("status", res.Status).WithValues(
287-
"message", res.Message).Info("Got status of resource:")
288-
statusMap[res.Status] = true
289-
}
290-
}
291-
292-
addonObject, ok := instance.(addonsv1alpha1.CommonObject)
293-
unstruct, unstructOk := instance.(*unstructured.Unstructured)
294-
aggregatedPhase := string(aggregateStatus(statusMap))
295-
changed := false
296-
if ok {
297-
addonStatus := addonObject.GetCommonStatus()
298-
if addonStatus.Phase != aggregatedPhase {
299-
addonStatus.Phase = aggregatedPhase
300-
addonObject.SetCommonStatus(addonStatus)
301-
changed = true
302-
}
303-
} else if unstructOk {
304-
statusPhase, _, err := unstructured.NestedString(unstruct.Object, "status", "phase")
305-
if err != nil {
306-
log.Error(err, "error retrieving status")
307-
return reconcile.Result{}, err
308-
}
309-
310-
if statusPhase != aggregatedPhase {
311-
err := unstructured.SetNestedField(unstruct.Object, aggregatedPhase, "status", "phase")
312-
if err != nil {
313-
log.Error(err, "error retrieving status")
314-
return reconcile.Result{}, err
315-
}
316-
changed = true
317-
}
318-
} else {
319-
return reconcile.Result{}, fmt.Errorf("instance %T was not an addonsv1alpha1.CommonObject or unstructured." +
320-
"Unstructured",
321-
instance)
322-
}
323-
324-
if changed == true {
325-
log.WithValues("name", instance.GetName()).WithValues("phase", aggregatedPhase).Info("updating status")
326-
err = r.client.Status().Update(ctx, instance)
327-
if err != nil {
328-
log.Error(err, "error updating status")
329-
return reconcile.Result{}, err
330-
}
331-
}
332-
333270
if r.options.sink != nil {
334271
if err := r.options.sink.Notify(ctx, instance, objects); err != nil {
335272
log.Error(err, "notifying sink")
@@ -632,7 +569,7 @@ func aggregateStatus(m map[status.Status]bool) status.Status {
632569
return status.CurrentStatus
633570
}
634571

635-
func getObjectFromCluster(obj *manifest.Object, r *Reconciler) (*unstructured.
572+
func GetObjectFromCluster(obj *manifest.Object, r *Reconciler) (*unstructured.
636573
Unstructured, error) {
637574
getOptions := metav1.GetOptions{}
638575
gvk := obj.GroupVersionKind()

0 commit comments

Comments
 (0)