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 }