github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/caas/kubernetes/provider/ingress_test.go (about) 1 // Copyright 2019 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package provider_test 5 6 import ( 7 jc "github.com/juju/testing/checkers" 8 "go.uber.org/mock/gomock" 9 gc "gopkg.in/check.v1" 10 apps "k8s.io/api/apps/v1" 11 appsv1 "k8s.io/api/apps/v1" 12 core "k8s.io/api/core/v1" 13 networkingv1 "k8s.io/api/networking/v1" 14 networkingv1beta1 "k8s.io/api/networking/v1beta1" 15 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 16 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 17 "k8s.io/apimachinery/pkg/runtime" 18 k8stypes "k8s.io/apimachinery/pkg/types" 19 "k8s.io/apimachinery/pkg/util/intstr" 20 k8sversion "k8s.io/apimachinery/pkg/version" 21 "k8s.io/utils/pointer" 22 23 "github.com/juju/juju/caas" 24 "github.com/juju/juju/caas/kubernetes/provider" 25 k8sspecs "github.com/juju/juju/caas/kubernetes/provider/specs" 26 "github.com/juju/juju/core/config" 27 "github.com/juju/juju/core/resources" 28 "github.com/juju/juju/core/status" 29 "github.com/juju/juju/testing" 30 ) 31 32 func (s *K8sBrokerSuite) assertIngressResources(c *gc.C, ingressResources []k8sspecs.K8sIngress, expectedErrString string, assertCalls ...any) { 33 basicPodSpec := getBasicPodspec() 34 basicPodSpec.ProviderPod = &k8sspecs.K8sPodSpec{ 35 KubernetesResources: &k8sspecs.KubernetesResources{ 36 IngressResources: ingressResources, 37 }, 38 } 39 workloadSpec, err := provider.PrepareWorkloadSpec( 40 "app-name", "app-name", basicPodSpec, resources.DockerImageDetails{RegistryPath: "operator/image-path"}, 41 ) 42 c.Assert(err, jc.ErrorIsNil) 43 podSpec := provider.Pod(workloadSpec).PodSpec 44 45 numUnits := int32(2) 46 statefulSetArg := &appsv1.StatefulSet{ 47 ObjectMeta: metav1.ObjectMeta{ 48 Name: "app-name", 49 Labels: map[string]string{"app.kubernetes.io/managed-by": "juju", "app.kubernetes.io/name": "app-name"}, 50 Annotations: map[string]string{ 51 "app.juju.is/uuid": "appuuid", 52 "controller.juju.is/id": testing.ControllerTag.Id(), 53 "charm.juju.is/modified-version": "0", 54 }, 55 }, 56 Spec: appsv1.StatefulSetSpec{ 57 Replicas: &numUnits, 58 Selector: &metav1.LabelSelector{ 59 MatchLabels: map[string]string{"app.kubernetes.io/name": "app-name"}, 60 }, 61 RevisionHistoryLimit: pointer.Int32Ptr(0), 62 Template: core.PodTemplateSpec{ 63 ObjectMeta: metav1.ObjectMeta{ 64 Labels: map[string]string{"app.kubernetes.io/name": "app-name"}, 65 Annotations: map[string]string{ 66 "apparmor.security.beta.kubernetes.io/pod": "runtime/default", 67 "seccomp.security.beta.kubernetes.io/pod": "docker/default", 68 "controller.juju.is/id": testing.ControllerTag.Id(), 69 "charm.juju.is/modified-version": "0", 70 }, 71 }, 72 Spec: podSpec, 73 }, 74 PodManagementPolicy: apps.ParallelPodManagement, 75 ServiceName: "app-name-endpoints", 76 }, 77 } 78 79 serviceArg := *basicServiceArg 80 serviceArg.Spec.Type = core.ServiceTypeClusterIP 81 82 assertCalls = append( 83 []any{ 84 s.mockStatefulSets.EXPECT().Get(gomock.Any(), "juju-operator-app-name", metav1.GetOptions{}). 85 Return(nil, s.k8sNotFoundError()), 86 }, 87 assertCalls..., 88 ) 89 90 ociImageSecret := s.getOCIImageSecret(c, nil) 91 if expectedErrString == "" { 92 // no error expected, so continue to check following assertions. 93 assertCalls = append(assertCalls, []any{ 94 s.mockSecrets.EXPECT().Create(gomock.Any(), ociImageSecret, metav1.CreateOptions{}). 95 Return(ociImageSecret, nil), 96 s.mockServices.EXPECT().Get(gomock.Any(), "app-name", metav1.GetOptions{}). 97 Return(nil, s.k8sNotFoundError()), 98 s.mockServices.EXPECT().Update(gomock.Any(), &serviceArg, metav1.UpdateOptions{}). 99 Return(nil, s.k8sNotFoundError()), 100 s.mockServices.EXPECT().Create(gomock.Any(), &serviceArg, metav1.CreateOptions{}). 101 Return(nil, nil), 102 s.mockServices.EXPECT().Get(gomock.Any(), "app-name-endpoints", metav1.GetOptions{}). 103 Return(nil, s.k8sNotFoundError()), 104 s.mockServices.EXPECT().Update(gomock.Any(), basicHeadlessServiceArg, metav1.UpdateOptions{}). 105 Return(nil, s.k8sNotFoundError()), 106 s.mockServices.EXPECT().Create(gomock.Any(), basicHeadlessServiceArg, metav1.CreateOptions{}). 107 Return(nil, nil), 108 s.mockStatefulSets.EXPECT().Get(gomock.Any(), "app-name", metav1.GetOptions{}). 109 Return(statefulSetArg, nil), 110 s.mockStatefulSets.EXPECT().Create(gomock.Any(), statefulSetArg, metav1.CreateOptions{}). 111 Return(nil, nil), 112 }...) 113 } 114 gomock.InOrder(assertCalls...) 115 116 params := &caas.ServiceParams{ 117 PodSpec: basicPodSpec, 118 Deployment: caas.DeploymentParams{ 119 DeploymentType: caas.DeploymentStateful, 120 }, 121 ImageDetails: resources.DockerImageDetails{RegistryPath: "operator/image-path"}, 122 ResourceTags: map[string]string{"juju-controller-uuid": testing.ControllerTag.Id()}, 123 } 124 err = s.broker.EnsureService("app-name", func(_ string, _ status.Status, e string, _ map[string]interface{}) error { 125 c.Logf("EnsureService error -> %q", e) 126 return nil 127 }, params, 2, config.ConfigAttributes{ 128 "kubernetes-service-loadbalancer-ip": "10.0.0.1", 129 "kubernetes-service-externalname": "ext-name", 130 }) 131 if expectedErrString != "" { 132 c.Assert(err, gc.ErrorMatches, expectedErrString) 133 } else { 134 c.Assert(err, jc.ErrorIsNil) 135 } 136 } 137 138 func (s *K8sBrokerSuite) TestEnsureServiceIngressResourcesCreateV1Beta1(c *gc.C) { 139 ctrl := s.setupController(c) 140 defer ctrl.Finish() 141 142 gomock.InOrder( 143 s.mockDiscovery.EXPECT().ServerVersion().Return(&k8sversion.Info{ 144 Major: "1", Minor: "18", 145 }, nil), 146 ) 147 148 ingress1Rule1 := networkingv1beta1.IngressRule{ 149 IngressRuleValue: networkingv1beta1.IngressRuleValue{ 150 HTTP: &networkingv1beta1.HTTPIngressRuleValue{ 151 Paths: []networkingv1beta1.HTTPIngressPath{ 152 { 153 Path: "/testpath", 154 Backend: networkingv1beta1.IngressBackend{ 155 ServiceName: "test", 156 ServicePort: intstr.IntOrString{IntVal: 80}, 157 }, 158 }, 159 }, 160 }, 161 }, 162 } 163 ingress1 := k8sspecs.K8sIngress{ 164 Meta: k8sspecs.Meta{ 165 Name: "test-ingress", 166 Labels: map[string]string{ 167 "foo": "bar", 168 }, 169 Annotations: map[string]string{ 170 "nginx.ingress.kubernetes.io/rewrite-target": "/", 171 }, 172 }, 173 Spec: k8sspecs.K8sIngressSpec{ 174 Version: k8sspecs.K8sIngressV1Beta1, 175 SpecV1Beta1: networkingv1beta1.IngressSpec{ 176 Rules: []networkingv1beta1.IngressRule{ingress1Rule1}, 177 }, 178 }, 179 } 180 181 ingressResources := []k8sspecs.K8sIngress{ingress1} 182 ingress := &networkingv1beta1.Ingress{ 183 ObjectMeta: metav1.ObjectMeta{ 184 Name: "test-ingress", 185 Labels: map[string]string{"app.kubernetes.io/managed-by": "juju", "app.kubernetes.io/name": "app-name", "foo": "bar"}, 186 Annotations: map[string]string{ 187 "nginx.ingress.kubernetes.io/rewrite-target": "/", 188 "controller.juju.is/id": "deadbeef-1bad-500d-9000-4b1d0d06f00d", 189 }, 190 }, 191 Spec: networkingv1beta1.IngressSpec{ 192 Rules: []networkingv1beta1.IngressRule{ingress1Rule1}, 193 }, 194 } 195 s.assertIngressResources( 196 c, ingressResources, "", 197 s.mockIngressV1Beta1.EXPECT().Create(gomock.Any(), ingress, metav1.CreateOptions{}).Return(ingress, nil), 198 ) 199 } 200 201 func (s *K8sBrokerSuite) TestEnsureServiceIngressResourcesUpdateV1Beta1(c *gc.C) { 202 ctrl := s.setupController(c) 203 defer ctrl.Finish() 204 205 gomock.InOrder( 206 s.mockDiscovery.EXPECT().ServerVersion().Return(&k8sversion.Info{ 207 Major: "1", Minor: "18", 208 }, nil), 209 ) 210 211 ingress1Rule1 := networkingv1beta1.IngressRule{ 212 IngressRuleValue: networkingv1beta1.IngressRuleValue{ 213 HTTP: &networkingv1beta1.HTTPIngressRuleValue{ 214 Paths: []networkingv1beta1.HTTPIngressPath{ 215 { 216 Path: "/testpath", 217 Backend: networkingv1beta1.IngressBackend{ 218 ServiceName: "test", 219 ServicePort: intstr.IntOrString{IntVal: 80}, 220 }, 221 }, 222 }, 223 }, 224 }, 225 } 226 ingress1 := k8sspecs.K8sIngress{ 227 Meta: k8sspecs.Meta{ 228 Name: "test-ingress", 229 Labels: map[string]string{ 230 "foo": "bar", 231 }, 232 Annotations: map[string]string{ 233 "nginx.ingress.kubernetes.io/rewrite-target": "/", 234 }, 235 }, 236 Spec: k8sspecs.K8sIngressSpec{ 237 Version: k8sspecs.K8sIngressV1Beta1, 238 SpecV1Beta1: networkingv1beta1.IngressSpec{ 239 Rules: []networkingv1beta1.IngressRule{ingress1Rule1}, 240 }, 241 }, 242 } 243 244 ingressResources := []k8sspecs.K8sIngress{ingress1} 245 ingress := &networkingv1beta1.Ingress{ 246 ObjectMeta: metav1.ObjectMeta{ 247 Name: "test-ingress", 248 Labels: map[string]string{"app.kubernetes.io/managed-by": "juju", "app.kubernetes.io/name": "app-name", "foo": "bar"}, 249 Annotations: map[string]string{ 250 "nginx.ingress.kubernetes.io/rewrite-target": "/", 251 "controller.juju.is/id": "deadbeef-1bad-500d-9000-4b1d0d06f00d", 252 }, 253 }, 254 Spec: networkingv1beta1.IngressSpec{ 255 Rules: []networkingv1beta1.IngressRule{ingress1Rule1}, 256 }, 257 } 258 data, err := runtime.Encode(unstructured.UnstructuredJSONScheme, ingress) 259 c.Assert(err, jc.ErrorIsNil) 260 s.assertIngressResources( 261 c, ingressResources, "", 262 s.mockIngressV1Beta1.EXPECT().Create(gomock.Any(), ingress, metav1.CreateOptions{}).Return(nil, s.k8sAlreadyExistsError()), 263 s.mockIngressV1Beta1.EXPECT().Get(gomock.Any(), "test-ingress", metav1.GetOptions{}).Return(ingress, nil), 264 s.mockIngressV1Beta1.EXPECT(). 265 Patch(gomock.Any(), ingress.GetName(), k8stypes.StrategicMergePatchType, data, metav1.PatchOptions{FieldManager: "juju"}). 266 Return(ingress, nil), 267 ) 268 } 269 270 func (s *K8sBrokerSuite) TestEnsureServiceIngressResourcesUpdateConflictWithExistingNonJujuManagedIngressV1Beta1(c *gc.C) { 271 ctrl := s.setupController(c) 272 defer ctrl.Finish() 273 274 gomock.InOrder( 275 s.mockDiscovery.EXPECT().ServerVersion().Return(&k8sversion.Info{ 276 Major: "1", Minor: "18", 277 }, nil), 278 ) 279 280 ingress1Rule1 := networkingv1beta1.IngressRule{ 281 IngressRuleValue: networkingv1beta1.IngressRuleValue{ 282 HTTP: &networkingv1beta1.HTTPIngressRuleValue{ 283 Paths: []networkingv1beta1.HTTPIngressPath{ 284 { 285 Path: "/testpath", 286 Backend: networkingv1beta1.IngressBackend{ 287 ServiceName: "test", 288 ServicePort: intstr.IntOrString{IntVal: 80}, 289 }, 290 }, 291 }, 292 }, 293 }, 294 } 295 ingress1 := k8sspecs.K8sIngress{ 296 Meta: k8sspecs.Meta{ 297 Name: "test-ingress", 298 Labels: map[string]string{ 299 "foo": "bar", 300 }, 301 Annotations: map[string]string{ 302 "nginx.ingress.kubernetes.io/rewrite-target": "/", 303 }, 304 }, 305 306 Spec: k8sspecs.K8sIngressSpec{ 307 Version: k8sspecs.K8sIngressV1Beta1, 308 SpecV1Beta1: networkingv1beta1.IngressSpec{ 309 Rules: []networkingv1beta1.IngressRule{ingress1Rule1}, 310 }, 311 }, 312 } 313 314 ingressResources := []k8sspecs.K8sIngress{ingress1} 315 316 getIngress := func() *networkingv1beta1.Ingress { 317 return &networkingv1beta1.Ingress{ 318 ObjectMeta: metav1.ObjectMeta{ 319 Name: "test-ingress", 320 Labels: map[string]string{"app.kubernetes.io/managed-by": "juju", "app.kubernetes.io/name": "app-name", "foo": "bar"}, 321 Annotations: map[string]string{ 322 "nginx.ingress.kubernetes.io/rewrite-target": "/", 323 "controller.juju.is/id": "deadbeef-1bad-500d-9000-4b1d0d06f00d", 324 }, 325 }, 326 Spec: networkingv1beta1.IngressSpec{ 327 Rules: []networkingv1beta1.IngressRule{ingress1Rule1}, 328 }, 329 } 330 } 331 ingress := getIngress() 332 existingNonJujuManagedIngress := getIngress() 333 existingNonJujuManagedIngress.SetLabels(map[string]string{}) 334 s.assertIngressResources( 335 c, ingressResources, `creating or updating ingress resources: ensuring ingress "test-ingress" with version "v1beta1": existing ingress "test-ingress" found which does not belong to "app-name"`, 336 s.mockIngressV1Beta1.EXPECT().Create(gomock.Any(), ingress, gomock.Any()).Return(nil, s.k8sAlreadyExistsError()), 337 s.mockIngressV1Beta1.EXPECT().Get(gomock.Any(), "test-ingress", metav1.GetOptions{}).Return(existingNonJujuManagedIngress, nil), 338 ) 339 } 340 341 func (s *K8sBrokerSuite) TestEnsureServiceIngressResourcesCreateV1(c *gc.C) { 342 ctrl := s.setupController(c) 343 defer ctrl.Finish() 344 345 gomock.InOrder( 346 s.mockDiscovery.EXPECT().ServerVersion().Return(&k8sversion.Info{ 347 Major: "1", Minor: "22", 348 }, nil), 349 ) 350 351 ingress1Rule1 := networkingv1.IngressRule{ 352 IngressRuleValue: networkingv1.IngressRuleValue{ 353 HTTP: &networkingv1.HTTPIngressRuleValue{ 354 Paths: []networkingv1.HTTPIngressPath{ 355 { 356 Path: "/testpath", 357 Backend: networkingv1.IngressBackend{ 358 Resource: &core.TypedLocalObjectReference{ 359 APIGroup: pointer.StringPtr("k8s.example.com"), 360 Kind: "StorageBucket", 361 Name: "icon-assets", 362 }, 363 }, 364 }, 365 }, 366 }, 367 }, 368 } 369 ingress1 := k8sspecs.K8sIngress{ 370 Meta: k8sspecs.Meta{ 371 Name: "test-ingress", 372 Labels: map[string]string{ 373 "foo": "bar", 374 }, 375 Annotations: map[string]string{ 376 "nginx.ingress.kubernetes.io/rewrite-target": "/", 377 }, 378 }, 379 Spec: k8sspecs.K8sIngressSpec{ 380 Version: k8sspecs.K8sIngressV1, 381 SpecV1: networkingv1.IngressSpec{ 382 Rules: []networkingv1.IngressRule{ingress1Rule1}, 383 }, 384 }, 385 } 386 387 ingressResources := []k8sspecs.K8sIngress{ingress1} 388 ingress := &networkingv1.Ingress{ 389 ObjectMeta: metav1.ObjectMeta{ 390 Name: "test-ingress", 391 Labels: map[string]string{ 392 "foo": "bar", 393 "app.kubernetes.io/name": "app-name", 394 "app.kubernetes.io/managed-by": "juju", 395 }, 396 Annotations: map[string]string{ 397 "nginx.ingress.kubernetes.io/rewrite-target": "/", 398 "controller.juju.is/id": "deadbeef-1bad-500d-9000-4b1d0d06f00d", 399 }, 400 }, 401 Spec: networkingv1.IngressSpec{ 402 Rules: []networkingv1.IngressRule{ingress1Rule1}, 403 }, 404 } 405 s.assertIngressResources( 406 c, ingressResources, "", 407 s.mockIngressV1.EXPECT().Create(gomock.Any(), ingress, gomock.Any()).Return(ingress, nil), 408 ) 409 } 410 411 func (s *K8sBrokerSuite) TestEnsureServiceIngressResourcesUpdateV1(c *gc.C) { 412 ctrl := s.setupController(c) 413 defer ctrl.Finish() 414 415 gomock.InOrder( 416 s.mockDiscovery.EXPECT().ServerVersion().Return(&k8sversion.Info{ 417 Major: "1", Minor: "22", 418 }, nil), 419 ) 420 421 ingress1Rule1 := networkingv1.IngressRule{ 422 IngressRuleValue: networkingv1.IngressRuleValue{ 423 HTTP: &networkingv1.HTTPIngressRuleValue{ 424 Paths: []networkingv1.HTTPIngressPath{ 425 { 426 Path: "/testpath", 427 Backend: networkingv1.IngressBackend{ 428 Resource: &core.TypedLocalObjectReference{ 429 APIGroup: pointer.StringPtr("k8s.example.com"), 430 Kind: "StorageBucket", 431 Name: "icon-assets", 432 }, 433 }, 434 }, 435 }, 436 }, 437 }, 438 } 439 ingress1 := k8sspecs.K8sIngress{ 440 Meta: k8sspecs.Meta{ 441 Name: "test-ingress", 442 Labels: map[string]string{ 443 "foo": "bar", 444 }, 445 Annotations: map[string]string{ 446 "nginx.ingress.kubernetes.io/rewrite-target": "/", 447 }, 448 }, 449 Spec: k8sspecs.K8sIngressSpec{ 450 Version: k8sspecs.K8sIngressV1, 451 SpecV1: networkingv1.IngressSpec{ 452 Rules: []networkingv1.IngressRule{ingress1Rule1}, 453 }, 454 }, 455 } 456 457 ingressResources := []k8sspecs.K8sIngress{ingress1} 458 ingress := &networkingv1.Ingress{ 459 ObjectMeta: metav1.ObjectMeta{ 460 Name: "test-ingress", 461 Labels: map[string]string{ 462 "foo": "bar", 463 "app.kubernetes.io/name": "app-name", 464 "app.kubernetes.io/managed-by": "juju", 465 }, 466 Annotations: map[string]string{ 467 "nginx.ingress.kubernetes.io/rewrite-target": "/", 468 "controller.juju.is/id": "deadbeef-1bad-500d-9000-4b1d0d06f00d", 469 }, 470 }, 471 Spec: networkingv1.IngressSpec{ 472 Rules: []networkingv1.IngressRule{ingress1Rule1}, 473 }, 474 } 475 data, err := runtime.Encode(unstructured.UnstructuredJSONScheme, ingress) 476 c.Assert(err, jc.ErrorIsNil) 477 s.assertIngressResources( 478 c, ingressResources, "", 479 s.mockIngressV1.EXPECT().Create(gomock.Any(), ingress, gomock.Any()).Return(nil, s.k8sAlreadyExistsError()), 480 s.mockIngressV1.EXPECT().Get(gomock.Any(), "test-ingress", metav1.GetOptions{}).Return(ingress, nil), 481 s.mockIngressV1.EXPECT(). 482 Patch(gomock.Any(), ingress.GetName(), k8stypes.StrategicMergePatchType, data, metav1.PatchOptions{FieldManager: "juju"}). 483 Return(ingress, nil), 484 ) 485 } 486 487 func (s *K8sBrokerSuite) TestEnsureServiceIngressResourcesUpdateConflictWithExistingNonJujuManagedIngressV1(c *gc.C) { 488 ctrl := s.setupController(c) 489 defer ctrl.Finish() 490 491 gomock.InOrder( 492 s.mockDiscovery.EXPECT().ServerVersion().Return(&k8sversion.Info{ 493 Major: "1", Minor: "22", 494 }, nil), 495 ) 496 497 ingress1Rule1 := networkingv1.IngressRule{ 498 IngressRuleValue: networkingv1.IngressRuleValue{ 499 HTTP: &networkingv1.HTTPIngressRuleValue{ 500 Paths: []networkingv1.HTTPIngressPath{ 501 { 502 Path: "/testpath", 503 Backend: networkingv1.IngressBackend{ 504 Resource: &core.TypedLocalObjectReference{ 505 APIGroup: pointer.StringPtr("k8s.example.com"), 506 Kind: "StorageBucket", 507 Name: "icon-assets", 508 }, 509 }, 510 }, 511 }, 512 }, 513 }, 514 } 515 ingress1 := k8sspecs.K8sIngress{ 516 Meta: k8sspecs.Meta{ 517 Name: "test-ingress", 518 Labels: map[string]string{ 519 "foo": "bar", 520 }, 521 Annotations: map[string]string{ 522 "nginx.ingress.kubernetes.io/rewrite-target": "/", 523 }, 524 }, 525 526 Spec: k8sspecs.K8sIngressSpec{ 527 Version: k8sspecs.K8sIngressV1, 528 SpecV1: networkingv1.IngressSpec{ 529 Rules: []networkingv1.IngressRule{ingress1Rule1}, 530 }, 531 }, 532 } 533 534 ingressResources := []k8sspecs.K8sIngress{ingress1} 535 536 getIngress := func() *networkingv1.Ingress { 537 return &networkingv1.Ingress{ 538 ObjectMeta: metav1.ObjectMeta{ 539 Name: "test-ingress", 540 Labels: map[string]string{ 541 "foo": "bar", 542 "app.kubernetes.io/name": "app-name", 543 "app.kubernetes.io/managed-by": "juju", 544 }, 545 Annotations: map[string]string{ 546 "nginx.ingress.kubernetes.io/rewrite-target": "/", 547 "controller.juju.is/id": "deadbeef-1bad-500d-9000-4b1d0d06f00d", 548 }, 549 }, 550 Spec: networkingv1.IngressSpec{ 551 Rules: []networkingv1.IngressRule{ingress1Rule1}, 552 }, 553 } 554 } 555 ingress := getIngress() 556 existingNonJujuManagedIngress := getIngress() 557 existingNonJujuManagedIngress.SetLabels(map[string]string{}) 558 s.assertIngressResources( 559 c, ingressResources, `creating or updating ingress resources: ensuring ingress "test-ingress" with version "v1": existing ingress "test-ingress" found which does not belong to "app-name"`, 560 s.mockIngressV1.EXPECT().Create(gomock.Any(), ingress, gomock.Any()).Return(nil, s.k8sAlreadyExistsError()), 561 s.mockIngressV1.EXPECT().Get(gomock.Any(), "test-ingress", metav1.GetOptions{}).Return(existingNonJujuManagedIngress, nil), 562 ) 563 } 564 565 func (s *K8sBrokerSuite) TestEnsureServiceIngressResourcesUpdateConflictWithIngressCreatedByJujuExpose(c *gc.C) { 566 ctrl := s.setupController(c) 567 defer ctrl.Finish() 568 569 gomock.InOrder( 570 s.mockDiscovery.EXPECT().ServerVersion().Return(&k8sversion.Info{ 571 Major: "1", Minor: "22", 572 }, nil), 573 ) 574 575 ingress1Rule1 := networkingv1beta1.IngressRule{ 576 IngressRuleValue: networkingv1beta1.IngressRuleValue{ 577 HTTP: &networkingv1beta1.HTTPIngressRuleValue{ 578 Paths: []networkingv1beta1.HTTPIngressPath{ 579 { 580 Path: "/testpath", 581 Backend: networkingv1beta1.IngressBackend{ 582 ServiceName: "test", 583 ServicePort: intstr.IntOrString{IntVal: 80}, 584 }, 585 }, 586 }, 587 }, 588 }, 589 } 590 ingress1 := k8sspecs.K8sIngress{ 591 Meta: k8sspecs.Meta{ 592 Name: "app-name", 593 Labels: map[string]string{ 594 "foo": "bar", 595 }, 596 Annotations: map[string]string{ 597 "nginx.ingress.kubernetes.io/rewrite-target": "/", 598 }, 599 }, 600 Spec: k8sspecs.K8sIngressSpec{ 601 Version: k8sspecs.K8sIngressV1Beta1, 602 SpecV1Beta1: networkingv1beta1.IngressSpec{ 603 Rules: []networkingv1beta1.IngressRule{ingress1Rule1}, 604 }, 605 }, 606 } 607 608 ingressResources := []k8sspecs.K8sIngress{ingress1} 609 s.assertIngressResources( 610 c, ingressResources, `creating or updating ingress resources: ingress name "app-name" is reserved for juju expose not valid`, 611 ) 612 } 613 614 func (s *K8sBrokerSuite) TestEnsureServiceIngressResourcesV1Beta1OnV1Cluster(c *gc.C) { 615 ctrl := s.setupController(c) 616 defer ctrl.Finish() 617 618 gomock.InOrder( 619 s.mockDiscovery.EXPECT().ServerVersion().Return(&k8sversion.Info{ 620 Major: "1", Minor: "19", 621 }, nil), 622 ) 623 624 ingress1Rule1 := networkingv1beta1.IngressRule{ 625 IngressRuleValue: networkingv1beta1.IngressRuleValue{ 626 HTTP: &networkingv1beta1.HTTPIngressRuleValue{ 627 Paths: []networkingv1beta1.HTTPIngressPath{ 628 { 629 Path: "/testpath", 630 Backend: networkingv1beta1.IngressBackend{ 631 ServiceName: "test", 632 ServicePort: intstr.IntOrString{IntVal: 80}, 633 }, 634 }, 635 }, 636 }, 637 }, 638 } 639 ingress1 := k8sspecs.K8sIngress{ 640 Meta: k8sspecs.Meta{ 641 Name: "test-ingress", 642 }, 643 Spec: k8sspecs.K8sIngressSpec{ 644 Version: k8sspecs.K8sIngressV1Beta1, 645 SpecV1Beta1: networkingv1beta1.IngressSpec{ 646 Rules: []networkingv1beta1.IngressRule{ingress1Rule1}, 647 }, 648 }, 649 } 650 651 pathType := networkingv1.PathTypeImplementationSpecific 652 ingressResources := []k8sspecs.K8sIngress{ingress1} 653 ingress := &networkingv1.Ingress{ 654 ObjectMeta: metav1.ObjectMeta{ 655 Name: "test-ingress", 656 Labels: map[string]string{"app.kubernetes.io/managed-by": "juju", "app.kubernetes.io/name": "app-name"}, 657 Annotations: map[string]string{ 658 "controller.juju.is/id": "deadbeef-1bad-500d-9000-4b1d0d06f00d", 659 }, 660 }, 661 Spec: networkingv1.IngressSpec{ 662 Rules: []networkingv1.IngressRule{{ 663 IngressRuleValue: networkingv1.IngressRuleValue{ 664 HTTP: &networkingv1.HTTPIngressRuleValue{ 665 Paths: []networkingv1.HTTPIngressPath{ 666 { 667 Path: "/testpath", 668 PathType: &pathType, 669 Backend: networkingv1.IngressBackend{ 670 Service: &networkingv1.IngressServiceBackend{ 671 Name: "test", 672 Port: networkingv1.ServiceBackendPort{Number: 80}, 673 }, 674 }, 675 }, 676 }, 677 }, 678 }, 679 }}, 680 }, 681 } 682 s.assertIngressResources( 683 c, ingressResources, "", 684 s.mockIngressV1.EXPECT().Create(gomock.Any(), ingress, metav1.CreateOptions{}).Return(ingress, nil), 685 ) 686 } 687 688 func (s *K8sBrokerSuite) TestEnsureServiceIngressResourcesV1OnV1BetaCluster(c *gc.C) { 689 ctrl := s.setupController(c) 690 defer ctrl.Finish() 691 692 gomock.InOrder( 693 s.mockDiscovery.EXPECT().ServerVersion().Return(&k8sversion.Info{ 694 Major: "1", Minor: "18", 695 }, nil), 696 ) 697 698 ingress1Rule1 := networkingv1.IngressRule{ 699 IngressRuleValue: networkingv1.IngressRuleValue{ 700 HTTP: &networkingv1.HTTPIngressRuleValue{ 701 Paths: []networkingv1.HTTPIngressPath{ 702 { 703 Path: "/testpath", 704 Backend: networkingv1.IngressBackend{ 705 Resource: &core.TypedLocalObjectReference{ 706 APIGroup: pointer.StringPtr("k8s.example.com"), 707 Kind: "StorageBucket", 708 Name: "icon-assets", 709 }, 710 }, 711 }, 712 }, 713 }, 714 }, 715 } 716 ingress1 := k8sspecs.K8sIngress{ 717 Meta: k8sspecs.Meta{ 718 Name: "test-ingress", 719 }, 720 Spec: k8sspecs.K8sIngressSpec{ 721 Version: k8sspecs.K8sIngressV1, 722 SpecV1: networkingv1.IngressSpec{ 723 Rules: []networkingv1.IngressRule{ingress1Rule1}, 724 }, 725 }, 726 } 727 728 ingressResources := []k8sspecs.K8sIngress{ingress1} 729 ingress := &networkingv1beta1.Ingress{ 730 ObjectMeta: metav1.ObjectMeta{ 731 Name: "test-ingress", 732 Labels: map[string]string{"app.kubernetes.io/managed-by": "juju", "app.kubernetes.io/name": "app-name"}, 733 Annotations: map[string]string{ 734 "controller.juju.is/id": "deadbeef-1bad-500d-9000-4b1d0d06f00d", 735 }, 736 }, 737 Spec: networkingv1beta1.IngressSpec{ 738 Rules: []networkingv1beta1.IngressRule{{ 739 IngressRuleValue: networkingv1beta1.IngressRuleValue{ 740 HTTP: &networkingv1beta1.HTTPIngressRuleValue{ 741 Paths: []networkingv1beta1.HTTPIngressPath{ 742 { 743 Path: "/testpath", 744 Backend: networkingv1beta1.IngressBackend{ 745 Resource: &core.TypedLocalObjectReference{ 746 APIGroup: pointer.StringPtr("k8s.example.com"), 747 Kind: "StorageBucket", 748 Name: "icon-assets", 749 }, 750 }, 751 }, 752 }, 753 }, 754 }, 755 }}, 756 }, 757 } 758 s.assertIngressResources( 759 c, ingressResources, "", 760 s.mockIngressV1Beta1.EXPECT().Create(gomock.Any(), ingress, metav1.CreateOptions{}).Return(ingress, nil), 761 ) 762 }