936 lines
34 KiB
Go
936 lines
34 KiB
Go
/*
|
|
Copyright 2022 The Karmada Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package e2e
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/onsi/ginkgo/v2"
|
|
"github.com/onsi/gomega"
|
|
appsv1 "k8s.io/api/apps/v1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
"k8s.io/apimachinery/pkg/util/rand"
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
|
"k8s.io/client-go/dynamic"
|
|
"k8s.io/client-go/kubernetes"
|
|
"k8s.io/klog/v2"
|
|
|
|
clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
|
|
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
|
|
searchv1alpha1 "github.com/karmada-io/karmada/pkg/apis/search/v1alpha1"
|
|
"github.com/karmada-io/karmada/pkg/karmadactl/join"
|
|
"github.com/karmada-io/karmada/pkg/karmadactl/options"
|
|
"github.com/karmada-io/karmada/pkg/karmadactl/unjoin"
|
|
cmdutil "github.com/karmada-io/karmada/pkg/karmadactl/util"
|
|
"github.com/karmada-io/karmada/test/e2e/framework"
|
|
testhelper "github.com/karmada-io/karmada/test/helper"
|
|
)
|
|
|
|
var _ = ginkgo.Describe("[karmada-search] karmada search testing", ginkgo.Ordered, func() {
|
|
var member1 = "member1"
|
|
var member2 = "member2"
|
|
|
|
var member1NodeName = "member1-control-plane"
|
|
var member2NodeName = "member2-control-plane"
|
|
var member1PodName = "etcd-member1-control-plane"
|
|
var member2PodName = "etcd-member2-control-plane"
|
|
var existsDeploymentName = "coredns"
|
|
var existsServiceName = "kubernetes"
|
|
var existsDaemonsetName = "kube-proxy"
|
|
var existsClusterRoleName = "cluster-admin"
|
|
var existsClusterRoleBindingName = "cluster-admin"
|
|
|
|
var pathPrefix = "/apis/search.karmada.io/v1alpha1/search/cache/"
|
|
var pathAllServices = pathPrefix + "api/v1/services"
|
|
var pathAllNodes = pathPrefix + "api/v1/nodes"
|
|
var pathAllPods = pathPrefix + "api/v1/pods"
|
|
var pathAllDeployments = pathPrefix + "apis/apps/v1/deployments"
|
|
var pathAllDaemonsets = pathPrefix + "apis/apps/v1/daemonsets"
|
|
var pathAllClusterRoles = pathPrefix + "apis/rbac.authorization.k8s.io/v1/clusterroles"
|
|
var pathAllClusterRoleBindings = pathPrefix + "apis/rbac.authorization.k8s.io/v1/clusterrolebindings"
|
|
var pathNSDeploymentsFmt = pathPrefix + "apis/apps/v1/namespaces/%s/deployments"
|
|
// var pathWithLabel = pathPrefix + "apis/apps/v1/namespaces/kube-system/deployments?labelSelector=k8s-app=kube-dns"
|
|
|
|
ginkgo.BeforeAll(func() {
|
|
// get clusters' name
|
|
pushModeClusters := framework.ClusterNamesWithSyncMode(clusterv1alpha1.Push)
|
|
ginkgo.By(fmt.Sprintf("get %v clusters in push mode", len(pushModeClusters)))
|
|
gomega.Expect(len(pushModeClusters) >= 2).Should(gomega.BeTrue())
|
|
pushModeClusters = pushModeClusters[:2]
|
|
sort.Strings(pushModeClusters)
|
|
member1, member2 = pushModeClusters[0], pushModeClusters[1]
|
|
ginkgo.By(fmt.Sprintf("test on %v and %v", member1, member2))
|
|
|
|
// clean ResourceRegistries before test
|
|
gomega.Expect(karmadaClient.SearchV1alpha1().ResourceRegistries().DeleteCollection(context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{})).Should(gomega.Succeed())
|
|
})
|
|
|
|
// use service as search object
|
|
ginkgo.Describe("no ResourceRegistry testings", func() {
|
|
ginkgo.It("[Service] should be not searchable", func() {
|
|
searchObject(pathAllServices, existsServiceName, false)
|
|
})
|
|
})
|
|
|
|
// use deployment, node, pod as search object
|
|
ginkgo.Describe("create ResourceRegistry testings", func() {
|
|
// use deployment as search object
|
|
ginkgo.Context("caching cluster member1", ginkgo.Ordered, func() {
|
|
var rrName string
|
|
var rr *searchv1alpha1.ResourceRegistry
|
|
var m1DmName, m2DmName string
|
|
var m1Dm, m2Dm *appsv1.Deployment
|
|
var member1Client, member2Client kubernetes.Interface
|
|
|
|
ginkgo.BeforeAll(func() {
|
|
member1Client = framework.GetClusterClient(member1)
|
|
gomega.Expect(member1Client).ShouldNot(gomega.BeNil())
|
|
m1DmName = "rr-member1-deployment-" + rand.String(RandomStrLength)
|
|
m1Dm = testhelper.NewDeployment(testNamespace, m1DmName)
|
|
framework.CreateDeployment(member1Client, m1Dm)
|
|
|
|
member2Client = framework.GetClusterClient(member2)
|
|
gomega.Expect(member2Client).ShouldNot(gomega.BeNil())
|
|
m2DmName = "rr-member2-deployment-" + rand.String(RandomStrLength)
|
|
m2Dm = testhelper.NewDeployment(testNamespace, m2DmName)
|
|
framework.CreateDeployment(member2Client, m2Dm)
|
|
|
|
rrName = resourceRegistryPrefix + rand.String(RandomStrLength)
|
|
rr = &searchv1alpha1.ResourceRegistry{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: rrName,
|
|
},
|
|
Spec: searchv1alpha1.ResourceRegistrySpec{
|
|
TargetCluster: policyv1alpha1.ClusterAffinity{
|
|
ClusterNames: []string{member1},
|
|
},
|
|
ResourceSelectors: []searchv1alpha1.ResourceSelector{
|
|
{
|
|
APIVersion: "apps/v1",
|
|
Kind: "Deployment",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
framework.CreateResourceRegistry(karmadaClient, rr)
|
|
})
|
|
|
|
ginkgo.AfterAll(func() {
|
|
framework.RemoveDeployment(member1Client, testNamespace, m1DmName)
|
|
framework.RemoveDeployment(member2Client, testNamespace, m2DmName)
|
|
framework.RemoveResourceRegistry(karmadaClient, rrName)
|
|
})
|
|
|
|
ginkgo.It("[member1 deployments] should be searchable", func() {
|
|
searchObject(pathAllDeployments, existsDeploymentName, true)
|
|
})
|
|
|
|
ginkgo.It("[member2 deployments] should be not searchable", func() {
|
|
searchObject(pathAllDeployments, m2DmName, false)
|
|
})
|
|
|
|
ginkgo.It("[member1 deployments namespace] should be searchable", func() {
|
|
searchObject(fmt.Sprintf(pathNSDeploymentsFmt, testNamespace), m1DmName, true)
|
|
})
|
|
|
|
ginkgo.It("[member2 deployments namespace] should be not searchable", func() {
|
|
searchObject(fmt.Sprintf(pathNSDeploymentsFmt, testNamespace), m2DmName, false)
|
|
})
|
|
|
|
// ginkgo.It("[deployments label] should be searchable", func() {
|
|
// searchObject(pathWithLabel, existsDeploymentName, true)
|
|
// })
|
|
})
|
|
|
|
// use node as search object
|
|
ginkgo.Context("caching cluster member1 & member2", ginkgo.Ordered, func() {
|
|
var rrName string
|
|
var rr *searchv1alpha1.ResourceRegistry
|
|
|
|
ginkgo.BeforeAll(func() {
|
|
rrName = resourceRegistryPrefix + rand.String(RandomStrLength)
|
|
rr = &searchv1alpha1.ResourceRegistry{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: rrName,
|
|
},
|
|
Spec: searchv1alpha1.ResourceRegistrySpec{
|
|
TargetCluster: policyv1alpha1.ClusterAffinity{
|
|
ClusterNames: []string{member1, member2},
|
|
},
|
|
ResourceSelectors: []searchv1alpha1.ResourceSelector{
|
|
{
|
|
APIVersion: "v1",
|
|
Kind: "Node",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
framework.CreateResourceRegistry(karmadaClient, rr)
|
|
})
|
|
|
|
ginkgo.AfterAll(func() {
|
|
framework.RemoveResourceRegistry(karmadaClient, rrName)
|
|
})
|
|
|
|
ginkgo.It("[member1 nodes] should be searchable", func() {
|
|
searchObject(pathAllNodes, member1NodeName, true)
|
|
})
|
|
|
|
ginkgo.It("[member2 nodes] should be searchable", func() {
|
|
searchObject(pathAllNodes, member2NodeName, true)
|
|
})
|
|
})
|
|
|
|
// use pod as search object
|
|
ginkgo.Context("add two resourceRegistry", ginkgo.Ordered, func() {
|
|
var rrName string
|
|
var rr *searchv1alpha1.ResourceRegistry
|
|
var rr2Name string
|
|
var rr2 *searchv1alpha1.ResourceRegistry
|
|
|
|
ginkgo.BeforeAll(func() {
|
|
rrName = resourceRegistryPrefix + rand.String(RandomStrLength)
|
|
rr = &searchv1alpha1.ResourceRegistry{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: rrName,
|
|
},
|
|
Spec: searchv1alpha1.ResourceRegistrySpec{
|
|
TargetCluster: policyv1alpha1.ClusterAffinity{
|
|
ClusterNames: []string{member1},
|
|
},
|
|
ResourceSelectors: []searchv1alpha1.ResourceSelector{
|
|
{
|
|
APIVersion: "v1",
|
|
Kind: "Pod",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
framework.CreateResourceRegistry(karmadaClient, rr)
|
|
|
|
rr2Name = resourceRegistryPrefix + rand.String(RandomStrLength)
|
|
rr2 = &searchv1alpha1.ResourceRegistry{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: rr2Name,
|
|
},
|
|
Spec: searchv1alpha1.ResourceRegistrySpec{
|
|
TargetCluster: policyv1alpha1.ClusterAffinity{
|
|
ClusterNames: []string{member2},
|
|
},
|
|
ResourceSelectors: []searchv1alpha1.ResourceSelector{
|
|
{
|
|
APIVersion: "v1",
|
|
Kind: "Pod",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
framework.CreateResourceRegistry(karmadaClient, rr2)
|
|
})
|
|
|
|
ginkgo.AfterAll(func() {
|
|
framework.RemoveResourceRegistry(karmadaClient, rrName)
|
|
framework.RemoveResourceRegistry(karmadaClient, rr2Name)
|
|
})
|
|
|
|
ginkgo.It("[member1 pods] should be searchable", func() {
|
|
searchObject(pathAllPods, member1PodName, true)
|
|
})
|
|
|
|
ginkgo.It("[member2 pods] should be searchable", func() {
|
|
searchObject(pathAllPods, member2PodName, true)
|
|
})
|
|
})
|
|
})
|
|
|
|
// use clusterrole, clusterrolebinding as search object
|
|
ginkgo.Describe("update resourceRegistry", ginkgo.Ordered, func() {
|
|
var rrName string
|
|
var rr *searchv1alpha1.ResourceRegistry
|
|
|
|
ginkgo.BeforeAll(func() {
|
|
rrName = resourceRegistryPrefix + rand.String(RandomStrLength)
|
|
rr = &searchv1alpha1.ResourceRegistry{
|
|
Spec: searchv1alpha1.ResourceRegistrySpec{
|
|
TargetCluster: policyv1alpha1.ClusterAffinity{
|
|
ClusterNames: []string{member1},
|
|
},
|
|
ResourceSelectors: []searchv1alpha1.ResourceSelector{
|
|
{
|
|
APIVersion: "rbac.authorization.k8s.io/v1",
|
|
Kind: "ClusterRole",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
rr.Name = rrName
|
|
framework.CreateResourceRegistry(karmadaClient, rr)
|
|
})
|
|
|
|
ginkgo.AfterAll(func() {
|
|
framework.RemoveResourceRegistry(karmadaClient, rrName)
|
|
})
|
|
|
|
ginkgo.It("[clusterrole] should be searchable", func() {
|
|
searchObject(pathAllClusterRoles, existsClusterRoleName, true)
|
|
})
|
|
|
|
ginkgo.It("[clusterrolebinding] should not be searchable", func() {
|
|
searchObject(pathAllClusterRoleBindings, existsClusterRoleBindingName, false)
|
|
})
|
|
|
|
ginkgo.It("[clusterrolebinding] should be searchable", func() {
|
|
ginkgo.By("update resourceRegistry, add clusterrolebinding")
|
|
rr.Spec.ResourceSelectors = []searchv1alpha1.ResourceSelector{
|
|
{
|
|
APIVersion: "rbac.authorization.k8s.io/v1",
|
|
Kind: "ClusterRole",
|
|
},
|
|
{
|
|
APIVersion: "rbac.authorization.k8s.io/v1",
|
|
Kind: "ClusterRoleBinding",
|
|
},
|
|
}
|
|
framework.UpdateResourceRegistry(karmadaClient, rr)
|
|
searchObject(pathAllClusterRoleBindings, existsClusterRoleBindingName, true)
|
|
})
|
|
})
|
|
|
|
// use daemonset as search object
|
|
ginkgo.Describe("delete resourceRegistry", ginkgo.Ordered, func() {
|
|
var rrName string
|
|
var rr *searchv1alpha1.ResourceRegistry
|
|
|
|
ginkgo.It("[daemonset] should be searchable", func() {
|
|
ginkgo.By("create resourceRegistry, add daemonset")
|
|
rrName = resourceRegistryPrefix + rand.String(RandomStrLength)
|
|
rr = &searchv1alpha1.ResourceRegistry{
|
|
Spec: searchv1alpha1.ResourceRegistrySpec{
|
|
TargetCluster: policyv1alpha1.ClusterAffinity{
|
|
ClusterNames: []string{member1},
|
|
},
|
|
ResourceSelectors: []searchv1alpha1.ResourceSelector{
|
|
{
|
|
APIVersion: "apps/v1",
|
|
Kind: "DaemonSet",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
rr.Name = rrName
|
|
framework.CreateResourceRegistry(karmadaClient, rr)
|
|
searchObject(pathAllDaemonsets, existsDaemonsetName, true)
|
|
})
|
|
|
|
ginkgo.It("[daemonset] should not be searchable", func() {
|
|
ginkgo.By("delete resourceRegistry")
|
|
framework.RemoveResourceRegistry(karmadaClient, rrName)
|
|
searchObject(pathAllDaemonsets, existsDaemonsetName, false)
|
|
})
|
|
})
|
|
|
|
ginkgo.Describe("backend store testing", ginkgo.Ordered, func() {
|
|
})
|
|
|
|
ginkgo.Describe("karmada proxy testing", ginkgo.Ordered, func() {
|
|
var (
|
|
pollTimeout, pollInterval = time.Second * 10, time.Second
|
|
m1Client, m2Client, proxyClient kubernetes.Interface
|
|
m1Dynamic, m2Dynamic dynamic.Interface
|
|
nodeGVR = corev1.SchemeGroupVersion.WithResource("nodes")
|
|
)
|
|
|
|
ginkgo.BeforeAll(func() {
|
|
m1Dynamic = framework.GetClusterDynamicClient(member1)
|
|
gomega.Expect(m1Dynamic).ShouldNot(gomega.BeNil())
|
|
m2Dynamic = framework.GetClusterDynamicClient(member2)
|
|
gomega.Expect(m2Dynamic).ShouldNot(gomega.BeNil())
|
|
|
|
m1Client = framework.GetClusterClient(member1)
|
|
gomega.Expect(m1Client).ShouldNot(gomega.BeNil())
|
|
m2Client = framework.GetClusterClient(member2)
|
|
gomega.Expect(m2Client).ShouldNot(gomega.BeNil())
|
|
|
|
proxyConfig := *restConfig
|
|
proxyConfig.Host += "/apis/search.karmada.io/v1alpha1/proxying/karmada/proxy"
|
|
proxyClient = kubernetes.NewForConfigOrDie(&proxyConfig)
|
|
})
|
|
|
|
ginkgo.Describe("resourceRegistry testings", func() {
|
|
var (
|
|
rr1, rr2 *searchv1alpha1.ResourceRegistry
|
|
m1Deploy, m2Deploy *appsv1.Deployment
|
|
)
|
|
|
|
ginkgo.BeforeAll(func() {
|
|
rr1 = &searchv1alpha1.ResourceRegistry{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: resourceRegistryPrefix + rand.String(RandomStrLength),
|
|
},
|
|
Spec: searchv1alpha1.ResourceRegistrySpec{
|
|
TargetCluster: policyv1alpha1.ClusterAffinity{
|
|
ClusterNames: []string{member1, member2},
|
|
},
|
|
ResourceSelectors: []searchv1alpha1.ResourceSelector{
|
|
{
|
|
APIVersion: "v1",
|
|
Kind: "Node",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
rr2 = &searchv1alpha1.ResourceRegistry{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: resourceRegistryPrefix + rand.String(RandomStrLength),
|
|
},
|
|
Spec: searchv1alpha1.ResourceRegistrySpec{
|
|
TargetCluster: policyv1alpha1.ClusterAffinity{
|
|
ClusterNames: []string{member1, member2},
|
|
},
|
|
ResourceSelectors: []searchv1alpha1.ResourceSelector{
|
|
{
|
|
APIVersion: "v1",
|
|
Kind: "Node",
|
|
},
|
|
{
|
|
APIVersion: "v1",
|
|
Kind: "Pod",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
m1Deploy = testhelper.NewDeployment(testNamespace, "proxy-m1-"+rand.String(RandomStrLength))
|
|
framework.CreateDeployment(m1Client, m1Deploy)
|
|
|
|
m2Deploy = testhelper.NewDeployment(testNamespace, "proxy-m2-"+rand.String(RandomStrLength))
|
|
framework.CreateDeployment(m2Client, m2Deploy)
|
|
})
|
|
|
|
ginkgo.AfterAll(func() {
|
|
err := karmadaClient.SearchV1alpha1().ResourceRegistries().Delete(context.TODO(), rr1.Name, metav1.DeleteOptions{})
|
|
if err != nil && !apierrors.IsNotFound(err) {
|
|
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
}
|
|
err = karmadaClient.SearchV1alpha1().ResourceRegistries().Delete(context.TODO(), rr2.Name, metav1.DeleteOptions{})
|
|
if err != nil && !apierrors.IsNotFound(err) {
|
|
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
}
|
|
|
|
framework.RemoveDeployment(m1Client, testNamespace, m1Deploy.Name)
|
|
framework.RemoveDeployment(m2Client, testNamespace, m2Deploy.Name)
|
|
})
|
|
|
|
ginkgo.It("create, update, delete resourceRegistry", func() {
|
|
ginkgo.By("no resourceRegistries testings", func() {
|
|
ginkgo.By("should not list pods", func() {
|
|
gomega.Eventually(func(g gomega.Gomega) {
|
|
list, err := proxyClient.CoreV1().Pods(testNamespace).List(context.TODO(), metav1.ListOptions{})
|
|
g.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
g.Expect(list.Items).Should(gomega.BeEmpty())
|
|
}, pollTimeout, pollInterval).Should(gomega.Succeed())
|
|
})
|
|
|
|
ginkgo.By("should not list nodes", func() {
|
|
gomega.Eventually(func(g gomega.Gomega) {
|
|
list, err := proxyClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
|
|
g.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
g.Expect(list.Items).Should(gomega.BeEmpty())
|
|
}, pollTimeout, pollInterval).Should(gomega.Succeed())
|
|
})
|
|
})
|
|
|
|
ginkgo.By("create resourceRegistry rr1 for nodes", func() {
|
|
framework.CreateResourceRegistry(karmadaClient, rr1)
|
|
|
|
ginkgo.By("should not list pods", func() {
|
|
gomega.Eventually(func(g gomega.Gomega) {
|
|
list, err := proxyClient.CoreV1().Pods(testNamespace).List(context.TODO(), metav1.ListOptions{})
|
|
g.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
g.Expect(list.Items).Should(gomega.BeEmpty())
|
|
}, pollTimeout, pollInterval).Should(gomega.Succeed())
|
|
})
|
|
|
|
ginkgo.By("should list nodes", func() {
|
|
gomega.Eventually(func(g gomega.Gomega) {
|
|
list, err := proxyClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
|
|
g.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
g.Expect(list.Items).ShouldNot(gomega.BeEmpty())
|
|
}, pollTimeout, pollInterval).Should(gomega.Succeed())
|
|
})
|
|
})
|
|
|
|
ginkgo.By("create resourceRegistry rr2 for pods, nodes", func() {
|
|
framework.CreateResourceRegistry(karmadaClient, rr2)
|
|
|
|
ginkgo.By("should list pods", func() {
|
|
gomega.Eventually(func(g gomega.Gomega) {
|
|
list, err := proxyClient.CoreV1().Pods(testNamespace).List(context.TODO(), metav1.ListOptions{})
|
|
g.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
g.Expect(list.Items).ShouldNot(gomega.BeEmpty())
|
|
}, pollTimeout, pollInterval).Should(gomega.Succeed())
|
|
})
|
|
|
|
ginkgo.By("should list nodes", func() {
|
|
gomega.Eventually(func(g gomega.Gomega) {
|
|
list, err := proxyClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
|
|
g.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
g.Expect(list.Items).ShouldNot(gomega.BeEmpty())
|
|
}, pollTimeout, pollInterval).Should(gomega.Succeed())
|
|
})
|
|
})
|
|
|
|
ginkgo.By("delete resourceRegistries rr1 and rr2", func() {
|
|
framework.RemoveResourceRegistry(karmadaClient, rr1.Name)
|
|
framework.RemoveResourceRegistry(karmadaClient, rr2.Name)
|
|
|
|
ginkgo.By("should not list pods", func() {
|
|
gomega.Eventually(func(g gomega.Gomega) {
|
|
list, err := proxyClient.CoreV1().Pods(testNamespace).List(context.TODO(), metav1.ListOptions{})
|
|
g.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
g.Expect(list.Items).Should(gomega.BeEmpty())
|
|
}, pollTimeout, pollInterval).Should(gomega.Succeed())
|
|
})
|
|
|
|
ginkgo.By("should not list nodes", func() {
|
|
gomega.Eventually(func(g gomega.Gomega) {
|
|
list, err := proxyClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
|
|
g.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
g.Expect(list.Items).Should(gomega.BeEmpty())
|
|
}, pollTimeout, pollInterval).Should(gomega.Succeed())
|
|
})
|
|
})
|
|
})
|
|
})
|
|
ginkgo.Describe("access resources testings", func() {
|
|
ginkgo.Context("caching nodes", func() {
|
|
var (
|
|
rr *searchv1alpha1.ResourceRegistry
|
|
|
|
deleteAnnotationAfterTest = func(c kubernetes.Interface, name string, anno string) {
|
|
data := []byte(`{"metadata": {"annotations": {"` + anno + `":null}}}`)
|
|
_, err := c.CoreV1().Nodes().Patch(context.TODO(), name, types.StrategicMergePatchType, data, metav1.PatchOptions{})
|
|
klog.Warningf("Clean node %v's annotation %v failed: %v", name, anno, err)
|
|
}
|
|
)
|
|
|
|
ginkgo.BeforeAll(func() {
|
|
rr = &searchv1alpha1.ResourceRegistry{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: resourceRegistryPrefix + rand.String(RandomStrLength),
|
|
},
|
|
Spec: searchv1alpha1.ResourceRegistrySpec{
|
|
TargetCluster: policyv1alpha1.ClusterAffinity{
|
|
ClusterNames: []string{member1, member2},
|
|
},
|
|
ResourceSelectors: []searchv1alpha1.ResourceSelector{
|
|
{
|
|
APIVersion: "v1",
|
|
Kind: "Node",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
framework.CreateResourceRegistry(karmadaClient, rr)
|
|
})
|
|
|
|
ginkgo.AfterAll(func() {
|
|
framework.RemoveResourceRegistry(karmadaClient, rr.Name)
|
|
})
|
|
|
|
ginkgo.It("could get node", func() {
|
|
testObject := framework.GetAnyResourceOrFail(m1Dynamic.Resource(nodeGVR))
|
|
|
|
var get *corev1.Node
|
|
gomega.Eventually(func(g gomega.Gomega) {
|
|
var err error
|
|
get, err = proxyClient.CoreV1().Nodes().Get(context.TODO(), testObject.GetName(), metav1.GetOptions{})
|
|
g.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
}, pollTimeout, pollInterval).Should(gomega.Succeed())
|
|
|
|
gomega.Expect(get.Annotations[clusterv1alpha1.CacheSourceAnnotationKey]).Should(gomega.Equal(member1))
|
|
})
|
|
|
|
ginkgo.It("could list nodes", func() {
|
|
fromM1 := framework.GetResourceNames(m1Dynamic.Resource(nodeGVR))
|
|
ginkgo.By("list nodes from member1: " + strings.Join(sets.List(fromM1), ","))
|
|
fromM2 := framework.GetResourceNames(m2Dynamic.Resource(nodeGVR))
|
|
ginkgo.By("list nodes from member2: " + strings.Join(sets.List(fromM2), ","))
|
|
fromMembers := sets.New[string]().Union(fromM1).Union(fromM2)
|
|
|
|
var proxyList *corev1.NodeList
|
|
gomega.Eventually(func(g gomega.Gomega) {
|
|
var err error
|
|
proxyList, err = proxyClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
|
|
g.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
|
|
fromProxy := sets.New[string]()
|
|
for _, item := range proxyList.Items {
|
|
fromProxy.Insert(item.Name)
|
|
}
|
|
g.Expect(fromProxy).Should(gomega.Equal(fromMembers))
|
|
}, pollTimeout, pollInterval).Should(gomega.Succeed())
|
|
|
|
// assert cache source annotation
|
|
groupM1, groupM2 := sets.New[string](), sets.New[string]()
|
|
for _, item := range proxyList.Items {
|
|
cluster := item.Annotations[clusterv1alpha1.CacheSourceAnnotationKey]
|
|
switch cluster {
|
|
case member1:
|
|
groupM1.Insert(item.Name)
|
|
case member2:
|
|
groupM2.Insert(item.Name)
|
|
}
|
|
}
|
|
gomega.Expect(groupM1).Should(gomega.Equal(fromM1))
|
|
gomega.Expect(groupM2).Should(gomega.Equal(fromM2))
|
|
})
|
|
|
|
ginkgo.It("could chunk list nodes", func() {
|
|
fromM1, err := m1Client.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
|
|
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
ginkgo.By(fmt.Sprintf("list %v nodes from member1", len(fromM1.Items)))
|
|
|
|
fromM2, err := m2Client.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
|
|
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
ginkgo.By(fmt.Sprintf("list %v nodes from member2", len(fromM2.Items)))
|
|
|
|
total := len(fromM1.Items) + len(fromM2.Items)
|
|
if total < 2 {
|
|
ginkgo.Skip(fmt.Sprintf("Total nodes %v, less than 2", total))
|
|
}
|
|
|
|
var list1 *corev1.NodeList
|
|
gomega.Eventually(func(g gomega.Gomega) {
|
|
list1, err = proxyClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{Limit: 1})
|
|
g.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
g.Expect(list1.Items).ShouldNot(gomega.BeEmpty())
|
|
}, pollTimeout, pollInterval).Should(gomega.Succeed())
|
|
|
|
gomega.Expect(list1.Items).Should(gomega.HaveLen(1))
|
|
gomega.Expect(list1.Continue).ShouldNot(gomega.BeEmpty())
|
|
|
|
list2, err := proxyClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{
|
|
Limit: 999999999999,
|
|
Continue: list1.Continue,
|
|
})
|
|
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
gomega.Expect(list2.Items).Should(gomega.HaveLen(total - len(list1.Items)))
|
|
})
|
|
|
|
ginkgo.It("could list & watch nodes", func() {
|
|
var listObj *corev1.NodeList
|
|
gomega.Eventually(func(g gomega.Gomega) {
|
|
var err error
|
|
listObj, err = proxyClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
|
|
g.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
g.Expect(listObj.Items).ShouldNot(gomega.BeEmpty())
|
|
}, pollTimeout, pollInterval).Should(gomega.Succeed())
|
|
|
|
watcher, err := proxyClient.CoreV1().Nodes().Watch(context.TODO(), metav1.ListOptions{ResourceVersion: listObj.ResourceVersion})
|
|
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
defer watcher.Stop()
|
|
|
|
testNode := framework.GetAnyResourceOrFail(m1Dynamic.Resource(nodeGVR))
|
|
|
|
anno := "proxy-ann-" + rand.String(RandomStrLength)
|
|
data := []byte(`{"metadata": {"annotations": {"` + anno + `": "true"}}}`)
|
|
_, err = m1Client.CoreV1().Nodes().Patch(context.TODO(), testNode.GetName(), types.StrategicMergePatchType, data, metav1.PatchOptions{})
|
|
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
defer func() {
|
|
deleteAnnotationAfterTest(m1Client, testNode.GetName(), anno)
|
|
}()
|
|
|
|
var get *corev1.Node
|
|
gomega.Eventually(func() bool {
|
|
var ok bool
|
|
event := <-watcher.ResultChan()
|
|
get, ok = event.Object.(*corev1.Node)
|
|
if !ok {
|
|
return false
|
|
}
|
|
return get.UID == testNode.GetUID() && metav1.HasAnnotation(get.ObjectMeta, anno)
|
|
}, time.Second*10, 0).Should(gomega.BeTrue())
|
|
|
|
gomega.Expect(get.Annotations[clusterv1alpha1.CacheSourceAnnotationKey]).Should(gomega.Equal(member1))
|
|
})
|
|
|
|
ginkgo.It("could path nodes", func() {
|
|
testObject := framework.GetAnyResourceOrFail(m1Dynamic.Resource(nodeGVR))
|
|
|
|
anno := "proxy-ann-" + rand.String(RandomStrLength)
|
|
data := []byte(`{"metadata": {"annotations": {"` + anno + `": "true"}}}`)
|
|
|
|
gomega.Eventually(func() error {
|
|
_, err := proxyClient.CoreV1().Nodes().Patch(context.TODO(), testObject.GetName(), types.StrategicMergePatchType, data, metav1.PatchOptions{})
|
|
return err
|
|
}, pollTimeout, pollInterval).Should(gomega.Succeed())
|
|
defer func() {
|
|
deleteAnnotationAfterTest(m1Client, testObject.GetName(), anno)
|
|
}()
|
|
|
|
testPod, err := m1Client.CoreV1().Nodes().Get(context.TODO(), testObject.GetName(), metav1.GetOptions{})
|
|
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
gomega.Expect(testPod.Annotations).Should(gomega.HaveKey(anno))
|
|
})
|
|
|
|
ginkgo.It("could update node", func() {
|
|
testObject := framework.GetAnyResourceOrFail(m2Dynamic.Resource(nodeGVR))
|
|
anno := "proxy-ann-" + rand.String(RandomStrLength)
|
|
|
|
ginkgo.By("update node " + testObject.GetName())
|
|
gomega.Eventually(func(g gomega.Gomega) {
|
|
node, err := proxyClient.CoreV1().Nodes().Get(context.TODO(), testObject.GetName(), metav1.GetOptions{})
|
|
g.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
|
|
metav1.SetMetaDataAnnotation(&node.ObjectMeta, anno, "true")
|
|
_, err = proxyClient.CoreV1().Nodes().Update(context.TODO(), node, metav1.UpdateOptions{})
|
|
g.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
}, pollTimeout, pollInterval).Should(gomega.Succeed())
|
|
defer func() {
|
|
deleteAnnotationAfterTest(m2Client, testObject.GetName(), anno)
|
|
}()
|
|
|
|
node, err := m2Client.CoreV1().Nodes().Get(context.TODO(), testObject.GetName(), metav1.GetOptions{})
|
|
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
gomega.Expect(metav1.HasAnnotation(node.ObjectMeta, anno)).Should(gomega.BeTrue())
|
|
gomega.Expect(metav1.HasAnnotation(node.ObjectMeta, clusterv1alpha1.CacheSourceAnnotationKey)).Should(gomega.BeFalse())
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
// test when cluster joined, updated, karmada will reconcile search cache
|
|
var _ = framework.SerialDescribe("reconcile ResourceRegistry when clusters joined, updated", func() {
|
|
var member1 = "member1"
|
|
var member2 = "member2"
|
|
|
|
var member1PodName = "etcd-member1-control-plane"
|
|
var member2PodName = "etcd-member2-control-plane"
|
|
var existsDeploymentName = "coredns"
|
|
|
|
var pathPrefix = "/apis/search.karmada.io/v1alpha1/search/cache/"
|
|
var pathAllPods = pathPrefix + "api/v1/pods"
|
|
var pathAllDeployments = pathPrefix + "apis/apps/v1/deployments"
|
|
|
|
ginkgo.Context("when cluster joined", func() {
|
|
var (
|
|
clusterName string
|
|
homeDir string
|
|
kubeConfigPath string
|
|
controlPlane string
|
|
clusterContext string
|
|
f cmdutil.Factory
|
|
|
|
rrName string
|
|
rr *searchv1alpha1.ResourceRegistry
|
|
resourceSelector searchv1alpha1.ResourceSelector
|
|
)
|
|
|
|
ginkgo.BeforeEach(func() {
|
|
clusterName = "member-e2e-" + rand.String(RandomStrLength)
|
|
homeDir = os.Getenv("HOME")
|
|
kubeConfigPath = fmt.Sprintf("%s/.kube/%s.config", homeDir, clusterName)
|
|
controlPlane = fmt.Sprintf("%s-control-plane", clusterName)
|
|
clusterContext = fmt.Sprintf("kind-%s", clusterName)
|
|
defaultConfigFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag().WithDiscoveryBurst(300).WithDiscoveryQPS(50.0)
|
|
defaultConfigFlags.Context = &karmadaContext
|
|
f = cmdutil.NewFactory(defaultConfigFlags)
|
|
|
|
// create a cluster
|
|
ginkgo.By(fmt.Sprintf("Creating cluster: %s", clusterName), func() {
|
|
err := createCluster(clusterName, kubeConfigPath, controlPlane, clusterContext)
|
|
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
})
|
|
|
|
// create a ResourceRegistry with a non joined cluster name
|
|
rrName = resourceRegistryPrefix + rand.String(RandomStrLength)
|
|
resourceSelector = searchv1alpha1.ResourceSelector{
|
|
APIVersion: "apps/v1",
|
|
Kind: "Deployment",
|
|
}
|
|
rr = &searchv1alpha1.ResourceRegistry{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: rrName,
|
|
},
|
|
Spec: searchv1alpha1.ResourceRegistrySpec{
|
|
TargetCluster: policyv1alpha1.ClusterAffinity{
|
|
ClusterNames: []string{clusterName},
|
|
},
|
|
ResourceSelectors: []searchv1alpha1.ResourceSelector{
|
|
resourceSelector,
|
|
},
|
|
},
|
|
}
|
|
// create a ResourceRegistry with a non joined cluster name
|
|
ginkgo.By(fmt.Sprintf("create ResourceRegistry %s with a non joined cluster name %v and resource selector %v", rrName, clusterName, resourceSelector), func() {
|
|
framework.CreateResourceRegistry(karmadaClient, rr)
|
|
})
|
|
})
|
|
|
|
ginkgo.AfterEach(func() {
|
|
framework.RemoveResourceRegistry(karmadaClient, rrName)
|
|
|
|
ginkgo.By(fmt.Sprintf("Deleting clusters: %s", clusterName), func() {
|
|
err := deleteCluster(clusterName, kubeConfigPath)
|
|
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
_ = os.Remove(kubeConfigPath)
|
|
})
|
|
})
|
|
|
|
ginkgo.It("[member clusters joined] could reconcile ResourceRegistry", func() {
|
|
// search cache should not have the deployment
|
|
searchObject(pathAllDeployments, existsDeploymentName, false)
|
|
// join the cluster
|
|
ginkgo.By(fmt.Sprintf("Joining cluster: %s", clusterName), func() {
|
|
opts := join.CommandJoinOption{
|
|
DryRun: false,
|
|
ClusterNamespace: "karmada-cluster",
|
|
ClusterName: clusterName,
|
|
ClusterContext: clusterContext,
|
|
ClusterKubeConfig: kubeConfigPath,
|
|
}
|
|
err := opts.Run(f)
|
|
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
})
|
|
// search cache should have the deployment
|
|
searchObject(pathAllDeployments, existsDeploymentName, true)
|
|
|
|
ginkgo.By(fmt.Sprintf("Unjoinning cluster: %s", clusterName), func() {
|
|
opts := unjoin.CommandUnjoinOption{
|
|
DryRun: false,
|
|
ClusterNamespace: "karmada-cluster",
|
|
ClusterName: clusterName,
|
|
ClusterContext: clusterContext,
|
|
ClusterKubeConfig: kubeConfigPath,
|
|
Wait: 5 * options.DefaultKarmadactlCommandDuration,
|
|
}
|
|
err := opts.Run(f)
|
|
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
})
|
|
})
|
|
})
|
|
|
|
ginkgo.Context("when cluster updated", func() {
|
|
var (
|
|
rrName string
|
|
rr *searchv1alpha1.ResourceRegistry
|
|
labelKey string
|
|
labelValue string
|
|
labelSelector *metav1.LabelSelector
|
|
resourceSelector searchv1alpha1.ResourceSelector
|
|
)
|
|
|
|
ginkgo.BeforeEach(func() {
|
|
// create a ResourceRegistry with label selector
|
|
rrName = resourceRegistryPrefix + rand.String(RandomStrLength)
|
|
labelKey = "karmada-cluster.k8s.some.com/managed"
|
|
labelValue = "true"
|
|
labelSelector = &metav1.LabelSelector{
|
|
MatchExpressions: []metav1.LabelSelectorRequirement{
|
|
{
|
|
Key: labelKey,
|
|
Operator: metav1.LabelSelectorOpExists,
|
|
},
|
|
},
|
|
}
|
|
resourceSelector = searchv1alpha1.ResourceSelector{
|
|
APIVersion: "v1",
|
|
Kind: "Pod",
|
|
}
|
|
rr = &searchv1alpha1.ResourceRegistry{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: rrName,
|
|
},
|
|
Spec: searchv1alpha1.ResourceRegistrySpec{
|
|
TargetCluster: policyv1alpha1.ClusterAffinity{
|
|
LabelSelector: labelSelector,
|
|
},
|
|
ResourceSelectors: []searchv1alpha1.ResourceSelector{
|
|
resourceSelector,
|
|
},
|
|
},
|
|
}
|
|
ginkgo.By(fmt.Sprintf("create ResourceRegistry %s with label selector %v and resource selector %v", rrName, labelSelector, resourceSelector), func() {
|
|
framework.CreateResourceRegistry(karmadaClient, rr)
|
|
})
|
|
})
|
|
|
|
ginkgo.AfterEach(func() {
|
|
framework.RemoveResourceRegistry(karmadaClient, rrName)
|
|
})
|
|
|
|
ginkgo.It("[member clusters updated, deleted label] could reconcile ResourceRegistry", func() {
|
|
// search the pod in the ResourceRegistry will fail
|
|
searchObject(pathAllPods, member1PodName, false)
|
|
|
|
// add the label to the member1 cluster
|
|
ginkgo.By(fmt.Sprintf("add label %s=%s to cluster %s", labelKey, labelValue, member1), func() {
|
|
framework.UpdateClusterLabels(karmadaClient, member1, map[string]string{labelKey: labelValue})
|
|
})
|
|
|
|
// search the pod in the ResourceRegistry will success
|
|
searchObject(pathAllPods, member1PodName, true)
|
|
|
|
// add the label to the member2 cluster
|
|
ginkgo.By(fmt.Sprintf("add label %s=%s to cluster %s", labelKey, labelValue, member2), func() {
|
|
framework.UpdateClusterLabels(karmadaClient, member2, map[string]string{labelKey: labelValue})
|
|
})
|
|
|
|
// search the pod in the ResourceRegistry will success
|
|
searchObject(pathAllPods, member2PodName, true)
|
|
|
|
// delete the label of the member2 cluster
|
|
ginkgo.By(fmt.Sprintf("delete label %s=%s to cluster %s", labelKey, labelValue, member2), func() {
|
|
framework.DeleteClusterLabels(karmadaClient, member2, map[string]string{labelKey: labelValue})
|
|
})
|
|
|
|
// search the pod in the ResourceRegistry will fail
|
|
searchObject(pathAllPods, member2PodName, false)
|
|
|
|
// delete the label of the member1 cluster
|
|
ginkgo.By(fmt.Sprintf("delete label %s=%s to cluster %s", labelKey, labelValue, member1), func() {
|
|
framework.DeleteClusterLabels(karmadaClient, member1, map[string]string{labelKey: labelValue})
|
|
|
|
})
|
|
|
|
// search the pod in the ResourceRegistry will fail
|
|
searchObject(pathAllPods, member1PodName, false)
|
|
})
|
|
})
|
|
})
|
|
|
|
func searchObject(path, target string, exists bool) {
|
|
gomega.Eventually(func(g gomega.Gomega) (bool, error) {
|
|
res := karmadaClient.SearchV1alpha1().RESTClient().Get().AbsPath(path).Do(context.TODO())
|
|
g.Expect(res.Error()).ShouldNot(gomega.HaveOccurred())
|
|
raw, err := res.Raw()
|
|
g.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
return strings.Contains(string(raw), target), nil
|
|
}, pollTimeout, pollInterval).Should(gomega.Equal(exists))
|
|
}
|