...

Source file src/k8s.io/kubernetes/test/integration/client/dynamic_client_test.go

Documentation: k8s.io/kubernetes/test/integration/client

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    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  	// Create a Pod with the normal client
    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  	// check dynamic list
    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  	// check dynamic get
    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  	// delete the pod dynamically
   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  		// We expect to see an ADD of event-9 and only event-9. (This
   190  		// catches a bug where all the events would have been sent down
   191  		// the channel.)
   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  	// Apply an unstructured with the dynamic client
   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  				// namespace will always get set by extract,
   230  				// so we add it here (even though it's optional)
   231  				// to ensure what we apply equals what we extract.
   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  	// apply the unstructured object to the cluster
   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  	// extract the object
   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  	// confirm that the extracted object equals the applied object
   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