github.com/oam-dev/kubevela@v1.9.11/pkg/velaql/providers/query/endpoint_test.go (about) 1 /* 2 Copyright 2022 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 "time" 23 24 . "github.com/onsi/ginkgo/v2" 25 . "github.com/onsi/gomega" 26 corev1 "k8s.io/api/core/v1" 27 v1 "k8s.io/api/networking/v1" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 30 "k8s.io/apimachinery/pkg/runtime/schema" 31 "k8s.io/apimachinery/pkg/util/intstr" 32 "sigs.k8s.io/controller-runtime/pkg/client" 33 "sigs.k8s.io/yaml" 34 35 monitorContext "github.com/kubevela/pkg/monitor/context" 36 "github.com/kubevela/workflow/pkg/cue/model/value" 37 38 "github.com/oam-dev/kubevela/apis/core.oam.dev/common" 39 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" 40 "github.com/oam-dev/kubevela/apis/types" 41 "github.com/oam-dev/kubevela/pkg/oam" 42 "github.com/oam-dev/kubevela/pkg/oam/util" 43 querytypes "github.com/oam-dev/kubevela/pkg/velaql/providers/query/types" 44 ) 45 46 var _ = Describe("Test query endpoints", func() { 47 48 BeforeEach(func() { 49 }) 50 51 Context("Test Generate Endpoints", func() { 52 It("Test endpoints with additional rules", func() { 53 err := k8sClient.Create(context.TODO(), &corev1.Namespace{ 54 ObjectMeta: metav1.ObjectMeta{ 55 Name: "vela-system", 56 }, 57 }) 58 Expect(err).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{})) 59 sts := common.AppStatus{ 60 AppliedResources: []common.ClusterObjectReference{ 61 { 62 Cluster: "", 63 ObjectReference: corev1.ObjectReference{ 64 APIVersion: "machinelearning.seldon.io/v1", 65 Kind: "SeldonDeployment", 66 Namespace: "default", 67 Name: "sdep2", 68 }, 69 }, 70 }, 71 } 72 testApp := &v1beta1.Application{ 73 ObjectMeta: metav1.ObjectMeta{ 74 Name: "endpoints-app-2", 75 Namespace: "default", 76 }, 77 Spec: v1beta1.ApplicationSpec{ 78 Components: []common.ApplicationComponent{ 79 { 80 Name: "endpoints-test-2", 81 Type: "webservice", 82 }, 83 }, 84 }, 85 Status: sts, 86 } 87 Expect(k8sClient.Create(context.TODO(), testApp)).Should(BeNil()) 88 var gtapp v1beta1.Application 89 Expect(k8sClient.Get(context.TODO(), client.ObjectKey{Name: "endpoints-app-2", Namespace: "default"}, >app)).Should(BeNil()) 90 gtapp.Status = sts 91 Expect(k8sClient.Status().Update(ctx, >app)).Should(BeNil()) 92 var mr []v1beta1.ManagedResource 93 for _, ar := range sts.AppliedResources { 94 smr := v1beta1.ManagedResource{ 95 ClusterObjectReference: ar, 96 } 97 smr.Component = "endpoints-test-2" 98 mr = append(mr, smr) 99 } 100 rt := &v1beta1.ResourceTracker{ 101 ObjectMeta: metav1.ObjectMeta{ 102 Name: "endpoints-app-2", 103 Namespace: "default", 104 Labels: map[string]string{ 105 oam.LabelAppName: testApp.Name, 106 oam.LabelAppNamespace: testApp.Namespace, 107 }, 108 }, 109 Spec: v1beta1.ResourceTrackerSpec{ 110 Type: v1beta1.ResourceTrackerTypeRoot, 111 ManagedResources: mr, 112 }, 113 } 114 err = k8sClient.Create(context.TODO(), rt) 115 Expect(err).Should(BeNil()) 116 117 By("Prepare configmap for relationship") 118 119 err = k8sClient.Create(context.TODO(), &corev1.ConfigMap{ 120 ObjectMeta: metav1.ObjectMeta{ 121 Name: "rule-for-seldon-test", 122 Namespace: types.DefaultKubeVelaNS, 123 Labels: map[string]string{ 124 oam.LabelResourceRules: "true", 125 oam.LabelResourceRuleFormat: oam.ResourceTopologyFormatJSON, 126 }, 127 }, 128 Data: map[string]string{ 129 "rules": `[ 130 { 131 "parentResourceType": { 132 "group": "machinelearning.seldon.io", 133 "kind": "SeldonDeployment" 134 }, 135 "childrenResourceType": [ 136 { 137 "apiVersion": "v1", 138 "kind": "Service" 139 } 140 ] 141 } 142 ]`, 143 }, 144 }) 145 Expect(err).Should(BeNil()) 146 testServiceList := []map[string]interface{}{ 147 { 148 "name": "clusterip-2", 149 "ports": []corev1.ServicePort{ 150 {Port: 80, TargetPort: intstr.FromInt(80), Name: "80port"}, 151 {Port: 81, TargetPort: intstr.FromInt(81), Name: "81port"}, 152 }, 153 "type": corev1.ServiceTypeClusterIP, 154 }, 155 { 156 "name": "load-balancer", 157 "ports": []corev1.ServicePort{ 158 {Port: 8080, TargetPort: intstr.FromInt(8080), Name: "8080port", NodePort: 30020}, 159 }, 160 "type": corev1.ServiceTypeLoadBalancer, 161 "status": corev1.ServiceStatus{ 162 LoadBalancer: corev1.LoadBalancerStatus{ 163 Ingress: []corev1.LoadBalancerIngress{ 164 { 165 IP: "2.2.2.2", 166 }, 167 }, 168 }, 169 }, 170 }, 171 { 172 "name": "seldon-ambassador-2", 173 "ports": []corev1.ServicePort{ 174 {Port: 80, TargetPort: intstr.FromInt(80), Name: "80port"}, 175 }, 176 "type": corev1.ServiceTypeLoadBalancer, 177 "status": corev1.ServiceStatus{ 178 LoadBalancer: corev1.LoadBalancerStatus{ 179 Ingress: []corev1.LoadBalancerIngress{ 180 { 181 IP: "1.1.1.1", 182 }, 183 }, 184 }, 185 }, 186 }, 187 } 188 189 abgvk := schema.GroupVersionKind{ 190 Group: "machinelearning.seldon.io", 191 Version: "v1", 192 Kind: "SeldonDeployment", 193 } 194 obj := &unstructured.Unstructured{} 195 obj.SetName("sdep2") 196 obj.SetNamespace("default") 197 obj.SetAnnotations(map[string]string{ 198 annoAmbassadorServiceName: "seldon-ambassador-2", 199 annoAmbassadorServiceNamespace: "default", 200 }) 201 obj.SetGroupVersionKind(abgvk) 202 err = k8sClient.Create(context.TODO(), obj) 203 Expect(err).Should(BeNil()) 204 abobj := &unstructured.Unstructured{} 205 abobj.SetGroupVersionKind(abgvk) 206 Expect(k8sClient.Get(ctx, client.ObjectKey{Name: "sdep2", Namespace: "default"}, abobj)).Should(BeNil()) 207 208 for _, s := range testServiceList { 209 ns := "default" 210 if s["namespace"] != nil { 211 ns = s["namespace"].(string) 212 } 213 service := &corev1.Service{ 214 ObjectMeta: metav1.ObjectMeta{ 215 Name: s["name"].(string), 216 Namespace: ns, 217 OwnerReferences: []metav1.OwnerReference{ 218 {APIVersion: "machinelearning.seldon.io/v1", Kind: "SeldonDeployment", Name: "sdep2", UID: abobj.GetUID()}, 219 }, 220 }, 221 Spec: corev1.ServiceSpec{ 222 Ports: s["ports"].([]corev1.ServicePort), 223 Type: s["type"].(corev1.ServiceType), 224 }, 225 } 226 227 if s["labels"] != nil { 228 service.Labels = s["labels"].(map[string]string) 229 } 230 err := k8sClient.Create(context.TODO(), service) 231 Expect(err).Should(BeNil()) 232 if s["status"] != nil { 233 service.Status = s["status"].(corev1.ServiceStatus) 234 err := k8sClient.Status().Update(context.TODO(), service) 235 Expect(err).Should(BeNil()) 236 } 237 } 238 239 opt := `app: { 240 name: "endpoints-app-2" 241 namespace: "default" 242 filter: { 243 cluster: "", 244 clusterNamespace: "default", 245 } 246 withTree: true 247 }` 248 v, err := value.NewValue(opt, nil, "") 249 Expect(err).Should(BeNil()) 250 pr := &provider{ 251 cli: k8sClient, 252 } 253 logCtx := monitorContext.NewTraceContext(ctx, "") 254 err = pr.CollectServiceEndpoints(logCtx, nil, v, nil) 255 Expect(err).Should(BeNil()) 256 257 urls := []string{ 258 "http://1.1.1.1/seldon/default/sdep2", 259 "http://clusterip-2.default", 260 "clusterip-2.default:81", 261 "http://2.2.2.2:8080", 262 "http://1.1.1.1", 263 } 264 endValue, err := v.Field("list") 265 Expect(err).Should(BeNil()) 266 var endpoints []querytypes.ServiceEndpoint 267 err = endValue.Decode(&endpoints) 268 Expect(err).Should(BeNil()) 269 var edps []string 270 for _, e := range endpoints { 271 edps = append(edps, e.String()) 272 } 273 Expect(edps).Should(BeEquivalentTo(urls)) 274 }) 275 276 It("Test select gateway IP", func() { 277 masterNode := corev1.Node{ 278 ObjectMeta: metav1.ObjectMeta{ 279 Name: "node-1", 280 Labels: map[string]string{ 281 "node-role.kubernetes.io/master": "true", 282 }, 283 }, 284 Status: corev1.NodeStatus{ 285 Addresses: []corev1.NodeAddress{ 286 { 287 Type: corev1.NodeInternalIP, 288 Address: "node1-internal-ip", 289 }, 290 }, 291 }, 292 } 293 workerNode1 := corev1.Node{ 294 ObjectMeta: metav1.ObjectMeta{ 295 Name: "node-2", 296 Labels: map[string]string{ 297 "node-role.kubernetes.io/worker": "true", 298 }, 299 }, 300 Status: corev1.NodeStatus{ 301 Addresses: []corev1.NodeAddress{ 302 { 303 Type: corev1.NodeInternalIP, 304 Address: "node2-internal-ip", 305 }, 306 { 307 Type: corev1.NodeExternalIP, 308 Address: "node2-external-ip", 309 }, 310 }, 311 }, 312 } 313 workerNode2 := corev1.Node{ 314 ObjectMeta: metav1.ObjectMeta{ 315 Name: "node-3", 316 Labels: map[string]string{ 317 "node-role.kubernetes.io/worker": "true", 318 }, 319 }, 320 Status: corev1.NodeStatus{ 321 Addresses: []corev1.NodeAddress{ 322 { 323 Type: corev1.NodeInternalIP, 324 Address: "node3-internal-ip", 325 }, 326 }, 327 }, 328 } 329 gatewayNode := corev1.Node{ 330 ObjectMeta: metav1.ObjectMeta{ 331 Name: "node-4", 332 Labels: map[string]string{ 333 "node-role.kubernetes.io/gateway": "true", 334 }, 335 }, 336 Status: corev1.NodeStatus{ 337 Addresses: []corev1.NodeAddress{ 338 { 339 Type: corev1.NodeInternalIP, 340 Address: "node4-internal-ip", 341 }, 342 { 343 Type: corev1.NodeExternalIP, 344 Address: "node4-external-ip", 345 }, 346 }, 347 }, 348 } 349 testCase := []struct { 350 note string 351 nodes []corev1.Node 352 wantIP string 353 }{ 354 { 355 note: "only master node", 356 nodes: []corev1.Node{masterNode}, 357 wantIP: "node1-internal-ip", 358 }, 359 { 360 note: "with worker node, select external ip first", 361 nodes: []corev1.Node{masterNode, workerNode1}, 362 wantIP: "node2-external-ip", 363 }, 364 { 365 note: "with worker node, select worker's internal ip", 366 nodes: []corev1.Node{masterNode, workerNode2}, 367 wantIP: "node3-internal-ip", 368 }, 369 { 370 note: "with gateway node, gateway node first", 371 nodes: []corev1.Node{masterNode, workerNode1, workerNode1, gatewayNode}, 372 wantIP: "node4-external-ip", 373 }, 374 } 375 for _, tc := range testCase { 376 By(tc.note) 377 ip := selectGatewayIP(tc.nodes) 378 Expect(ip).Should(Equal(tc.wantIP)) 379 } 380 }) 381 }) 382 }) 383 384 var _ = Describe("Test get ingress endpoint", func() { 385 It("Test get ingress endpoint with different apiVersion", func() { 386 ingress1 := v1.Ingress{} 387 Expect(yaml.Unmarshal([]byte(ingressYaml1), &ingress1)).Should(BeNil()) 388 389 err := k8sClient.Create(ctx, &ingress1) 390 Expect(err).Should(BeNil()) 391 gvk := schema.GroupVersionKind{Group: "networking.k8s.io", Version: "v1", Kind: "Ingress"} 392 Eventually(func() error { 393 eps := getServiceEndpoints(ctx, k8sClient, gvk, ingress1.Name, ingress1.Namespace, "", "", nil) 394 if len(eps) != 1 { 395 return fmt.Errorf("result length missmatch") 396 } 397 return nil 398 }, 2*time.Second, 500*time.Millisecond).Should(BeNil()) 399 }) 400 }) 401 402 var ingressYaml1 = ` 403 apiVersion: networking.k8s.io/v1 404 kind: Ingress 405 metadata: 406 name: ingress-1 407 namespace: default 408 spec: 409 rules: 410 - http: 411 paths: 412 - path: /testpath 413 pathType: Prefix 414 backend: 415 service: 416 name: test 417 port: 418 number: 80 419 `