1 package kclient
2
3 import (
4 "context"
5 "encoding/json"
6 "errors"
7 "fmt"
8 "time"
9
10 kerrors "k8s.io/apimachinery/pkg/api/errors"
11 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
13 "k8s.io/apimachinery/pkg/runtime/schema"
14 "k8s.io/apimachinery/pkg/types"
15 "k8s.io/apimachinery/pkg/watch"
16 "k8s.io/klog"
17 )
18
19
20
21 func (c *Client) PatchDynamicResource(resource unstructured.Unstructured) (bool, error) {
22 klog.V(5).Infoln("Applying resource via server-side apply:")
23 klog.V(5).Infoln(resourceAsJson(resource.Object))
24 unversionedResource := resource.DeepCopy()
25 unversionedResource.SetResourceVersion("")
26 data, err := json.Marshal(unversionedResource.Object)
27 if err != nil {
28 return false, fmt.Errorf("unable to marshal resource: %w", err)
29 }
30
31 gvr, err := c.GetRestMappingFromUnstructured(*unversionedResource)
32 if err != nil {
33 return false, err
34 }
35
36 var previousGeneration int64 = -1
37
38 previous, err := c.DynamicClient.Resource(gvr.Resource).Namespace(c.Namespace).Get(context.TODO(), unversionedResource.GetName(), metav1.GetOptions{})
39 if err != nil {
40 if !kerrors.IsNotFound(err) {
41 return false, err
42 }
43 } else {
44 previousGeneration = previous.GetGeneration()
45 }
46
47
48 current, err := c.DynamicClient.Resource(gvr.Resource).Namespace(c.Namespace).Patch(context.TODO(), unversionedResource.GetName(), types.ApplyPatchType, data, metav1.PatchOptions{FieldManager: FieldManager, Force: Bool(true)})
49 if err != nil {
50 return false, err
51 }
52 newGeneration := current.GetGeneration()
53
54 return newGeneration > previousGeneration, nil
55 }
56
57
58
59
60 func (c *Client) ListDynamicResources(namespace string, gvr schema.GroupVersionResource, selector string) (*unstructured.UnstructuredList, error) {
61
62 if c.DynamicClient == nil {
63 return nil, nil
64 }
65
66 ns := namespace
67 if ns == "" {
68 ns = c.Namespace
69 }
70
71 listOptions := metav1.ListOptions{}
72 if selector != "" {
73 listOptions.LabelSelector = selector
74 }
75
76 list, err := c.DynamicClient.Resource(gvr).Namespace(ns).List(context.TODO(), listOptions)
77 if err != nil {
78 if kerrors.IsNotFound(err) {
79
80 return &unstructured.UnstructuredList{}, nil
81 }
82 return nil, err
83 }
84
85 return list, nil
86 }
87
88
89
90
91 func (c *Client) ListClusterWideDynamicResources(gvr schema.GroupVersionResource, selector string) (*unstructured.UnstructuredList, error) {
92
93 if c.DynamicClient == nil {
94 return nil, nil
95 }
96
97 listOptions := metav1.ListOptions{}
98 if selector != "" {
99 listOptions.LabelSelector = selector
100 }
101
102 list, err := c.DynamicClient.Resource(gvr).List(context.TODO(), listOptions)
103 if err != nil {
104 if kerrors.IsNotFound(err) {
105 return &unstructured.UnstructuredList{}, nil
106 }
107 return nil, err
108 }
109
110 return list, nil
111 }
112
113
114 func (c *Client) GetDynamicResource(gvr schema.GroupVersionResource, name string) (*unstructured.Unstructured, error) {
115 res, err := c.DynamicClient.Resource(gvr).Namespace(c.Namespace).Get(context.TODO(), name, metav1.GetOptions{})
116 if err != nil {
117 return nil, err
118 }
119 return res, nil
120 }
121
122
123 func (c *Client) UpdateDynamicResource(gvr schema.GroupVersionResource, name string, u *unstructured.Unstructured) error {
124 _, err := c.DynamicClient.Resource(gvr).Namespace(c.Namespace).Update(context.TODO(), u, metav1.UpdateOptions{})
125 if err != nil {
126 return err
127 }
128 return nil
129 }
130
131 type GVRN struct {
132 gvr schema.GroupVersionResource
133 name string
134 }
135
136
137
138
139 func (c *Client) DeleteDynamicResource(name string, gvr schema.GroupVersionResource, wait bool) error {
140
141 doDeleteResource := func() error {
142 return c.DynamicClient.Resource(gvr).Namespace(c.Namespace).Delete(context.TODO(), name, metav1.DeleteOptions{
143 PropagationPolicy: func(f metav1.DeletionPropagation) *metav1.DeletionPropagation {
144 if wait {
145 return &f
146 }
147 return nil
148 }(metav1.DeletePropagationForeground),
149 })
150 }
151
152 if !wait {
153 return doDeleteResource()
154 }
155
156
157 thisRes, err := c.GetDynamicResource(gvr, name)
158 if err != nil {
159 return err
160 }
161 all, err := c.GetAllResourcesFromSelector("", c.Namespace)
162 if err != nil {
163 return err
164 }
165
166 var toWait []GVRN
167 for _, res := range all {
168 ownerRefs := res.GetOwnerReferences()
169 for _, ownerRef := range ownerRefs {
170 if ownerRef.UID == thisRes.GetUID() {
171 if ownerRef.BlockOwnerDeletion == nil || !*ownerRef.BlockOwnerDeletion {
172 mapping, err2 := c.GetRestMappingFromUnstructured(res)
173 if err2 != nil {
174 return err2
175 }
176 toWait = append(toWait, GVRN{
177 gvr: mapping.Resource,
178 name: res.GetName(),
179 })
180 }
181 }
182 }
183 }
184
185 err = doDeleteResource()
186 if err != nil {
187 return err
188 }
189 err = c.WaitDynamicResourceDeleted(gvr, name)
190 if err != nil {
191 return err
192 }
193 for _, wait := range toWait {
194 err = c.WaitDynamicResourceDeleted(wait.gvr, wait.name)
195 if err != nil {
196 return err
197 }
198 }
199 return nil
200 }
201
202
203 func (c *Client) WaitDynamicResourceDeleted(gvr schema.GroupVersionResource, name string) error {
204
205 watcher, err := c.DynamicClient.Resource(gvr).Namespace(c.Namespace).Watch(context.TODO(), metav1.ListOptions{FieldSelector: "metadata.name=" + name})
206 if err != nil {
207 return err
208 }
209 defer watcher.Stop()
210
211 _, err = c.GetDynamicResource(gvr, name)
212 if err != nil {
213
214 if kerrors.IsNotFound(err) {
215 return nil
216 }
217 return err
218 }
219
220 for {
221 select {
222 case <-time.After(time.Minute):
223 return fmt.Errorf("timeout while waiting for %q resource to be deleted", name)
224
225 case val, ok := <-watcher.ResultChan():
226 if !ok {
227 return errors.New("error getting value from resultchan")
228 }
229 if val.Type == watch.Deleted {
230 return nil
231 }
232 }
233 }
234 }
235
View as plain text