1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package k8s
16
17 import (
18 "encoding/json"
19 "fmt"
20 "reflect"
21
22 k8sv1alpha1 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/k8s/v1alpha1"
23 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/util"
24
25 corev1 "k8s.io/api/core/v1"
26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
28 "k8s.io/apimachinery/pkg/types"
29 "sigs.k8s.io/structured-merge-diff/v4/fieldpath"
30 )
31
32
33 type Resource struct {
34
35 metav1.TypeMeta `json:",inline"`
36 metav1.ObjectMeta `json:"metadata,omitempty"`
37 Spec map[string]interface{} `json:"spec,omitempty"`
38 Status map[string]interface{} `json:"status,omitempty"`
39
40
41
42
43
44
45
46
47
48
49 ManagedFields *fieldpath.Set `json:"-"`
50 }
51
52 func (r *Resource) GetNamespacedName() types.NamespacedName {
53 return types.NamespacedName{
54 Namespace: r.GetNamespace(),
55 Name: r.GetName(),
56 }
57 }
58
59
60
61
62
63 func NewResource(u *unstructured.Unstructured) (*Resource, error) {
64 resource := &Resource{}
65 if err := util.Marshal(u, resource); err != nil {
66 return nil, err
67 }
68 managedFields, err := GetK8sManagedFields(u)
69 if err != nil {
70 return nil, err
71 }
72 resource.ManagedFields = managedFields
73 return resource, nil
74 }
75
76 func (r *Resource) MarshalAsUnstructured() (*unstructured.Unstructured, error) {
77 u := &unstructured.Unstructured{}
78 if err := util.Marshal(r, u); err != nil {
79 return nil, fmt.Errorf("error marshing resource to Unstructured %w", err)
80 }
81 removeNilCreationTimestamp(u.Object)
82 return u, nil
83 }
84
85 func (r *Resource) IsResourceIDConfigured() (bool, error) {
86 val, exists, err := unstructured.NestedString(r.Spec, ResourceIDFieldName)
87 if err != nil {
88 return false, fmt.Errorf("error getting the value of "+
89 "\"spec.%s\": %w", ResourceIDFieldName, err)
90 }
91
92 if !exists {
93 return false, nil
94 }
95
96 if val == "" {
97 return false, fmt.Errorf("the value of '%s' is invalid: '' (empty "+
98 "string)", ResourceIDFieldPath)
99 }
100 return true, nil
101 }
102
103 func (r *Resource) HasResourceIDField() bool {
104 _, ok := r.Spec[ResourceIDFieldName]
105 return ok
106 }
107
108
109 func removeNilCreationTimestamp(object map[string]interface{}) {
110 metadata, ok := object["metadata"].(map[string]interface{})
111 if !ok {
112 panic("expected object to have a metadata field of type 'map[string]interface{}'")
113 }
114 creationTimestampKey := "creationTimestamp"
115 if _, ok := metadata[creationTimestampKey]; ok {
116 if creationTimeStamp, ok := metadata[creationTimestampKey]; ok {
117 if creationTimeStamp == nil {
118 delete(metadata, creationTimestampKey)
119 }
120 }
121 }
122 }
123
124 func IsResourceReady(r *Resource) bool {
125 cond, found := GetReadyCondition(r)
126 return found && cond.Status == corev1.ConditionTrue
127 }
128
129 func GetReadyCondition(r *Resource) (condition k8sv1alpha1.Condition, found bool) {
130 if currConditionsRaw, ok := r.Status["conditions"].([]interface{}); ok {
131 if currConditions, err := MarshalAsConditionsSlice(currConditionsRaw); err == nil {
132 for _, condition := range currConditions {
133 if condition.Type == k8sv1alpha1.ReadyConditionType {
134 return condition, true
135 }
136 }
137 }
138 }
139 return k8sv1alpha1.Condition{}, false
140 }
141
142 func ReadyConditionMatches(resource *Resource, status corev1.ConditionStatus, rs, msg string) bool {
143 cond, found := GetReadyCondition(resource)
144 if !found {
145 return false
146 }
147 return ConditionsEqualIgnoreTransitionTime(cond, NewCustomReadyCondition(status, rs, msg))
148 }
149
150 func IsSpecOrStatusUpdateRequired(resource *Resource, original *Resource) bool {
151 if !reflect.DeepEqual(resource.Spec, original.Spec) {
152 return true
153 }
154 if !reflect.DeepEqual(resource.Status, original.Status) {
155 return true
156 }
157
158 if len(resource.Status) == 0 || resource.Status["observedGeneration"] != float64(original.GetGeneration()) {
159 return true
160 }
161 return false
162 }
163
164 func IsAnnotationsUpdateRequired(resource *Resource, original *Resource) bool {
165 return !reflect.DeepEqual(resource.GetAnnotations(), original.GetAnnotations())
166 }
167
168 func MarshalObjectAsUnstructured(o metav1.Object) (*unstructured.Unstructured, error) {
169 b, err := json.Marshal(o)
170 if err != nil {
171 return nil, fmt.Errorf("error marshalling object %v: %w", o.GetName(), err)
172 }
173 u := &unstructured.Unstructured{}
174 if err := json.Unmarshal(b, u); err != nil {
175 return nil, fmt.Errorf("error unmarshalling object %v to unstructured.Unstructured: %w", o.GetName(), err)
176 }
177 removeNilCreationTimestamp(u.Object)
178 return u, nil
179 }
180
View as plain text