k8s.io/kubernetes@v1.29.3/pkg/registry/core/node/storage/storage_test.go (about)

     1  /*
     2  Copyright 2015 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 storage
    18  
    19  import (
    20  	"fmt"
    21  	"testing"
    22  
    23  	"k8s.io/apimachinery/pkg/api/resource"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"k8s.io/apimachinery/pkg/fields"
    26  	"k8s.io/apimachinery/pkg/labels"
    27  	"k8s.io/apimachinery/pkg/runtime"
    28  	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
    29  	"k8s.io/apiserver/pkg/registry/generic"
    30  	genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
    31  	"k8s.io/apiserver/pkg/registry/rest"
    32  	etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing"
    33  	api "k8s.io/kubernetes/pkg/apis/core"
    34  	kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
    35  	"k8s.io/kubernetes/pkg/registry/registrytest"
    36  )
    37  
    38  func newStorage(t *testing.T) (*REST, *etcd3testing.EtcdTestServer) {
    39  	etcdStorage, server := registrytest.NewEtcdStorage(t, "")
    40  	restOptions := generic.RESTOptions{
    41  		StorageConfig:           etcdStorage,
    42  		Decorator:               generic.UndecoratedStorage,
    43  		DeleteCollectionWorkers: 1,
    44  		ResourcePrefix:          "nodes",
    45  	}
    46  	storage, err := NewStorage(restOptions, kubeletclient.KubeletClientConfig{
    47  		Port:                  10250,
    48  		PreferredAddressTypes: []string{string(api.NodeInternalIP)},
    49  	}, nil)
    50  	if err != nil {
    51  		t.Fatal(err)
    52  	}
    53  	return storage.Node, server
    54  }
    55  
    56  type tweak func(*api.Node)
    57  
    58  func newNode(name string, tweaks ...tweak) *api.Node {
    59  	node := &api.Node{
    60  		ObjectMeta: metav1.ObjectMeta{
    61  			Name: name,
    62  			Labels: map[string]string{
    63  				"name": name,
    64  			},
    65  		},
    66  		Status: api.NodeStatus{
    67  			Capacity: api.ResourceList{
    68  				api.ResourceCPU:    resource.MustParse("10"),
    69  				api.ResourceMemory: resource.MustParse("0"),
    70  			},
    71  		},
    72  	}
    73  
    74  	for _, tweak := range tweaks {
    75  		tweak(node)
    76  	}
    77  
    78  	return node
    79  }
    80  
    81  func setNodeIPAddress(addr string) tweak {
    82  	return func(node *api.Node) {
    83  		node.Status.Addresses = []api.NodeAddress{
    84  			{Type: api.NodeInternalIP, Address: addr},
    85  		}
    86  	}
    87  }
    88  
    89  func setNodeDaemonEndpoint(port int32) tweak {
    90  	return func(node *api.Node) {
    91  		node.Status.DaemonEndpoints = api.NodeDaemonEndpoints{
    92  			KubeletEndpoint: api.DaemonEndpoint{
    93  				Port: port,
    94  			},
    95  		}
    96  	}
    97  }
    98  
    99  func TestCreate(t *testing.T) {
   100  	storage, server := newStorage(t)
   101  	defer server.Terminate(t)
   102  	defer storage.Store.DestroyFunc()
   103  	test := genericregistrytest.New(t, storage.Store).ClusterScope()
   104  	test.TestCreate(
   105  		// valid
   106  		newNode("foo"),
   107  		// invalid
   108  		newNode("_-a123-a_"),
   109  	)
   110  }
   111  
   112  func TestUpdate(t *testing.T) {
   113  	storage, server := newStorage(t)
   114  	defer server.Terminate(t)
   115  	defer storage.Store.DestroyFunc()
   116  	test := genericregistrytest.New(t, storage.Store).ClusterScope()
   117  	test.TestUpdate(
   118  		// valid
   119  		newNode("foo"),
   120  		// updateFunc
   121  		func(obj runtime.Object) runtime.Object {
   122  			object := obj.(*api.Node)
   123  			object.Spec.Unschedulable = !object.Spec.Unschedulable
   124  			return object
   125  		},
   126  	)
   127  }
   128  
   129  func TestDelete(t *testing.T) {
   130  	storage, server := newStorage(t)
   131  	defer server.Terminate(t)
   132  	defer storage.Store.DestroyFunc()
   133  	test := genericregistrytest.New(t, storage.Store).ClusterScope()
   134  	test.TestDelete(newNode("foo"))
   135  }
   136  
   137  func TestGet(t *testing.T) {
   138  	storage, server := newStorage(t)
   139  	defer server.Terminate(t)
   140  	defer storage.Store.DestroyFunc()
   141  	test := genericregistrytest.New(t, storage.Store).ClusterScope()
   142  	test.TestGet(newNode("foo"))
   143  }
   144  
   145  func TestList(t *testing.T) {
   146  	storage, server := newStorage(t)
   147  	defer server.Terminate(t)
   148  	defer storage.Store.DestroyFunc()
   149  	test := genericregistrytest.New(t, storage.Store).ClusterScope()
   150  	test.TestList(newNode("foo"))
   151  }
   152  
   153  func TestWatch(t *testing.T) {
   154  	storage, server := newStorage(t)
   155  	defer server.Terminate(t)
   156  	defer storage.Store.DestroyFunc()
   157  	test := genericregistrytest.New(t, storage.Store).ClusterScope()
   158  	test.TestWatch(
   159  		newNode("foo"),
   160  		// matching labels
   161  		[]labels.Set{
   162  			{"name": "foo"},
   163  		},
   164  		// not matching labels
   165  		[]labels.Set{
   166  			{"name": "bar"},
   167  			{"foo": "bar"},
   168  		},
   169  		// matching fields
   170  		[]fields.Set{
   171  			{"metadata.name": "foo"},
   172  		},
   173  		// not matching fields
   174  		[]fields.Set{
   175  			{"metadata.name": "bar"},
   176  		},
   177  	)
   178  }
   179  
   180  func TestShortNames(t *testing.T) {
   181  	storage, server := newStorage(t)
   182  	defer server.Terminate(t)
   183  	defer storage.Store.DestroyFunc()
   184  	expected := []string{"no"}
   185  	registrytest.AssertShortNames(t, storage, expected)
   186  }
   187  
   188  func TestResourceLocation(t *testing.T) {
   189  	type testCase struct {
   190  		name  string
   191  		node  *api.Node
   192  		query string
   193  		host  string
   194  		err   bool
   195  	}
   196  
   197  	testCases := []testCase{{
   198  		name:  "proxyable hostname with default port",
   199  		node:  newNode("node0", setNodeIPAddress("10.0.0.1")),
   200  		query: "node0",
   201  		host:  "10.0.0.1:10250",
   202  	}, {
   203  		name:  "proxyable hostname with kubelet port in query",
   204  		node:  newNode("node0", setNodeIPAddress("10.0.0.1")),
   205  		query: "node0:5000",
   206  		host:  "10.0.0.1:5000",
   207  	}, {
   208  		name:  "proxyable hostname with kubelet port in status",
   209  		node:  newNode("node0", setNodeIPAddress("10.0.0.1"), setNodeDaemonEndpoint(5000)),
   210  		query: "node0",
   211  		host:  "10.0.0.1:5000",
   212  	}, {
   213  		name:  "non-proxyable hostname with default port",
   214  		node:  newNode("node0", setNodeIPAddress("127.0.0.1")),
   215  		query: "node0",
   216  		host:  "",
   217  		err:   true,
   218  	}, {
   219  		name:  "non-proxyable hostname with kubelet port in query",
   220  		node:  newNode("node0", setNodeIPAddress("127.0.0.1")),
   221  		query: "node0:5000",
   222  		host:  "",
   223  		err:   true,
   224  	}, {
   225  		name:  "non-proxyable hostname with kubelet port in status",
   226  		node:  newNode("node0", setNodeIPAddress("127.0.0.1"), setNodeDaemonEndpoint(443)),
   227  		query: "node0",
   228  		host:  "",
   229  		err:   true,
   230  	}}
   231  
   232  	for _, testCase := range testCases {
   233  		t.Run(testCase.name, func(t *testing.T) {
   234  			storage, server := newStorage(t)
   235  			defer server.Terminate(t)
   236  			defer storage.Store.DestroyFunc()
   237  
   238  			ctx := genericapirequest.WithNamespace(genericapirequest.NewDefaultContext(), fmt.Sprintf("namespace-%s", testCase.name))
   239  			key, _ := storage.KeyFunc(ctx, testCase.node.Name)
   240  			if err := storage.Storage.Create(ctx, key, testCase.node, nil, 0, false); err != nil {
   241  				t.Fatalf("unexpected error: %v", err)
   242  			}
   243  
   244  			redirector := rest.Redirector(storage)
   245  			location, _, err := redirector.ResourceLocation(ctx, testCase.query)
   246  
   247  			if err != nil {
   248  				if !testCase.err {
   249  					t.Fatalf("Unexpected error: %v", err)
   250  				}
   251  				return
   252  			} else if testCase.err {
   253  				t.Fatalf("Expected error but got none")
   254  			}
   255  
   256  			if location == nil {
   257  				t.Errorf("Unexpected nil resource location: %v", location)
   258  			}
   259  
   260  			if location.Host != testCase.host {
   261  				t.Errorf("Unexpected host: expected %v, but got %v", testCase.host, location.Host)
   262  			}
   263  		})
   264  	}
   265  }