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"}, &gtapp)).Should(BeNil())
    90  			gtapp.Status = sts
    91  			Expect(k8sClient.Status().Update(ctx, &gtapp)).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  `