k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/registry/networking/ingress/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  	"testing"
    21  
    22  	"github.com/google/go-cmp/cmp"
    23  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    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  	"k8s.io/apimachinery/pkg/util/intstr"
    29  	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
    30  	"k8s.io/apiserver/pkg/registry/generic"
    31  	genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
    32  	"k8s.io/apiserver/pkg/registry/rest"
    33  	etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing"
    34  	_ "k8s.io/kubernetes/pkg/apis/extensions/install"
    35  	"k8s.io/kubernetes/pkg/apis/networking"
    36  	_ "k8s.io/kubernetes/pkg/apis/networking/install"
    37  	"k8s.io/kubernetes/pkg/registry/registrytest"
    38  )
    39  
    40  func newStorage(t *testing.T) (*REST, *StatusREST, *etcd3testing.EtcdTestServer) {
    41  	etcdStorage, server := registrytest.NewEtcdStorageForResource(t, networking.Resource("ingresses"))
    42  	restOptions := generic.RESTOptions{
    43  		StorageConfig:           etcdStorage,
    44  		Decorator:               generic.UndecoratedStorage,
    45  		DeleteCollectionWorkers: 1,
    46  		ResourcePrefix:          "ingresses",
    47  	}
    48  	ingressStorage, statusStorage, err := NewREST(restOptions)
    49  	if err != nil {
    50  		t.Fatalf("unexpected error from REST storage: %v", err)
    51  	}
    52  	return ingressStorage, statusStorage, server
    53  }
    54  
    55  var (
    56  	namespace           = "test" // genericregistrytest.Tester hardcode namespace that will be used when creating contexts.
    57  	name                = "foo-ingress"
    58  	defaultHostname     = "foo.bar.com"
    59  	defaultBackendName  = "default-backend"
    60  	defaultBackendPort  = intstr.FromInt32(80)
    61  	defaultLoadBalancer = "127.0.0.1"
    62  	defaultPath         = "/foo"
    63  	defaultPathType     = networking.PathTypeImplementationSpecific
    64  	defaultPathMap      = map[string]string{defaultPath: defaultBackendName}
    65  	defaultTLS          = []networking.IngressTLS{
    66  		{Hosts: []string{"foo.bar.com", "*.bar.com"}, SecretName: "foosecret"},
    67  	}
    68  	serviceBackend = &networking.IngressServiceBackend{
    69  		Name: "defaultbackend",
    70  		Port: networking.ServiceBackendPort{
    71  			Name:   "",
    72  			Number: 80,
    73  		},
    74  	}
    75  )
    76  
    77  type IngressRuleValues map[string]string
    78  
    79  func toHTTPIngressPaths(pathMap map[string]string) []networking.HTTPIngressPath {
    80  	httpPaths := []networking.HTTPIngressPath{}
    81  	for path, backend := range pathMap {
    82  		httpPaths = append(httpPaths, networking.HTTPIngressPath{
    83  			Path:     path,
    84  			PathType: &defaultPathType,
    85  			Backend: networking.IngressBackend{
    86  				Service: &networking.IngressServiceBackend{
    87  					Name: backend,
    88  					Port: networking.ServiceBackendPort{
    89  						Name:   defaultBackendPort.StrVal,
    90  						Number: defaultBackendPort.IntVal,
    91  					},
    92  				},
    93  			},
    94  		})
    95  	}
    96  	return httpPaths
    97  }
    98  
    99  func toIngressRules(hostRules map[string]IngressRuleValues) []networking.IngressRule {
   100  	rules := []networking.IngressRule{}
   101  	for host, pathMap := range hostRules {
   102  		rules = append(rules, networking.IngressRule{
   103  			Host: host,
   104  			IngressRuleValue: networking.IngressRuleValue{
   105  				HTTP: &networking.HTTPIngressRuleValue{
   106  					Paths: toHTTPIngressPaths(pathMap),
   107  				},
   108  			},
   109  		})
   110  	}
   111  	return rules
   112  }
   113  
   114  func newIngress(pathMap map[string]string) *networking.Ingress {
   115  	return &networking.Ingress{
   116  		ObjectMeta: metav1.ObjectMeta{
   117  			Name:      name,
   118  			Namespace: namespace,
   119  		},
   120  		Spec: networking.IngressSpec{
   121  			DefaultBackend: &networking.IngressBackend{
   122  				Service: serviceBackend.DeepCopy(),
   123  			},
   124  			Rules: toIngressRules(map[string]IngressRuleValues{
   125  				defaultHostname: pathMap,
   126  			}),
   127  			TLS: defaultTLS,
   128  		},
   129  		Status: networking.IngressStatus{
   130  			LoadBalancer: networking.IngressLoadBalancerStatus{
   131  				Ingress: []networking.IngressLoadBalancerIngress{
   132  					{IP: defaultLoadBalancer},
   133  				},
   134  			},
   135  		},
   136  	}
   137  }
   138  
   139  func validIngress() *networking.Ingress {
   140  	return newIngress(defaultPathMap)
   141  }
   142  
   143  func TestCreate(t *testing.T) {
   144  	storage, _, server := newStorage(t)
   145  	defer server.Terminate(t)
   146  	defer storage.Store.DestroyFunc()
   147  	test := genericregistrytest.New(t, storage.Store)
   148  	ingress := validIngress()
   149  	noDefaultBackendAndRules := validIngress()
   150  	noDefaultBackendAndRules.Spec.DefaultBackend.Service = &networking.IngressServiceBackend{}
   151  	noDefaultBackendAndRules.Spec.Rules = []networking.IngressRule{}
   152  	badPath := validIngress()
   153  	badPath.Spec.Rules = toIngressRules(map[string]IngressRuleValues{
   154  		"foo.bar.com": {"invalid-no-leading-slash": "svc"}})
   155  	test.TestCreate(
   156  		// valid
   157  		ingress,
   158  		noDefaultBackendAndRules,
   159  		badPath,
   160  	)
   161  }
   162  
   163  func TestUpdate(t *testing.T) {
   164  	storage, _, server := newStorage(t)
   165  	defer server.Terminate(t)
   166  	defer storage.Store.DestroyFunc()
   167  	test := genericregistrytest.New(t, storage.Store)
   168  	test.TestUpdate(
   169  		// valid
   170  		validIngress(),
   171  		// updateFunc
   172  		func(obj runtime.Object) runtime.Object {
   173  			object := obj.(*networking.Ingress)
   174  			object.Spec.Rules = toIngressRules(map[string]IngressRuleValues{
   175  				"bar.foo.com": {"/bar": defaultBackendName},
   176  			})
   177  			object.Spec.TLS = append(object.Spec.TLS, networking.IngressTLS{
   178  				Hosts:      []string{"*.google.com"},
   179  				SecretName: "googlesecret",
   180  			})
   181  			return object
   182  		},
   183  		// invalid updateFunc: ObjectMeta is not to be tampered with.
   184  		func(obj runtime.Object) runtime.Object {
   185  			object := obj.(*networking.Ingress)
   186  			object.Name = ""
   187  			return object
   188  		},
   189  
   190  		func(obj runtime.Object) runtime.Object {
   191  			object := obj.(*networking.Ingress)
   192  			object.Spec.Rules = toIngressRules(map[string]IngressRuleValues{
   193  				"foo.bar.com": {"invalid-no-leading-slash": "svc"}})
   194  			return object
   195  		},
   196  	)
   197  }
   198  
   199  func TestDelete(t *testing.T) {
   200  	storage, _, server := newStorage(t)
   201  	defer server.Terminate(t)
   202  	defer storage.Store.DestroyFunc()
   203  	test := genericregistrytest.New(t, storage.Store)
   204  	test.TestDelete(validIngress())
   205  }
   206  
   207  func TestGet(t *testing.T) {
   208  	storage, _, server := newStorage(t)
   209  	defer server.Terminate(t)
   210  	defer storage.Store.DestroyFunc()
   211  	test := genericregistrytest.New(t, storage.Store)
   212  	test.TestGet(validIngress())
   213  }
   214  
   215  func TestList(t *testing.T) {
   216  	storage, _, server := newStorage(t)
   217  	defer server.Terminate(t)
   218  	defer storage.Store.DestroyFunc()
   219  	test := genericregistrytest.New(t, storage.Store)
   220  	test.TestList(validIngress())
   221  }
   222  
   223  func TestWatch(t *testing.T) {
   224  	storage, _, server := newStorage(t)
   225  	defer server.Terminate(t)
   226  	defer storage.Store.DestroyFunc()
   227  	test := genericregistrytest.New(t, storage.Store)
   228  	test.TestWatch(
   229  		validIngress(),
   230  		// matching labels
   231  		[]labels.Set{},
   232  		// not matching labels
   233  		[]labels.Set{
   234  			{"a": "c"},
   235  			{"foo": "bar"},
   236  		},
   237  		// matching fields
   238  		[]fields.Set{
   239  			{"metadata.name": name},
   240  		},
   241  		// not matching fields
   242  		[]fields.Set{
   243  			{"metadata.name": "bar"},
   244  			{"name": name},
   245  		},
   246  	)
   247  }
   248  
   249  func TestUpdateStatus(t *testing.T) {
   250  	storage, statusStorage, server := newStorage(t)
   251  	defer server.Terminate(t)
   252  	defer storage.Store.DestroyFunc()
   253  
   254  	ingStart := validIngress()
   255  	ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace)
   256  	key, _ := storage.KeyFunc(ctx, ingStart.Name)
   257  	err := storage.Storage.Create(ctx, key, ingStart, nil, 0, false)
   258  	if err != nil {
   259  		t.Fatalf("Unexpected error: %v", err)
   260  	}
   261  
   262  	ing := ingStart.DeepCopy()
   263  	ing.Status.LoadBalancer.Ingress = []networking.IngressLoadBalancerIngress{
   264  		{
   265  			IP: defaultLoadBalancer,
   266  		},
   267  	}
   268  	_, _, err = statusStorage.Update(ctx, ing.Name, rest.DefaultUpdatedObjectInfo(ing), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{})
   269  	if err != nil {
   270  		t.Fatalf("Unexpected error: %v", err)
   271  	}
   272  	obj, err := storage.Get(ctx, ing.Name, &metav1.GetOptions{})
   273  	if err != nil {
   274  		t.Errorf("unexpected error: %v", err)
   275  	}
   276  	ingOut := obj.(*networking.Ingress)
   277  	// only compare relevant changes b/c of difference in metadata
   278  	if !apiequality.Semantic.DeepEqual(ing.Status, ingOut.Status) {
   279  		t.Errorf("unexpected object: %s", cmp.Diff(ing.Status, ingOut.Status))
   280  	}
   281  }
   282  
   283  func TestShortNames(t *testing.T) {
   284  	storage, _, server := newStorage(t)
   285  	defer server.Terminate(t)
   286  	defer storage.Store.DestroyFunc()
   287  	expected := []string{"ing"}
   288  	registrytest.AssertShortNames(t, storage, expected)
   289  }