Skip to content

Commit 1d1fd97

Browse files
authored
Merge pull request #112 from SomtochiAma/unstructured-support
Unstructured support in different functions
2 parents a6536ef + 9e95327 commit 1d1fd97

File tree

4 files changed

+141
-45
lines changed

4 files changed

+141
-45
lines changed

pkg/patterns/addon/application.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"errors"
2222
"fmt"
2323

24+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2425
addonsv1alpha1 "sigs.k8s.io/kubebuilder-declarative-pattern/pkg/patterns/addon/pkg/apis/v1alpha1"
2526
"sigs.k8s.io/kubebuilder-declarative-pattern/pkg/patterns/declarative"
2627
"sigs.k8s.io/kubebuilder-declarative-pattern/pkg/patterns/declarative/pkg/manifest"
@@ -42,8 +43,25 @@ const (
4243

4344
// TransformApplicationFromStatus modifies the Application in the deployment based off the CommonStatus
4445
func TransformApplicationFromStatus(ctx context.Context, instance declarative.DeclarativeObject, objects *manifest.Objects) error {
45-
addonObject, ok := instance.(addonsv1alpha1.CommonObject)
46-
if !ok {
46+
var version string
47+
var healthy bool
48+
var addonObject addonsv1alpha1.CommonObject
49+
50+
if unstruct, ok := instance.(*unstructured.Unstructured); ok {
51+
v, _, err := unstructured.NestedString(unstruct.Object, "spec", "version")
52+
if err != nil {
53+
return fmt.Errorf("unable to get version from unstuctured: %v", err)
54+
}
55+
version = v
56+
57+
healthy, _, err = unstructured.NestedBool(unstruct.Object, "status", "healthy")
58+
if err != nil {
59+
return fmt.Errorf("unable to get status from unstuctured: %v", err)
60+
}
61+
} else if addonObject, ok = instance.(addonsv1alpha1.CommonObject); ok {
62+
version = addonObject.CommonSpec().Version
63+
healthy = addonObject.GetCommonStatus().Healthy
64+
} else {
4765
return fmt.Errorf("instance %T was not an addonsv1alpha1.CommonObject", instance)
4866
}
4967

@@ -56,12 +74,12 @@ func TransformApplicationFromStatus(ctx context.Context, instance declarative.De
5674
}
5775

5876
assemblyPhase := Pending
59-
if addonObject.GetCommonStatus().Healthy {
77+
if healthy {
6078
assemblyPhase = Succeeded
6179
}
6280

6381
// TODO: Version should be on CommonStatus as well
64-
app.SetNestedField(addonObject.CommonSpec().Version, "spec", "descriptor", "version")
82+
app.SetNestedField(version, "spec", "descriptor", "version")
6583
app.SetNestedField(assemblyPhase, "spec", "assemblyPhase")
6684

6785
return nil

pkg/patterns/addon/patch.go

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,25 +35,37 @@ import (
3535
func ApplyPatches(ctx context.Context, object declarative.DeclarativeObject, objects *manifest.Objects) error {
3636
log := log.Log
3737

38-
p, ok := object.(addonsv1alpha1.Patchable)
39-
if !ok {
40-
return fmt.Errorf("provided object (%T) does not implement Patchable type", object)
41-
}
42-
4338
var patches []*unstructured.Unstructured
4439

45-
for _, p := range p.PatchSpec().Patches {
46-
// Object is nil, Raw is populated (with json, even when input was yaml)
47-
r := bytes.NewReader(p.Raw)
48-
decoder := yaml.NewYAMLOrJSONDecoder(r, 1024)
49-
patch := &unstructured.Unstructured{}
40+
unstruct, ok := object.(*unstructured.Unstructured)
41+
if ok {
42+
patch, _, err := unstructured.NestedSlice(unstruct.Object, "spec", "patches")
43+
if err != nil {
44+
return fmt.Errorf("unable to get patches from unstructured: %v", err)
45+
}
5046

51-
if err := decoder.Decode(patch); err != nil {
52-
return fmt.Errorf("error parsing json into unstructured object: %v", err)
47+
for _, p := range patch {
48+
m := p.(map[string]interface{})
49+
patches = append(patches, &unstructured.Unstructured{
50+
Object: m,
51+
})
5352
}
54-
log.WithValues("patch", patch).V(1).Info("parsed patch")
53+
} else if p, ok := object.(addonsv1alpha1.Patchable); ok {
54+
for _, p := range p.PatchSpec().Patches {
55+
// Object is nil, Raw is populated (with json, even when input was yaml)
56+
r := bytes.NewReader(p.Raw)
57+
decoder := yaml.NewYAMLOrJSONDecoder(r, 1024)
58+
patch := &unstructured.Unstructured{}
5559

56-
patches = append(patches, patch)
60+
if err := decoder.Decode(patch); err != nil {
61+
return fmt.Errorf("error parsing json into unstructured object: %v", err)
62+
}
63+
log.WithValues("patch", patch).V(1).Info("parsed patch")
64+
65+
patches = append(patches, patch)
66+
}
67+
} else {
68+
return fmt.Errorf("provided object (%T) does not implement Patchable type", object)
5769
}
5870

5971
return objects.Patch(patches)

pkg/patterns/addon/pkg/loaders/fs.go

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"flag"
2222
"fmt"
23+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2324
"strings"
2425

2526
"k8s.io/apimachinery/pkg/runtime"
@@ -53,16 +54,36 @@ func NewManifestLoader(channel string) (*ManifestLoader, error) {
5354
func (c *ManifestLoader) ResolveManifest(ctx context.Context, object runtime.Object) (map[string]string, error) {
5455
log := log.Log
5556

56-
addonObject, ok := object.(addonsv1alpha1.CommonObject)
57-
if !ok {
58-
return nil, fmt.Errorf("object %T was not an addonsv1alpha1.CommonObject", object)
59-
}
57+
var (
58+
channelName string
59+
version string
60+
componentName string
61+
)
6062

61-
componentName := addonObject.ComponentName()
63+
unstruct, ok := object.(*unstructured.Unstructured)
64+
if ok {
65+
v, _, err := unstructured.NestedString(unstruct.Object, "spec", "version")
66+
if err != nil {
67+
return nil, fmt.Errorf("unable to get spec.version: %v", err)
68+
}
69+
version = v
6270

63-
spec := addonObject.CommonSpec()
64-
version := spec.Version
65-
channelName := spec.Channel
71+
c, _, err := unstructured.NestedString(unstruct.Object, "spec", "channel")
72+
if err != nil {
73+
return nil, fmt.Errorf("unable to get spec.version: %v", err)
74+
}
75+
channelName = c
76+
77+
componentName = strings.ToLower(unstruct.GetKind())
78+
} else if addonObject, ok := object.(addonsv1alpha1.CommonObject); ok {
79+
componentName = addonObject.ComponentName()
80+
81+
spec := addonObject.CommonSpec()
82+
version = spec.Version
83+
channelName = spec.Channel
84+
} else {
85+
return nil, fmt.Errorf("object %T was not an addonsv1alpha1.CommonObject", object)
86+
}
6687

6788
// TODO: We should actually do id (1.1.2-aws or 1.1.1-nginx). But maybe YAGNI
6889
id := version

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

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package status
1919
import (
2020
"context"
2121
"fmt"
22+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2223
"reflect"
2324

2425
appsv1 "k8s.io/api/apps/v1"
@@ -46,50 +47,94 @@ type aggregator struct {
4647
func (a *aggregator) Reconciled(ctx context.Context, src declarative.DeclarativeObject, objs *manifest.Objects) error {
4748
log := log.Log
4849

49-
instance, ok := src.(addonv1alpha1.CommonObject)
50-
if !ok {
51-
return fmt.Errorf("object %T was not an addonv1alpha1.CommonObject", src)
52-
}
53-
54-
status := addonv1alpha1.CommonStatus{Healthy: true}
50+
statusHealthy := true
51+
statusErrors := []string{}
5552

5653
for _, o := range objs.Items {
5754
gk := o.Group + "/" + o.Kind
5855
healthy := true
5956
var err error
6057
switch gk {
6158
case "/Service":
62-
healthy, err = a.service(ctx, instance, o.Name)
59+
healthy, err = a.service(ctx, src, o.Name)
6360
case "extensions/Deployment", "apps/Deployment":
64-
healthy, err = a.deployment(ctx, instance, o.Name)
61+
healthy, err = a.deployment(ctx, src, o.Name)
6562
default:
6663
log.WithValues("type", gk).V(2).Info("type not implemented for status aggregation, skipping")
6764
}
6865

69-
status.Healthy = status.Healthy && healthy
66+
statusHealthy = statusHealthy && healthy
7067
if err != nil {
71-
status.Errors = append(status.Errors, fmt.Sprintf("%v", err))
68+
statusErrors = append(statusErrors, fmt.Sprintf("%v", err))
7269
}
7370
}
7471

75-
log.WithValues("object", src).WithValues("status", status).V(2).Info("built status")
72+
log.WithValues("object", src).WithValues("status", statusHealthy).V(2).Info("built status")
7673

77-
if !reflect.DeepEqual(status, instance.GetCommonStatus()) {
78-
instance.SetCommonStatus(status)
74+
unstruct, ok := src.(*unstructured.Unstructured)
75+
instance, commonOkay := src.(addonv1alpha1.CommonObject)
7976

80-
log.WithValues("name", instance.GetName()).WithValues("status", status).Info("updating status")
77+
unstructStatus := make(map[string]interface{})
78+
var status addonv1alpha1.CommonStatus
79+
80+
if ok {
81+
unstructStatus["Healthy"] = true
82+
} else if commonOkay {
83+
status = addonv1alpha1.CommonStatus{Healthy: true}
84+
} else {
85+
return fmt.Errorf("object %T was not an addonv1alpha1.CommonObject", src)
86+
}
8187

82-
err := a.client.Update(ctx, instance)
88+
if commonOkay {
89+
status.Errors = statusErrors
90+
status.Healthy = statusHealthy
91+
92+
if !reflect.DeepEqual(status, instance.GetCommonStatus()) {
93+
instance.SetCommonStatus(status)
94+
95+
log.WithValues("name", instance.GetName()).WithValues("status", status).Info("updating status")
96+
97+
err := a.client.Update(ctx, instance)
98+
if err != nil {
99+
log.Error(err, "updating status")
100+
return err
101+
}
102+
}
103+
} else {
104+
unstructStatus["Healthy"] = true
105+
unstructStatus["Errors"] = statusErrors
106+
s, _, err := unstructured.NestedMap(unstruct.Object, "status")
83107
if err != nil {
84-
log.Error(err, "updating status")
85-
return err
108+
log.Error(err, "getting status")
109+
return fmt.Errorf("unable to get status from unstructured: %v", err)
110+
}
111+
if !reflect.DeepEqual(status, s) {
112+
err = unstructured.SetNestedField(unstruct.Object, statusHealthy, "status", "healthy")
113+
if err != nil {
114+
log.Error(err, "updating status")
115+
return fmt.Errorf("unable to set status in unstructured: %v", err)
116+
}
117+
118+
err = unstructured.SetNestedStringSlice(unstruct.Object, statusErrors, "status", "errors")
119+
if err != nil {
120+
log.Error(err, "updating status")
121+
return fmt.Errorf("unable to set status in unstructured: %v", err)
122+
}
123+
124+
log.WithValues("name", unstruct.GetName()).WithValues("status", status).Info("updating status")
125+
126+
err = a.client.Update(ctx, unstruct)
127+
if err != nil {
128+
log.Error(err, "updating status")
129+
return err
130+
}
86131
}
87132
}
88133

89134
return nil
90135
}
91136

92-
func (a *aggregator) deployment(ctx context.Context, src addonv1alpha1.CommonObject, name string) (bool, error) {
137+
func (a *aggregator) deployment(ctx context.Context, src declarative.DeclarativeObject, name string) (bool, error) {
93138
key := client.ObjectKey{Namespace: src.GetNamespace(), Name: name}
94139
dep := &appsv1.Deployment{}
95140

@@ -106,7 +151,7 @@ func (a *aggregator) deployment(ctx context.Context, src addonv1alpha1.CommonObj
106151
return false, fmt.Errorf("deployment (%s) does not meet condition: %s", key, successfulDeployment)
107152
}
108153

109-
func (a *aggregator) service(ctx context.Context, src addonv1alpha1.CommonObject, name string) (bool, error) {
154+
func (a *aggregator) service(ctx context.Context, src declarative.DeclarativeObject, name string) (bool, error) {
110155
key := client.ObjectKey{Namespace: src.GetNamespace(), Name: name}
111156
svc := &corev1.Service{}
112157
err := a.client.Get(ctx, key, svc)

0 commit comments

Comments
 (0)