1
16
17 package client
18
19 import (
20 "context"
21 "encoding/json"
22 "fmt"
23 "reflect"
24 "testing"
25 "time"
26
27 v1 "k8s.io/api/core/v1"
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
30 "k8s.io/apimachinery/pkg/fields"
31 "k8s.io/apimachinery/pkg/runtime"
32 "k8s.io/apimachinery/pkg/runtime/schema"
33 "k8s.io/apimachinery/pkg/types"
34 "k8s.io/apimachinery/pkg/util/wait"
35 "k8s.io/apimachinery/pkg/watch"
36 metav1ac "k8s.io/client-go/applyconfigurations/meta/v1"
37 "k8s.io/client-go/discovery"
38 "k8s.io/client-go/dynamic"
39 clientset "k8s.io/client-go/kubernetes"
40 clientscheme "k8s.io/client-go/kubernetes/scheme"
41 kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
42 "k8s.io/kubernetes/test/integration/framework"
43 )
44
45 func TestDynamicClient(t *testing.T) {
46 result := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins", "ServiceAccount"}, framework.SharedEtcd())
47 defer result.TearDownFn()
48
49 client := clientset.NewForConfigOrDie(result.ClientConfig)
50 dynamicClient, err := dynamic.NewForConfig(result.ClientConfig)
51 if err != nil {
52 t.Fatalf("unexpected error creating dynamic client: %v", err)
53 }
54
55 resource := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
56
57
58 pod := &v1.Pod{
59 ObjectMeta: metav1.ObjectMeta{
60 GenerateName: "test",
61 },
62 Spec: v1.PodSpec{
63 Containers: []v1.Container{
64 {
65 Name: "test",
66 Image: "test-image",
67 },
68 },
69 },
70 }
71
72 actual, err := client.CoreV1().Pods("default").Create(context.TODO(), pod, metav1.CreateOptions{})
73 if err != nil {
74 t.Fatalf("unexpected error when creating pod: %v", err)
75 }
76
77
78 unstructuredList, err := dynamicClient.Resource(resource).Namespace("default").List(context.TODO(), metav1.ListOptions{})
79 if err != nil {
80 t.Fatalf("unexpected error when listing pods: %v", err)
81 }
82
83 if len(unstructuredList.Items) != 1 {
84 t.Fatalf("expected one pod, got %d", len(unstructuredList.Items))
85 }
86
87 got, err := unstructuredToPod(&unstructuredList.Items[0])
88 if err != nil {
89 t.Fatalf("unexpected error converting Unstructured to v1.Pod: %v", err)
90 }
91
92 if !reflect.DeepEqual(actual, got) {
93 t.Fatalf("unexpected pod in list. wanted %#v, got %#v", actual, got)
94 }
95
96
97 unstruct, err := dynamicClient.Resource(resource).Namespace("default").Get(context.TODO(), actual.Name, metav1.GetOptions{})
98 if err != nil {
99 t.Fatalf("unexpected error when getting pod %q: %v", actual.Name, err)
100 }
101
102 got, err = unstructuredToPod(unstruct)
103 if err != nil {
104 t.Fatalf("unexpected error converting Unstructured to v1.Pod: %v", err)
105 }
106
107 if !reflect.DeepEqual(actual, got) {
108 t.Fatalf("unexpected pod in list. wanted %#v, got %#v", actual, got)
109 }
110
111
112 err = dynamicClient.Resource(resource).Namespace("default").Delete(context.TODO(), actual.Name, metav1.DeleteOptions{})
113 if err != nil {
114 t.Fatalf("unexpected error when deleting pod: %v", err)
115 }
116
117 list, err := client.CoreV1().Pods("default").List(context.TODO(), metav1.ListOptions{})
118 if err != nil {
119 t.Fatalf("unexpected error when listing pods: %v", err)
120 }
121
122 if len(list.Items) != 0 {
123 t.Fatalf("expected zero pods, got %d", len(list.Items))
124 }
125 }
126
127 func TestDynamicClientWatch(t *testing.T) {
128 result := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
129 defer result.TearDownFn()
130
131 client := clientset.NewForConfigOrDie(result.ClientConfig)
132 dynamicClient, err := dynamic.NewForConfig(result.ClientConfig)
133 if err != nil {
134 t.Fatalf("unexpected error creating dynamic client: %v", err)
135 }
136
137 resource := v1.SchemeGroupVersion.WithResource("events")
138
139 mkEvent := func(i int) *v1.Event {
140 name := fmt.Sprintf("event-%v", i)
141 return &v1.Event{
142 ObjectMeta: metav1.ObjectMeta{
143 Namespace: "default",
144 Name: name,
145 },
146 InvolvedObject: v1.ObjectReference{
147 Namespace: "default",
148 Name: name,
149 },
150 Reason: fmt.Sprintf("event %v", i),
151 }
152 }
153
154 rv1 := ""
155 for i := 0; i < 10; i++ {
156 event := mkEvent(i)
157 got, err := client.CoreV1().Events("default").Create(context.TODO(), event, metav1.CreateOptions{})
158 if err != nil {
159 t.Fatalf("Failed creating event %#q: %v", event, err)
160 }
161 if rv1 == "" {
162 rv1 = got.ResourceVersion
163 if rv1 == "" {
164 t.Fatal("did not get a resource version.")
165 }
166 }
167 t.Logf("Created event %#v", got.ObjectMeta)
168 }
169
170 w, err := dynamicClient.Resource(resource).Namespace("default").Watch(context.TODO(), metav1.ListOptions{
171 ResourceVersion: rv1,
172 Watch: true,
173 FieldSelector: fields.OneTermEqualSelector("metadata.name", "event-9").String(),
174 })
175
176 if err != nil {
177 t.Fatalf("Failed watch: %v", err)
178 }
179 defer w.Stop()
180
181 select {
182 case <-time.After(wait.ForeverTestTimeout):
183 t.Fatalf("watch took longer than %s", wait.ForeverTestTimeout.String())
184 case got, ok := <-w.ResultChan():
185 if !ok {
186 t.Fatal("Watch channel closed unexpectedly.")
187 }
188
189
190
191
192 if e, a := watch.Added, got.Type; e != a {
193 t.Errorf("Wanted %v, got %v", e, a)
194 }
195
196 unstructured, ok := got.Object.(*unstructured.Unstructured)
197 if !ok {
198 t.Fatalf("Unexpected watch event containing object %#q", got.Object)
199 }
200 event, err := unstructuredToEvent(unstructured)
201 if err != nil {
202 t.Fatalf("unexpected error converting Unstructured to v1.Event: %v", err)
203 }
204 if e, a := "event-9", event.Name; e != a {
205 t.Errorf("Wanted %v, got %v", e, a)
206 }
207 }
208 }
209
210 func TestUnstructuredExtract(t *testing.T) {
211 result := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins", "ServiceAccount"}, framework.SharedEtcd())
212 defer result.TearDownFn()
213
214 dynamicClient, err := dynamic.NewForConfig(result.ClientConfig)
215 if err != nil {
216 t.Fatalf("unexpected error creating dynamic client: %v", err)
217 }
218
219 resource := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
220
221
222 name := "test-pod"
223 pod := &unstructured.Unstructured{
224 Object: map[string]interface{}{
225 "apiVersion": "v1",
226 "kind": "Pod",
227 "metadata": map[string]interface{}{
228 "name": name,
229
230
231
232 "namespace": "default",
233 },
234 "spec": map[string]interface{}{
235 "containers": []interface{}{
236 map[string]interface{}{
237 "name": "test",
238 "image": "test-image",
239 },
240 },
241 },
242 },
243 }
244 mgr := "testManager"
245 podData, err := json.Marshal(pod)
246 if err != nil {
247 t.Fatalf("failed to marshal pod into bytes: %v", err)
248 }
249
250
251 actual, err := dynamicClient.Resource(resource).Namespace("default").Patch(
252 context.TODO(),
253 name,
254 types.ApplyPatchType,
255 podData,
256 metav1.PatchOptions{FieldManager: mgr})
257 if err != nil {
258 t.Fatalf("unexpected error when creating pod: %v", err)
259 }
260
261
262 discoveryClient := discovery.NewDiscoveryClientForConfigOrDie(result.ClientConfig)
263 extractor, err := metav1ac.NewUnstructuredExtractor(discoveryClient)
264 if err != nil {
265 t.Fatalf("unexpected error when constructing extrator: %v", err)
266 }
267 extracted, err := extractor.Extract(actual, mgr)
268 if err != nil {
269 t.Fatalf("unexpected error when extracting: %v", err)
270 }
271
272
273 if !reflect.DeepEqual(pod, extracted) {
274 t.Fatalf("extracted pod doesn't equal applied pod. wanted:\n %v\n, got:\n %v\n", pod, extracted)
275 }
276
277 }
278
279 func unstructuredToPod(obj *unstructured.Unstructured) (*v1.Pod, error) {
280 json, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
281 if err != nil {
282 return nil, err
283 }
284 pod := new(v1.Pod)
285 err = runtime.DecodeInto(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), json, pod)
286 pod.Kind = ""
287 pod.APIVersion = ""
288 return pod, err
289 }
290
291 func unstructuredToEvent(obj *unstructured.Unstructured) (*v1.Event, error) {
292 json, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
293 if err != nil {
294 return nil, err
295 }
296 event := new(v1.Event)
297 err = runtime.DecodeInto(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), json, event)
298 return event, err
299 }
300
View as plain text