github.com/oam-dev/kubevela@v1.9.11/pkg/velaql/providers/query/handler_test.go (about) 1 /* 2 Copyright 2021. The KubeVela 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 query 18 19 import ( 20 "context" 21 "fmt" 22 "os" 23 "time" 24 25 . "github.com/onsi/ginkgo/v2" 26 . "github.com/onsi/gomega" 27 v1 "k8s.io/api/apps/v1" 28 corev1 "k8s.io/api/core/v1" 29 networkv1 "k8s.io/api/networking/v1" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 32 "k8s.io/apimachinery/pkg/runtime/schema" 33 "k8s.io/apimachinery/pkg/util/intstr" 34 "k8s.io/klog/v2" 35 "sigs.k8s.io/controller-runtime/pkg/client" 36 "sigs.k8s.io/yaml" 37 38 monitorContext "github.com/kubevela/pkg/monitor/context" 39 "github.com/kubevela/workflow/pkg/cue/model/value" 40 "github.com/kubevela/workflow/pkg/providers" 41 42 "github.com/oam-dev/kubevela/apis/core.oam.dev/common" 43 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" 44 helmapi "github.com/oam-dev/kubevela/pkg/appfile/helm/flux2apis" 45 "github.com/oam-dev/kubevela/pkg/oam" 46 "github.com/oam-dev/kubevela/pkg/oam/util" 47 verrors "github.com/oam-dev/kubevela/pkg/utils/errors" 48 querytypes "github.com/oam-dev/kubevela/pkg/velaql/providers/query/types" 49 ) 50 51 type AppResourcesList struct { 52 List []Resource `json:"list,omitempty"` 53 App interface{} `json:"app"` 54 Err string `json:"err,omitempty"` 55 } 56 57 type PodList struct { 58 List []*unstructured.Unstructured `json:"list"` 59 Value interface{} `json:"value"` 60 Cluster string `json:"cluster"` 61 } 62 63 var _ = Describe("Test Query Provider", func() { 64 var baseDeploy *v1.Deployment 65 var baseService *corev1.Service 66 var basePod *corev1.Pod 67 68 BeforeEach(func() { 69 baseDeploy = new(v1.Deployment) 70 Expect(yaml.Unmarshal([]byte(deploymentYaml), baseDeploy)).Should(BeNil()) 71 72 baseService = new(corev1.Service) 73 Expect(yaml.Unmarshal([]byte(serviceYaml), baseService)).Should(BeNil()) 74 75 basePod = new(corev1.Pod) 76 Expect(yaml.Unmarshal([]byte(podYaml), basePod)).Should(BeNil()) 77 }) 78 79 Context("Test ListResourcesInApp", func() { 80 It("Test list latest resources created by application", func() { 81 namespace := "test" 82 ns := corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}} 83 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 84 85 app := v1beta1.Application{ 86 ObjectMeta: metav1.ObjectMeta{ 87 Name: "test", 88 Namespace: "test", 89 Annotations: map[string]string{ 90 oam.AnnotationKubeVelaVersion: "v1.3.1", 91 oam.AnnotationPublishVersion: "v1", 92 }, 93 }, 94 Spec: v1beta1.ApplicationSpec{ 95 Components: []common.ApplicationComponent{{ 96 Name: "web", 97 Type: "webservice", 98 Properties: util.Object2RawExtension(map[string]string{ 99 "image": "busybox", 100 }), 101 Traits: []common.ApplicationTrait{{ 102 Type: "expose", 103 Properties: util.Object2RawExtension(map[string]interface{}{ 104 "ports": []int{8000}, 105 }), 106 }}, 107 }}, 108 }, 109 } 110 111 Expect(k8sClient.Create(ctx, &app)).Should(BeNil()) 112 oldApp := new(v1beta1.Application) 113 Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(&app), oldApp)).Should(BeNil()) 114 oldApp.Status.LatestRevision = &common.Revision{ 115 Revision: 1, 116 } 117 oldApp.Status.AppliedResources = []common.ClusterObjectReference{{ 118 Cluster: "", 119 Creator: "workflow", 120 ObjectReference: corev1.ObjectReference{ 121 APIVersion: "v1", 122 Kind: "Service", 123 Namespace: namespace, 124 Name: "web", 125 }, 126 }, { 127 Cluster: "", 128 Creator: "workflow", 129 ObjectReference: corev1.ObjectReference{ 130 APIVersion: "apps/v1", 131 Kind: "Deployment", 132 Namespace: namespace, 133 Name: "web", 134 }, 135 }} 136 Eventually(func() error { 137 err := k8sClient.Status().Update(ctx, oldApp) 138 if err != nil { 139 return err 140 } 141 return nil 142 }, 300*time.Microsecond, 3*time.Second).Should(BeNil()) 143 144 appDeploy := baseDeploy.DeepCopy() 145 appDeploy.SetName("web") 146 appDeploy.SetNamespace(namespace) 147 appDeploy.SetLabels(map[string]string{ 148 oam.LabelAppComponent: "web", 149 oam.LabelAppRevision: "test-v1", 150 }) 151 Expect(k8sClient.Create(ctx, appDeploy)).Should(BeNil()) 152 153 appService := baseService.DeepCopy() 154 appService.SetName("web") 155 appService.SetNamespace(namespace) 156 appService.SetLabels(map[string]string{ 157 oam.LabelAppComponent: "web", 158 oam.LabelAppRevision: "test-v1", 159 }) 160 Expect(k8sClient.Create(ctx, appService)).Should(BeNil()) 161 162 rt := &v1beta1.ResourceTracker{ 163 ObjectMeta: metav1.ObjectMeta{ 164 Name: fmt.Sprintf("%s-v1-%s", oldApp.Name, oldApp.Namespace), 165 Labels: map[string]string{ 166 oam.LabelAppName: oldApp.Name, 167 oam.LabelAppNamespace: oldApp.Namespace, 168 }, 169 Annotations: map[string]string{ 170 oam.AnnotationPublishVersion: "v1", 171 }, 172 }, 173 Spec: v1beta1.ResourceTrackerSpec{ 174 ManagedResources: []v1beta1.ManagedResource{ 175 { 176 ClusterObjectReference: common.ClusterObjectReference{ 177 Cluster: "", 178 ObjectReference: corev1.ObjectReference{ 179 APIVersion: "v1", 180 Kind: "Service", 181 Namespace: namespace, 182 Name: "web", 183 }, 184 }, 185 OAMObjectReference: common.OAMObjectReference{ 186 Component: "web", 187 }, 188 }, 189 { 190 ClusterObjectReference: common.ClusterObjectReference{ 191 Cluster: "", 192 ObjectReference: corev1.ObjectReference{ 193 APIVersion: "apps/v1", 194 Kind: "Deployment", 195 Namespace: namespace, 196 Name: "web", 197 }, 198 }, 199 OAMObjectReference: common.OAMObjectReference{ 200 Component: "web", 201 }, 202 }, 203 }, 204 Type: v1beta1.ResourceTrackerTypeVersioned, 205 }, 206 } 207 Expect(k8sClient.Create(ctx, rt)).Should(BeNil()) 208 209 prd := provider{cli: k8sClient} 210 opt := `app: { 211 name: "test" 212 namespace: "test" 213 filter: { 214 cluster: "", 215 clusterNamespace: "test", 216 components: ["web"] 217 } 218 }` 219 v, err := value.NewValue(opt, nil, "") 220 Expect(err).Should(BeNil()) 221 logCtx := monitorContext.NewTraceContext(ctx, "") 222 Expect(prd.ListResourcesInApp(logCtx, nil, v, nil)).Should(BeNil()) 223 224 appResList := new(AppResourcesList) 225 Expect(v.UnmarshalTo(appResList)).Should(BeNil()) 226 if appResList.Err != "" { 227 klog.Error(appResList.Err) 228 } 229 230 Expect(len(appResList.List)).Should(Equal(2)) 231 232 Expect(appResList.List[0].Object.GroupVersionKind()).Should(Equal(oldApp.Status.AppliedResources[0].GroupVersionKind())) 233 Expect(appResList.List[1].Object.GroupVersionKind()).Should(Equal(oldApp.Status.AppliedResources[1].GroupVersionKind())) 234 235 updateApp := new(v1beta1.Application) 236 Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(&app), updateApp)).Should(BeNil()) 237 238 updateApp.ObjectMeta.Annotations = map[string]string{ 239 oam.AnnotationKubeVelaVersion: "v1.1.0", 240 } 241 Expect(k8sClient.Update(ctx, updateApp)).Should(BeNil()) 242 newValue, err := value.NewValue(opt, nil, "") 243 Expect(err).Should(BeNil()) 244 Expect(prd.ListResourcesInApp(logCtx, nil, newValue, nil)).Should(BeNil()) 245 newAppResList := new(AppResourcesList) 246 Expect(v.UnmarshalTo(newAppResList)).Should(BeNil()) 247 Expect(len(newAppResList.List)).Should(Equal(2)) 248 Expect(newAppResList.List[0].Object.GroupVersionKind()).Should(Equal(updateApp.Status.AppliedResources[0].GroupVersionKind())) 249 Expect(newAppResList.List[1].Object.GroupVersionKind()).Should(Equal(updateApp.Status.AppliedResources[1].GroupVersionKind())) 250 }) 251 252 It("Test list resource with incomplete parameter", func() { 253 optWithoutApp := "" 254 prd := provider{cli: k8sClient} 255 newV, err := value.NewValue(optWithoutApp, nil, "") 256 Expect(err).Should(BeNil()) 257 logCtx := monitorContext.NewTraceContext(ctx, "") 258 err = prd.ListResourcesInApp(logCtx, nil, newV, nil) 259 Expect(err).ShouldNot(BeNil()) 260 Expect(verrors.IsCuePathNotFound(err)).Should(BeTrue()) 261 }) 262 }) 263 264 Context("Test ListAppliedResources", func() { 265 It("Test list applied resources created by application", func() { 266 // create test app 267 app := v1beta1.Application{ 268 ObjectMeta: metav1.ObjectMeta{ 269 Name: "test-applied", 270 Namespace: "default", 271 }, 272 Spec: v1beta1.ApplicationSpec{ 273 Components: []common.ApplicationComponent{{ 274 Name: "web", 275 Type: "webservice", 276 Properties: util.Object2RawExtension(map[string]string{ 277 "image": "busybox", 278 }), 279 Traits: []common.ApplicationTrait{{ 280 Type: "expose", 281 Properties: util.Object2RawExtension(map[string]interface{}{ 282 "ports": []int{8000}, 283 }), 284 }}, 285 }}, 286 }, 287 } 288 Expect(k8sClient.Create(ctx, &app)).Should(BeNil()) 289 // create RT 290 rt := &v1beta1.ResourceTracker{ 291 ObjectMeta: metav1.ObjectMeta{ 292 Name: "test-applied", 293 Namespace: "default", 294 Labels: map[string]string{ 295 oam.LabelAppName: app.Name, 296 oam.LabelAppNamespace: app.Namespace, 297 }, 298 }, 299 Spec: v1beta1.ResourceTrackerSpec{ 300 Type: v1beta1.ResourceTrackerTypeRoot, 301 ManagedResources: []v1beta1.ManagedResource{ 302 { 303 ClusterObjectReference: common.ClusterObjectReference{ 304 Cluster: "", 305 ObjectReference: corev1.ObjectReference{ 306 Kind: "Deployment", 307 APIVersion: "apps/v1", 308 Namespace: "default", 309 Name: "web", 310 }, 311 }, 312 OAMObjectReference: common.OAMObjectReference{ 313 Component: "web", 314 }, 315 }, 316 { 317 ClusterObjectReference: common.ClusterObjectReference{ 318 Cluster: "", 319 ObjectReference: corev1.ObjectReference{ 320 Kind: "Service", 321 APIVersion: "v1", 322 Namespace: "default", 323 Name: "web", 324 }, 325 }, 326 OAMObjectReference: common.OAMObjectReference{ 327 Trait: "expose", 328 Component: "web", 329 }, 330 }, 331 }, 332 }, 333 } 334 err := k8sClient.Create(context.TODO(), rt) 335 Expect(err).Should(BeNil()) 336 prd := provider{cli: k8sClient} 337 opt := `app: { 338 name: "test-applied" 339 namespace: "default" 340 filter: { 341 components: ["web"] 342 } 343 }` 344 v, err := value.NewValue(opt, nil, "") 345 Expect(err).Should(BeNil()) 346 logCtx := monitorContext.NewTraceContext(ctx, "") 347 Expect(prd.ListAppliedResources(logCtx, nil, v, nil)).Should(BeNil()) 348 type Res struct { 349 List []v1beta1.ManagedResource `json:"list"` 350 } 351 var res Res 352 err = v.UnmarshalTo(&res) 353 Expect(err).Should(BeNil()) 354 Expect(len(res.List)).Should(Equal(2)) 355 356 By("test filter with the apiVersion and kind") 357 optWithVersion := `app: { 358 name: "test-applied" 359 namespace: "default" 360 filter: { 361 components: ["web"] 362 apiVersion: "apps/v1" 363 } 364 }` 365 valueWithVersion, err := value.NewValue(optWithVersion, nil, "") 366 Expect(err).Should(BeNil()) 367 Expect(prd.ListAppliedResources(logCtx, nil, valueWithVersion, nil)).Should(BeNil()) 368 var res2 Res 369 err = valueWithVersion.UnmarshalTo(&res2) 370 Expect(err).Should(BeNil()) 371 Expect(len(res2.List)).Should(Equal(1)) 372 Expect(res2.List[0].Kind).Should(Equal("Deployment")) 373 374 optWithKind := `app: { 375 name: "test-applied" 376 namespace: "default" 377 filter: { 378 components: ["web"] 379 kind: "Service" 380 } 381 }` 382 valueWithKind, err := value.NewValue(optWithKind, nil, "") 383 Expect(err).Should(BeNil()) 384 Expect(prd.ListAppliedResources(logCtx, nil, valueWithKind, nil)).Should(BeNil()) 385 var res3 Res 386 err = valueWithKind.UnmarshalTo(&res3) 387 Expect(err).Should(BeNil()) 388 Expect(len(res3.List)).Should(Equal(1)) 389 Expect(res3.List[0].Kind).Should(Equal("Service")) 390 }) 391 }) 392 393 Context("Test search event from k8s object", func() { 394 It("Test search event with incomplete parameter", func() { 395 emptyOpt := "" 396 prd := provider{cli: k8sClient} 397 v, err := value.NewValue(emptyOpt, nil, "") 398 Expect(err).Should(BeNil()) 399 logCtx := monitorContext.NewTraceContext(ctx, "") 400 err = prd.SearchEvents(logCtx, nil, v, nil) 401 Expect(err).ShouldNot(BeNil()) 402 Expect(verrors.IsCuePathNotFound(err)).Should(BeTrue()) 403 404 optWithoutCluster := `value: {}` 405 v, err = value.NewValue(optWithoutCluster, nil, "") 406 Expect(err).Should(BeNil()) 407 err = prd.SearchEvents(logCtx, nil, v, nil) 408 Expect(err).ShouldNot(BeNil()) 409 Expect(verrors.IsCuePathNotFound(err)).Should(BeTrue()) 410 411 optWithWrongValue := `value: {} 412 cluster: "test"` 413 v, err = value.NewValue(optWithWrongValue, nil, "") 414 Expect(err).Should(BeNil()) 415 err = prd.SearchEvents(logCtx, nil, v, nil) 416 Expect(err).ShouldNot(BeNil()) 417 }) 418 }) 419 420 Context("Test CollectLogsInPod", func() { 421 It("Test CollectLogsInPod with specified container", func() { 422 prd := provider{cli: k8sClient, cfg: cfg} 423 pod := &corev1.Pod{ 424 ObjectMeta: metav1.ObjectMeta{Name: "hello-world", Namespace: "default"}, 425 Spec: corev1.PodSpec{ 426 Containers: []corev1.Container{{Name: "main", Image: "busybox"}}, 427 }} 428 Expect(k8sClient.Create(ctx, pod)).Should(Succeed()) 429 logCtx := monitorContext.NewTraceContext(ctx, "") 430 431 v, err := value.NewValue(``, nil, "") 432 Expect(err).Should(Succeed()) 433 err = prd.CollectLogsInPod(logCtx, nil, v, nil) 434 Expect(err).ShouldNot(BeNil()) 435 Expect(verrors.IsCuePathNotFound(err)).Should(BeTrue()) 436 437 v, err = value.NewValue(`cluster: "local"`, nil, "") 438 Expect(err).Should(Succeed()) 439 err = prd.CollectLogsInPod(logCtx, nil, v, nil) 440 Expect(err).ShouldNot(BeNil()) 441 Expect(verrors.IsCuePathNotFound(err)).Should(BeTrue()) 442 443 v, err = value.NewValue(`cluster: "local" 444 namespace: "default"`, nil, "") 445 Expect(err).Should(Succeed()) 446 err = prd.CollectLogsInPod(logCtx, nil, v, nil) 447 Expect(err).ShouldNot(BeNil()) 448 Expect(verrors.IsCuePathNotFound(err)).Should(BeTrue()) 449 450 v, err = value.NewValue(`cluster: "local" 451 namespace: "default" 452 pod: "hello-world"`, nil, "") 453 Expect(err).Should(Succeed()) 454 err = prd.CollectLogsInPod(logCtx, nil, v, nil) 455 Expect(err).ShouldNot(BeNil()) 456 Expect(verrors.IsCuePathNotFound(err)).Should(BeTrue()) 457 458 v, err = value.NewValue(`cluster: "local" 459 namespace: "default" 460 pod: "hello-world" 461 options: { 462 container: 1 463 }`, nil, "") 464 Expect(err).Should(Succeed()) 465 err = prd.CollectLogsInPod(logCtx, nil, v, nil) 466 Expect(err).ShouldNot(BeNil()) 467 Expect(err.Error()).Should(ContainSubstring("invalid log options content")) 468 469 v, err = value.NewValue(`cluster: "local" 470 namespace: "default" 471 pod: "hello-world" 472 options: { 473 container: "main" 474 previous: true 475 sinceSeconds: 100 476 tailLines: 50 477 }`, nil, "") 478 Expect(err).Should(Succeed()) 479 Expect(prd.CollectLogsInPod(logCtx, nil, v, nil)).Should(Succeed()) 480 _, err = v.GetString("outputs", "logs") 481 Expect(err).Should(Succeed()) 482 }) 483 }) 484 485 It("Test install provider", func() { 486 p := providers.NewProviders() 487 Install(p, k8sClient, cfg) 488 h, ok := p.GetHandler("query", "listResourcesInApp") 489 Expect(h).ShouldNot(BeNil()) 490 Expect(ok).Should(Equal(true)) 491 h, ok = p.GetHandler("query", "collectResources") 492 Expect(h).ShouldNot(BeNil()) 493 Expect(ok).Should(Equal(true)) 494 l, ok := p.GetHandler("query", "listAppliedResources") 495 Expect(l).ShouldNot(BeNil()) 496 Expect(ok).Should(Equal(true)) 497 h, ok = p.GetHandler("query", "searchEvents") 498 Expect(ok).Should(Equal(true)) 499 Expect(h).ShouldNot(BeNil()) 500 h, ok = p.GetHandler("query", "collectLogsInPod") 501 Expect(ok).Should(Equal(true)) 502 Expect(h).ShouldNot(BeNil()) 503 h, ok = p.GetHandler("query", "collectServiceEndpoints") 504 Expect(ok).Should(Equal(true)) 505 Expect(h).ShouldNot(BeNil()) 506 }) 507 508 It("Test generator service endpoints", func() { 509 appsts := common.AppStatus{ 510 AppliedResources: []common.ClusterObjectReference{ 511 { 512 Cluster: "", 513 ObjectReference: corev1.ObjectReference{ 514 Kind: "Ingress", 515 Namespace: "default", 516 Name: "ingress-http", 517 APIVersion: "networking.k8s.io/v1", 518 }, 519 }, 520 { 521 Cluster: "", 522 ObjectReference: corev1.ObjectReference{ 523 Kind: "Ingress", 524 Namespace: "default", 525 Name: "ingress-https", 526 APIVersion: "networking.k8s.io/v1", 527 }, 528 }, 529 { 530 Cluster: "", 531 ObjectReference: corev1.ObjectReference{ 532 Kind: "Ingress", 533 Namespace: "default", 534 Name: "ingress-paths", 535 APIVersion: "networking.k8s.io/v1", 536 }, 537 }, 538 { 539 Cluster: "", 540 ObjectReference: corev1.ObjectReference{ 541 APIVersion: "v1", 542 Kind: "Service", 543 Namespace: "default", 544 Name: "nodeport", 545 }, 546 }, 547 { 548 Cluster: "", 549 ObjectReference: corev1.ObjectReference{ 550 APIVersion: "v1", 551 Kind: "Service", 552 Namespace: "default", 553 Name: "loadbalancer", 554 }, 555 }, 556 { 557 Cluster: "", 558 ObjectReference: corev1.ObjectReference{ 559 APIVersion: "helm.toolkit.fluxcd.io/v2beta1", 560 Kind: helmapi.HelmReleaseGVK.Kind, 561 Namespace: "default", 562 Name: "helm-release", 563 }, 564 }, 565 { 566 Cluster: "", 567 ObjectReference: corev1.ObjectReference{ 568 APIVersion: "machinelearning.seldon.io/v1", 569 Kind: "SeldonDeployment", 570 Namespace: "default", 571 Name: "sdep", 572 }, 573 }, 574 { 575 Cluster: "", 576 ObjectReference: corev1.ObjectReference{ 577 APIVersion: "gateway.networking.k8s.io/v1beta1", 578 Kind: "HTTPRoute", 579 Namespace: "default", 580 Name: "http-test-route", 581 }, 582 }, 583 { 584 Cluster: "", 585 ObjectReference: corev1.ObjectReference{ 586 APIVersion: "gateway.networking.k8s.io/v1beta1", 587 Kind: "HTTPRoute", 588 Namespace: "default", 589 Name: "velaux-ssl", 590 }, 591 }, 592 }, 593 } 594 testApp := &v1beta1.Application{ 595 ObjectMeta: metav1.ObjectMeta{ 596 Name: "endpoints-app", 597 Namespace: "default", 598 }, 599 Spec: v1beta1.ApplicationSpec{ 600 Components: []common.ApplicationComponent{ 601 { 602 Name: "endpoints-test", 603 Type: "webservice", 604 }, 605 }, 606 }, 607 Status: appsts, 608 } 609 err := k8sClient.Create(context.TODO(), testApp) 610 Expect(err).Should(BeNil()) 611 612 var gtapp v1beta1.Application 613 Expect(k8sClient.Get(context.TODO(), client.ObjectKey{Name: "endpoints-app", Namespace: "default"}, >app)).Should(BeNil()) 614 gtapp.Status = appsts 615 Expect(k8sClient.Status().Update(ctx, >app)).Should(BeNil()) 616 var mr []v1beta1.ManagedResource 617 for _, ar := range appsts.AppliedResources { 618 smr := v1beta1.ManagedResource{ 619 ClusterObjectReference: ar, 620 } 621 smr.Component = "endpoints-test" 622 mr = append(mr, smr) 623 } 624 rt := &v1beta1.ResourceTracker{ 625 ObjectMeta: metav1.ObjectMeta{ 626 Name: "endpoints-app", 627 Namespace: "default", 628 Labels: map[string]string{ 629 oam.LabelAppName: testApp.Name, 630 oam.LabelAppNamespace: testApp.Namespace, 631 }, 632 }, 633 Spec: v1beta1.ResourceTrackerSpec{ 634 Type: v1beta1.ResourceTrackerTypeRoot, 635 ManagedResources: mr, 636 }, 637 } 638 err = k8sClient.Create(context.TODO(), rt) 639 Expect(err).Should(BeNil()) 640 641 helmRelease := &unstructured.Unstructured{} 642 helmRelease.SetName("helm-release") 643 helmRelease.SetNamespace("default") 644 helmRelease.SetGroupVersionKind(helmapi.HelmReleaseGVK) 645 err = k8sClient.Create(context.TODO(), helmRelease) 646 Expect(err).Should(BeNil()) 647 648 testServiceList := []map[string]interface{}{ 649 { 650 "name": "clusterip", 651 "ports": []corev1.ServicePort{ 652 {Port: 80, TargetPort: intstr.FromInt(80), Name: "80port"}, 653 {Port: 81, TargetPort: intstr.FromInt(81), Name: "81port"}, 654 }, 655 "type": corev1.ServiceTypeClusterIP, 656 }, 657 { 658 "name": "nodeport", 659 "ports": []corev1.ServicePort{ 660 {Port: 80, TargetPort: intstr.FromInt(80), NodePort: 30229}, 661 }, 662 "type": corev1.ServiceTypeNodePort, 663 }, 664 { 665 "name": "loadbalancer", 666 "ports": []corev1.ServicePort{ 667 {Port: 80, TargetPort: intstr.FromInt(80), Name: "80port", NodePort: 30080}, 668 {Port: 81, TargetPort: intstr.FromInt(81), Name: "81port", NodePort: 30081}, 669 }, 670 "type": corev1.ServiceTypeLoadBalancer, 671 "status": corev1.ServiceStatus{ 672 LoadBalancer: corev1.LoadBalancerStatus{ 673 Ingress: []corev1.LoadBalancerIngress{ 674 { 675 IP: "10.10.10.10", 676 }, 677 { 678 Hostname: "text.example.com", 679 }, 680 }, 681 }, 682 }, 683 }, 684 { 685 "name": "helm1", 686 "ports": []corev1.ServicePort{ 687 {Port: 80, NodePort: 30002, TargetPort: intstr.FromInt(80)}, 688 }, 689 "type": corev1.ServiceTypeNodePort, 690 "labels": map[string]string{ 691 "helm.toolkit.fluxcd.io/name": "helm-release", 692 "helm.toolkit.fluxcd.io/namespace": "default", 693 }, 694 }, 695 { 696 "name": "seldon-ambassador", 697 "ports": []corev1.ServicePort{ 698 {Port: 80, TargetPort: intstr.FromInt(80), Name: "80port", NodePort: 30011}, 699 }, 700 "type": corev1.ServiceTypeLoadBalancer, 701 "status": corev1.ServiceStatus{ 702 LoadBalancer: corev1.LoadBalancerStatus{ 703 Ingress: []corev1.LoadBalancerIngress{ 704 { 705 IP: "1.1.1.1", 706 }, 707 }, 708 }, 709 }, 710 }, 711 } 712 err = k8sClient.Create(context.TODO(), &corev1.Namespace{ 713 ObjectMeta: metav1.ObjectMeta{ 714 Name: "vela-system", 715 }, 716 }) 717 Expect(err).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{})) 718 for _, s := range testServiceList { 719 ns := "default" 720 if s["namespace"] != nil { 721 ns = s["namespace"].(string) 722 } 723 service := &corev1.Service{ 724 ObjectMeta: metav1.ObjectMeta{ 725 Name: s["name"].(string), 726 Namespace: ns, 727 }, 728 Spec: corev1.ServiceSpec{ 729 Ports: s["ports"].([]corev1.ServicePort), 730 Type: s["type"].(corev1.ServiceType), 731 }, 732 } 733 734 if s["labels"] != nil { 735 service.Labels = s["labels"].(map[string]string) 736 } 737 err := k8sClient.Create(context.TODO(), service) 738 Expect(err).Should(BeNil()) 739 if s["status"] != nil { 740 service.Status = s["status"].(corev1.ServiceStatus) 741 err := k8sClient.Status().Update(context.TODO(), service) 742 Expect(err).Should(BeNil()) 743 } 744 } 745 746 var prefixbeta = networkv1.PathTypePrefix 747 testIngress := []client.Object{ 748 &networkv1.Ingress{ 749 ObjectMeta: metav1.ObjectMeta{ 750 Name: "ingress-http", 751 Namespace: "default", 752 }, 753 Spec: networkv1.IngressSpec{ 754 Rules: []networkv1.IngressRule{ 755 { 756 Host: "ingress.domain", 757 IngressRuleValue: networkv1.IngressRuleValue{ 758 HTTP: &networkv1.HTTPIngressRuleValue{ 759 Paths: []networkv1.HTTPIngressPath{ 760 { 761 Path: "/", 762 Backend: networkv1.IngressBackend{ 763 Service: &networkv1.IngressServiceBackend{ 764 Name: "clusterip", 765 Port: networkv1.ServiceBackendPort{ 766 Number: 80, 767 }, 768 }, 769 }, 770 PathType: &prefixbeta, 771 }, 772 }, 773 }, 774 }, 775 }, 776 }, 777 }, 778 }, 779 &networkv1.Ingress{ 780 ObjectMeta: metav1.ObjectMeta{ 781 Name: "ingress-https", 782 Namespace: "default", 783 }, 784 Spec: networkv1.IngressSpec{ 785 TLS: []networkv1.IngressTLS{ 786 { 787 SecretName: "https-secret", 788 }, 789 }, 790 Rules: []networkv1.IngressRule{ 791 { 792 Host: "ingress.domain.https", 793 IngressRuleValue: networkv1.IngressRuleValue{ 794 HTTP: &networkv1.HTTPIngressRuleValue{ 795 Paths: []networkv1.HTTPIngressPath{ 796 { 797 Path: "/", 798 Backend: networkv1.IngressBackend{ 799 Service: &networkv1.IngressServiceBackend{ 800 Name: "clusterip", 801 Port: networkv1.ServiceBackendPort{ 802 Number: 80, 803 }, 804 }, 805 }, 806 PathType: &prefixbeta, 807 }, 808 }, 809 }, 810 }, 811 }, 812 }, 813 }, 814 }, 815 &networkv1.Ingress{ 816 ObjectMeta: metav1.ObjectMeta{ 817 Name: "ingress-paths", 818 Namespace: "default", 819 }, 820 Spec: networkv1.IngressSpec{ 821 TLS: []networkv1.IngressTLS{ 822 { 823 SecretName: "https-secret", 824 }, 825 }, 826 Rules: []networkv1.IngressRule{ 827 { 828 Host: "ingress.domain.path", 829 IngressRuleValue: networkv1.IngressRuleValue{ 830 HTTP: &networkv1.HTTPIngressRuleValue{ 831 Paths: []networkv1.HTTPIngressPath{ 832 { 833 Path: "/test", 834 Backend: networkv1.IngressBackend{ 835 Service: &networkv1.IngressServiceBackend{ 836 Name: "clusterip", 837 Port: networkv1.ServiceBackendPort{ 838 Number: 80, 839 }, 840 }, 841 }, 842 PathType: &prefixbeta, 843 }, 844 { 845 Path: "/test2", 846 Backend: networkv1.IngressBackend{ 847 Service: &networkv1.IngressServiceBackend{ 848 Name: "clusterip", 849 Port: networkv1.ServiceBackendPort{ 850 Number: 80, 851 }, 852 }, 853 }, 854 PathType: &prefixbeta, 855 }, 856 }, 857 }, 858 }, 859 }, 860 }, 861 }, 862 }, 863 &networkv1.Ingress{ 864 TypeMeta: metav1.TypeMeta{ 865 APIVersion: "networking.k8s.io/v1beta1", 866 }, 867 ObjectMeta: metav1.ObjectMeta{ 868 Name: "ingress-helm", 869 Namespace: "default", 870 Labels: map[string]string{ 871 "helm.toolkit.fluxcd.io/name": "helm-release", 872 "helm.toolkit.fluxcd.io/namespace": "default", 873 }, 874 }, 875 Spec: networkv1.IngressSpec{ 876 Rules: []networkv1.IngressRule{ 877 { 878 Host: "ingress.domain.helm", 879 IngressRuleValue: networkv1.IngressRuleValue{ 880 HTTP: &networkv1.HTTPIngressRuleValue{ 881 Paths: []networkv1.HTTPIngressPath{ 882 { 883 Path: "/", 884 Backend: networkv1.IngressBackend{ 885 Service: &networkv1.IngressServiceBackend{ 886 Name: "clusterip", 887 Port: networkv1.ServiceBackendPort{ 888 Number: 80, 889 }, 890 }, 891 }, 892 PathType: &prefixbeta, 893 }, 894 }, 895 }, 896 }, 897 }, 898 }, 899 }, 900 }, 901 } 902 903 for _, ing := range testIngress { 904 err := k8sClient.Create(context.TODO(), ing) 905 Expect(err).Should(BeNil()) 906 } 907 908 obj := &unstructured.Unstructured{} 909 obj.SetName("sdep") 910 obj.SetNamespace("default") 911 obj.SetAnnotations(map[string]string{ 912 annoAmbassadorServiceName: "seldon-ambassador", 913 annoAmbassadorServiceNamespace: "default", 914 }) 915 obj.SetGroupVersionKind(schema.GroupVersionKind{ 916 Group: "machinelearning.seldon.io", 917 Version: "v1", 918 Kind: "SeldonDeployment", 919 }) 920 err = k8sClient.Create(context.TODO(), obj) 921 Expect(err).Should(BeNil()) 922 923 // Create the HTTPRoute for test 924 resources := []string{ 925 "./testdata/gateway/http-route.yaml", 926 "./testdata/gateway/gateway.yaml", 927 "./testdata/gateway/gateway-tls.yaml", 928 "./testdata/gateway/https-route.yaml", 929 } 930 var objects []client.Object 931 for _, resource := range resources { 932 data, err := os.ReadFile(resource) 933 Expect(err).Should(BeNil()) 934 var route unstructured.Unstructured 935 err = yaml.Unmarshal(data, &route) 936 Expect(err).Should(BeNil()) 937 objects = append(objects, &route) 938 } 939 940 for _, res := range objects { 941 err := k8sClient.Create(context.TODO(), res) 942 Expect(err).Should(BeNil()) 943 } 944 945 // Prepare nodes in test environment 946 masterNode := &corev1.Node{ 947 ObjectMeta: metav1.ObjectMeta{ 948 Name: "node-1", 949 Labels: map[string]string{ 950 "node-role.kubernetes.io/master": "true", 951 }, 952 }, 953 Status: corev1.NodeStatus{ 954 Addresses: []corev1.NodeAddress{ 955 { 956 Type: corev1.NodeInternalIP, 957 Address: "internal-ip-1", 958 }, 959 }, 960 }, 961 } 962 workerNode := &corev1.Node{ 963 ObjectMeta: metav1.ObjectMeta{ 964 Name: "node-2", 965 Labels: map[string]string{ 966 "node-role.kubernetes.io/worker": "true", 967 }, 968 }, 969 Status: corev1.NodeStatus{ 970 Addresses: []corev1.NodeAddress{ 971 { 972 Type: corev1.NodeInternalIP, 973 Address: "internal-ip-2", 974 }, 975 { 976 Type: corev1.NodeExternalIP, 977 Address: "external-ip-2", 978 }, 979 }, 980 }, 981 } 982 Expect(k8sClient.Create(ctx, masterNode)).Should(BeNil()) 983 Expect(k8sClient.Create(ctx, workerNode)).Should(BeNil()) 984 985 opt := `app: { 986 name: "endpoints-app" 987 namespace: "default" 988 filter: { 989 cluster: "", 990 clusterNamespace: "default", 991 } 992 withTree: true 993 }` 994 v, err := value.NewValue(opt, nil, "") 995 Expect(err).Should(BeNil()) 996 pr := &provider{ 997 cli: k8sClient, 998 } 999 logCtx := monitorContext.NewTraceContext(ctx, "") 1000 err = pr.CollectServiceEndpoints(logCtx, nil, v, nil) 1001 Expect(err).Should(BeNil()) 1002 gatewayIP := selectorNodeIP(ctx, "", k8sClient) 1003 Expect(gatewayIP).Should(Equal("external-ip-2")) 1004 urls := []string{ 1005 "http://ingress.domain", 1006 "https://ingress.domain.https", 1007 "https://ingress.domain.path/test", 1008 "https://ingress.domain.path/test2", 1009 fmt.Sprintf("http://%s:30229", gatewayIP), 1010 "http://10.10.10.10", 1011 "http://text.example.com", 1012 "10.10.10.10:81", 1013 "text.example.com:81", 1014 fmt.Sprintf("http://%s:30002", gatewayIP), 1015 "http://ingress.domain.helm", 1016 "http://1.1.1.1/seldon/default/sdep", 1017 "http://gateway.domain", 1018 "http://gateway.domain/api", 1019 "https://demo.kubevela.net", 1020 } 1021 endValue, err := v.Field("list") 1022 Expect(err).Should(BeNil()) 1023 var endpoints []querytypes.ServiceEndpoint 1024 err = endValue.Decode(&endpoints) 1025 Expect(err).Should(BeNil()) 1026 Expect(len(urls)).Should(Equal(len(endpoints))) 1027 for i, e := range endpoints { 1028 fmt.Println(e.String()) 1029 Expect(urls[i]).Should(Equal(e.String())) 1030 } 1031 }) 1032 }) 1033 1034 var deploymentYaml = ` 1035 apiVersion: apps/v1 1036 kind: Deployment 1037 metadata: 1038 labels: 1039 app.oam.dev/app-revision-hash: ee69f7ed168cd8fa 1040 app.oam.dev/appRevision: first-vela-app-v1 1041 app.oam.dev/component: express-server 1042 app.oam.dev/name: first-vela-app 1043 app.oam.dev/resourceType: WORKLOAD 1044 app.oam.dev/revision: express-server-v1 1045 oam.dev/render-hash: ee2d39b553b6ef03 1046 workload.oam.dev/type: webservice 1047 name: express-server 1048 namespace: default 1049 spec: 1050 replicas: 2 1051 selector: 1052 matchLabels: 1053 app.oam.dev/component: express-server 1054 template: 1055 metadata: 1056 labels: 1057 app.oam.dev/component: express-server 1058 spec: 1059 containers: 1060 - image: crccheck/hello-world 1061 imagePullPolicy: Always 1062 name: express-server 1063 ports: 1064 - containerPort: 8000 1065 protocol: TCP 1066 ` 1067 1068 var serviceYaml = ` 1069 apiVersion: v1 1070 kind: Service 1071 metadata: 1072 labels: 1073 app.oam.dev/app-revision-hash: ee69f7ed168cd8fa 1074 app.oam.dev/appRevision: first-vela-app-v1 1075 app.oam.dev/component: express-server 1076 app.oam.dev/name: first-vela-app 1077 app.oam.dev/resourceType: TRAIT 1078 app.oam.dev/revision: express-server-v1 1079 oam.dev/render-hash: bebe99ac3e9607d0 1080 trait.oam.dev/resource: service 1081 trait.oam.dev/type: ingress-1-20 1082 name: express-server 1083 namespace: default 1084 spec: 1085 ports: 1086 - port: 8000 1087 protocol: TCP 1088 targetPort: 8000 1089 selector: 1090 app.oam.dev/component: express-server 1091 ` 1092 1093 var podYaml = ` 1094 apiVersion: v1 1095 kind: Pod 1096 metadata: 1097 labels: 1098 app.oam.dev/component: express-server 1099 name: express-server-b77f4476b-4mt5m 1100 namespace: default 1101 spec: 1102 containers: 1103 - image: crccheck/hello-world 1104 imagePullPolicy: Always 1105 name: express-server-1 1106 ports: 1107 - containerPort: 8000 1108 protocol: TCP 1109 `