diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml new file mode 100644 index 000000000..fee5028ab --- /dev/null +++ b/.github/workflows/e2e.yaml @@ -0,0 +1,33 @@ +name: E2E +on: + pull_request: +jobs: + e2e-test: + runs-on: ubuntu-22.04 + name: E2E Test + timeout-minutes: 40 + steps: + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: 1.20.x + - name: Checkout code + uses: actions/checkout@v4 + - name: Build Clusters + uses: helm/kind-action@v1.8.0 + with: + version: v0.20.0 + install_only: true + - name: Set Up Clusters + run: | + hack/e2e-test/build-clusters.sh + - name: Install Helm + uses: azure/setup-helm@v3 + with: + version: v3.10.1 + - name: Init kurator cluster + run: | + hack/e2e-test/install-kurator.sh + - name: fleet-clusters e2e test + run: | + hack/e2e-test/run-e2e.sh diff --git a/Makefile b/Makefile index 4da070d12..33b272f4d 100644 --- a/Makefile +++ b/Makefile @@ -146,7 +146,8 @@ gen-chart: sync-crds .PHONY: test test: clean tidy - go test ./... + go test ./pkg/... + go test ./cmd/... .PHONY: clean clean: diff --git a/e2e/attachedcluster_test.go b/e2e/attachedcluster_test.go new file mode 100644 index 000000000..4b5810cc1 --- /dev/null +++ b/e2e/attachedcluster_test.go @@ -0,0 +1,115 @@ +/* +Copyright Kurator 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" + "os" + "path/filepath" + "time" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "kurator.dev/kurator/e2e/resources" + clusterv1a1 "kurator.dev/kurator/pkg/apis/cluster/v1alpha1" + fleetv1a1 "kurator.dev/kurator/pkg/apis/fleet/v1alpha1" +) + +var _ = ginkgo.Describe("[AttachedClusters] AttachedClusters testing", func() { + var ( + namespace string + fleetname string + memberClusterName string + kubeconfigPath string + secret *corev1.Secret + attachedcluster *clusterv1a1.AttachedCluster + ) + + ginkgo.BeforeEach(func() { + namespace = "e2e-test" + fleetname = "e2etest" + memberClusterName = "kurator-member" + homeDir, err := os.UserHomeDir() + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + kubeconfigPath = filepath.Join(homeDir, ".kube/kurator-member.config") + + // create namespace for e2e test + e2eNamespace := resources.NewNamespace(namespace) + createNSErr := resources.CreateNamespace(kubeClient, e2eNamespace) + gomega.Expect(createNSErr).ShouldNot(gomega.HaveOccurred()) + time.Sleep(3 * time.Second) + + // build secrets use member cluster kubeconfig + kubeconfig, readfileErr := os.ReadFile(kubeconfigPath) + gomega.Expect(readfileErr).ShouldNot(gomega.HaveOccurred()) + data := make(map[string][]byte) + data[memberClusterName] = kubeconfig + secret = resources.NewSecret(namespace, memberClusterName, data) + + // build two attachedclusters + secretKeyRef := clusterv1a1.SecretKeyRef{ + Name: memberClusterName, + Key: memberClusterName, + } + attachedcluster = resources.NewAttachedCluster(namespace, memberClusterName, secretKeyRef) + }) + + ginkgo.AfterEach(func() { + fleerRemoveErr := resources.RemoveFleet(kuratorClient, namespace, fleetname) + gomega.Expect(fleerRemoveErr).ShouldNot(gomega.HaveOccurred()) + + attachedclusterRemoveErr := resources.RemoveAttachedCluster(kuratorClient, namespace, memberClusterName) + gomega.Expect(attachedclusterRemoveErr).ShouldNot(gomega.HaveOccurred()) + + secretRemoveErr := resources.RemoveSecret(kubeClient, namespace, memberClusterName) + gomega.Expect(secretRemoveErr).ShouldNot(gomega.HaveOccurred()) + + namespaceRemoveErr := resources.RemoveNamespace(kubeClient, namespace) + gomega.Expect(namespaceRemoveErr).ShouldNot(gomega.HaveOccurred()) + }) + + ginkgo.It("Create Fleet", func() { + // step 1.create secrets + secretCreateErr := resources.CreateSecret(kubeClient, secret) + gomega.Expect(secretCreateErr).ShouldNot(gomega.HaveOccurred()) + + // step 2.create attachedclusters + attachedCreateErr := resources.CreateAttachedCluster(kuratorClient, attachedcluster) + gomega.Expect(attachedCreateErr).ShouldNot(gomega.HaveOccurred()) + + time.Sleep(3 * time.Second) + // step 3.create fleet + clusters := []*corev1.ObjectReference{ + { + Name: memberClusterName, + Kind: "AttachedCluster", + }, + } + fleet := resources.NewFleet(namespace, fleetname, clusters) + fleetCreateErr := resources.CreateFleet(kuratorClient, fleet) + gomega.Expect(fleetCreateErr).ShouldNot(gomega.HaveOccurred()) + time.Sleep(3 * time.Second) + + // step 4.check fleet status + fleetPresentOnCluster, fleetGetErr := kuratorClient.FleetV1alpha1().Fleets(namespace).Get(context.TODO(), fleetname, metav1.GetOptions{}) + gomega.Expect(fleetGetErr).ShouldNot(gomega.HaveOccurred()) + gomega.Expect(fleetPresentOnCluster.Status.Phase).Should(gomega.Equal(fleetv1a1.ReadyPhase)) + }) +}) diff --git a/e2e/framework/cluster.go b/e2e/framework/cluster.go new file mode 100644 index 000000000..9f4efcb3a --- /dev/null +++ b/e2e/framework/cluster.go @@ -0,0 +1,44 @@ +/* +Copyright Kurator 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 framework + +import ( + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/klog/v2" +) + +// LoadRESTClientConfig creates a rest.Config using the passed kubeconfig. If context is empty, current context in kubeconfig will be used. +func LoadRESTClientConfig(kubeconfig string, context string) (*rest.Config, error) { + loader := &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig} + loadedConfig, err := loader.Load() + if err != nil { + return nil, err + } + + if context == "" { + context = loadedConfig.CurrentContext + } + klog.Infof("Use context %v", context) + + return clientcmd.NewNonInteractiveClientConfig( + *loadedConfig, + context, + &clientcmd.ConfigOverrides{}, + loader, + ).ClientConfig() +} diff --git a/e2e/resources/attachedcluster.go b/e2e/resources/attachedcluster.go new file mode 100644 index 000000000..cc1bf72bf --- /dev/null +++ b/e2e/resources/attachedcluster.go @@ -0,0 +1,77 @@ +/* +Copyright Kurator 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 resources + +import ( + "context" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + clusterv1a1 "kurator.dev/kurator/pkg/apis/cluster/v1alpha1" + kurator "kurator.dev/kurator/pkg/client-go/generated/clientset/versioned" +) + +func NewAttachedCluster(namespace string, name string, config clusterv1a1.SecretKeyRef) *clusterv1a1.AttachedCluster { + return &clusterv1a1.AttachedCluster{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "cluster.kurator.dev/v1alpha1", + Kind: "AttachedCluster", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Spec: clusterv1a1.AttachedClusterSpec{ + Kubeconfig: config, + }, + } +} + +// CreateAttachedCluster create AttachedCluster. +func CreateAttachedCluster(client kurator.Interface, attachedCluster *clusterv1a1.AttachedCluster) error { + _, err := client.ClusterV1alpha1().AttachedClusters(attachedCluster.Namespace).Create(context.TODO(), attachedCluster, metav1.CreateOptions{}) + if err != nil { + if apierrors.IsAlreadyExists(err) { + return UpdateAttachedCluster(client, attachedCluster) + } else { + return err + } + } + return nil +} + +// UpdateAttachedCluster update AttachedCluster +func UpdateAttachedCluster(client kurator.Interface, attachedCluster *clusterv1a1.AttachedCluster) error { + _, err := client.ClusterV1alpha1().AttachedClusters(attachedCluster.Namespace).Update(context.TODO(), attachedCluster, metav1.UpdateOptions{}) + if err != nil { + return err + } + return nil +} + +func RemoveAttachedCluster(client kurator.Interface, namespace, name string) error { + err := client.ClusterV1alpha1().AttachedClusters(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + return nil + } else { + return err + } + } + return nil +} diff --git a/e2e/resources/fleet.go b/e2e/resources/fleet.go new file mode 100644 index 000000000..f3fd8fa28 --- /dev/null +++ b/e2e/resources/fleet.go @@ -0,0 +1,79 @@ +/* +Copyright Kurator 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 resources + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + fleetv1a1 "kurator.dev/kurator/pkg/apis/fleet/v1alpha1" + kurator "kurator.dev/kurator/pkg/client-go/generated/clientset/versioned" +) + +// NewFleet will build a Fleet object. +func NewFleet(namespace string, name string, clusters []*corev1.ObjectReference) *fleetv1a1.Fleet { + return &fleetv1a1.Fleet{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "fleet.kurator.dev/v1alpha1", + Kind: "Fleet", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Spec: fleetv1a1.FleetSpec{ + Clusters: clusters, + }, + } +} + +// CreateAttachedCluster create AttachedCluster. +func CreateFleet(client kurator.Interface, fleet *fleetv1a1.Fleet) error { + _, err := client.FleetV1alpha1().Fleets(fleet.Namespace).Create(context.TODO(), fleet, metav1.CreateOptions{}) + if err != nil { + if apierrors.IsAlreadyExists(err) { + return UpdateFleet(client, fleet) + } else { + return err + } + } + return nil +} + +// UpdateAttachedCluster update AttachedCluster +func UpdateFleet(client kurator.Interface, fleet *fleetv1a1.Fleet) error { + _, err := client.FleetV1alpha1().Fleets(fleet.Namespace).Update(context.TODO(), fleet, metav1.UpdateOptions{}) + if err != nil { + return err + } + return nil +} + +func RemoveFleet(client kurator.Interface, namespace, name string) error { + err := client.FleetV1alpha1().Fleets(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + return nil + } else { + return err + } + } + return nil +} diff --git a/e2e/resources/namespace.go b/e2e/resources/namespace.go new file mode 100644 index 000000000..ff614f841 --- /dev/null +++ b/e2e/resources/namespace.go @@ -0,0 +1,59 @@ +/* +Copyright Kurator 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 resources + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +// NewNamespace will build a Namespace object. +func NewNamespace(namespace string) *corev1.Namespace { + return &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: namespace, + }, + } +} + +func CreateNamespace(client kubernetes.Interface, namespace *corev1.Namespace) error { + _, err := client.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{}) + if err != nil { + if apierrors.IsAlreadyExists(err) { + return nil + } else { + return err + } + } + return nil +} + +func RemoveNamespace(client kubernetes.Interface, name string) error { + err := client.CoreV1().Namespaces().Delete(context.TODO(), name, metav1.DeleteOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + return nil + } else { + return err + } + } + return nil +} diff --git a/e2e/resources/secret.go b/e2e/resources/secret.go new file mode 100644 index 000000000..78a69ee54 --- /dev/null +++ b/e2e/resources/secret.go @@ -0,0 +1,75 @@ +/* +Copyright Kurator 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 resources + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +// NewSecret will build a secret object. +func NewSecret(namespace string, name string, data map[string][]byte) *corev1.Secret { + return &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Data: data, + } +} + +// CreateSecret create Secret. +func CreateSecret(client kubernetes.Interface, secret *corev1.Secret) error { + _, err := client.CoreV1().Secrets(secret.Namespace).Create(context.TODO(), secret, metav1.CreateOptions{}) + if err != nil { + if apierrors.IsAlreadyExists(err) { + return UpdateSecret(client, secret) + } else { + return err + } + } + return nil +} + +// UpdateSecret update Secret +func UpdateSecret(client kubernetes.Interface, secret *corev1.Secret) error { + _, err := client.CoreV1().Secrets(secret.Namespace).Update(context.TODO(), secret, metav1.UpdateOptions{}) + if err != nil { + return err + } + return nil +} + +func RemoveSecret(client kubernetes.Interface, namespace, name string) error { + err := client.CoreV1().Secrets(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + return nil + } else { + return err + } + } + return nil +} diff --git a/e2e/suite.go b/e2e/suite.go new file mode 100644 index 000000000..0a0fd8430 --- /dev/null +++ b/e2e/suite.go @@ -0,0 +1,17 @@ +/* +Copyright Kurator 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 diff --git a/e2e/suite_test.go b/e2e/suite_test.go new file mode 100644 index 000000000..d085943a9 --- /dev/null +++ b/e2e/suite_test.go @@ -0,0 +1,57 @@ +/* +Copyright Kurator 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 ( + "os" + "testing" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + "k8s.io/client-go/kubernetes" + + "kurator.dev/kurator/e2e/framework" + kurator "kurator.dev/kurator/pkg/client-go/generated/clientset/versioned" +) + +var ( + kubeconfig string + kubeClient kubernetes.Interface + kuratorClient kurator.Interface + kuratorContext string +) + +func TestE2E(t *testing.T) { + gomega.RegisterFailHandler(ginkgo.Fail) + ginkgo.RunSpecs(t, "E2E Suite") +} + +var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { + return nil +}, func(bytes []byte) { + kubeconfig = os.Getenv("KUBECONFIG") + gomega.Expect(kubeconfig).ShouldNot(gomega.BeEmpty()) + + rest, err := framework.LoadRESTClientConfig(kubeconfig, kuratorContext) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + + kubeClient, err = kubernetes.NewForConfig(rest) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + + kuratorClient, err = kurator.NewForConfig(rest) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) +}) diff --git a/go.mod b/go.mod index efba8f9f8..726227de4 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,8 @@ require ( github.com/gosuri/uitable v0.0.4 github.com/hashicorp/go-multierror v1.1.1 github.com/karmada-io/karmada v1.4.2 - github.com/onsi/gomega v1.27.10 + github.com/onsi/ginkgo/v2 v2.15.0 + github.com/onsi/gomega v1.30.0 github.com/pkg/errors v0.9.1 github.com/pkg/sftp v1.13.5 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.61.1 @@ -133,6 +134,7 @@ require ( github.com/go-openapi/jsonpointer v0.20.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gobuffalo/flect v1.0.2 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.2 // indirect @@ -145,6 +147,7 @@ require ( github.com/google/gnostic v0.6.9 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.4.0 // indirect @@ -225,7 +228,7 @@ require ( go.uber.org/zap v1.26.0 // indirect go4.org v0.0.0-20201209231011-d4a079459e60 // indirect golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect - golang.org/x/mod v0.12.0 // indirect + golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.19.0 // indirect golang.org/x/oauth2 v0.15.0 // indirect golang.org/x/sync v0.5.0 // indirect @@ -233,7 +236,7 @@ require ( golang.org/x/term v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.13.0 // indirect + golang.org/x/tools v0.16.1 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/api v0.152.0 // indirect google.golang.org/appengine v1.6.8 // indirect diff --git a/go.sum b/go.sum index 774cb2b37..38749558e 100644 --- a/go.sum +++ b/go.sum @@ -369,6 +369,7 @@ github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPr github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA= @@ -465,6 +466,7 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA= +github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= @@ -720,7 +722,8 @@ github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1L github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc= github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk= github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo= -github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA= +github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= +github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= @@ -737,8 +740,8 @@ github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdM github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw= github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw= github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= +github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= @@ -1037,8 +1040,9 @@ golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1316,8 +1320,8 @@ golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= +golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/hack/e2e-test/build-clusters.sh b/hack/e2e-test/build-clusters.sh new file mode 100755 index 000000000..cbea3a392 --- /dev/null +++ b/hack/e2e-test/build-clusters.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +# shellcheck disable=SC2086,SC1090,SC2206,SC1091 +set -o errexit +set -o nounset +set -o pipefail + +# This script starts a local karmada control plane based on current codebase and with a certain number of clusters joined. +# Parameters: [HOST_IPADDRESS](optional) if you want to export clusters' API server port to specific IP address +# This script depends on utils in: ${REPO_ROOT}/hack/util.sh +# 1. used by developer to setup develop environment quickly. +# 2. used by e2e testing to setup test environment automatically. +ROOT_DIR=$(git rev-parse --show-toplevel)/hack +KIND_CONFIGS_ROOT=${ROOT_DIR}/kind-configs +source "${ROOT_DIR}"/util.sh + +KIND_VERSION=${KIND_VERSION:-"kindest/node:v1.27.3"} + +# variable define +KUBECONFIG_PATH=${KUBECONFIG_PATH:-"${HOME}/.kube"} +MAIN_KUBECONFIG=${MAIN_KUBECONFIG:-"${KUBECONFIG_PATH}/kurator-host.config"} +HOST_CLUSTER_NAME=${HOST_CLUSTER_NAME:-"kurator-host"} +MEMBER_CLUSTER_KUBECONFIG=${MEMBER_CLUSTER_KUBECONFIG:-"${KUBECONFIG_PATH}/kurator-member.config"} +MEMBER_CLUSTER_NAME=${MEMBER_CLUSTER_NAME:-"kurator-member"} +ENABLE_KIND_WITH_WORKER=${ENABLE_KIND_WITH_WORKER:-"false"} + +#prepare for kind cluster config +TEMP_PATH=$(mktemp -d) +echo -e "Preparing kind config in path: ${TEMP_PATH}" +#When the Enable worker option is turned on, select to copy the configuration that contains the worker. +if [ ${ENABLE_KIND_WITH_WORKER} = "true" ]; then + cp -rf ${ROOT_DIR}/kind-configs-with-worker/*.yaml "${TEMP_PATH}"/ +else + cp -rf "${KIND_CONFIGS_ROOT}"/*.yaml "${TEMP_PATH}"/ +fi + +util::create_cluster "${HOST_CLUSTER_NAME}" "${MAIN_KUBECONFIG}" "${KIND_VERSION}" "${TEMP_PATH}" "${TEMP_PATH}"/host.yaml +util::create_cluster "${MEMBER_CLUSTER_NAME}" "${MEMBER_CLUSTER_KUBECONFIG}" "${KIND_VERSION}" "${TEMP_PATH}" "${TEMP_PATH}"/member1.yaml + +util::check_clusters_ready "${MAIN_KUBECONFIG}" "${HOST_CLUSTER_NAME}" +sleep 5s +util::check_clusters_ready "${MEMBER_CLUSTER_KUBECONFIG}" "${MEMBER_CLUSTER_NAME}" +sleep 10s + +# connecting networks between primary, remote clusters +echo "connect primary <-> remote" +util::connect_kind_clusters "${HOST_CLUSTER_NAME}" "${MAIN_KUBECONFIG}" "${MEMBER_CLUSTER_NAME}" "${MEMBER_CLUSTER_KUBECONFIG}" 1 + +echo "cluster networks connected" + +echo "install metallb in host cluster" +util::install_metallb ${MAIN_KUBECONFIG} ${HOST_CLUSTER_NAME} "ipv4" "255" + + +echo "starting install metallb in member clusters" +MEMBER_CLUSTERS=(${MEMBER_CLUSTER_NAME}) +MEMBER_KUBECONFIGS=(${MEMBER_CLUSTER_KUBECONFIG}) +MEMBER_IPSPACES=("254" "253") +echo "install metallb in ${MEMBER_CLUSTERS}" +util::install_metallb ${MEMBER_KUBECONFIGS} ${MEMBER_CLUSTERS} "ipv4" ${MEMBER_IPSPACES} + +function print_success() { + echo "Local clusters is running." + echo -e "\nTo start using your host cluster, run:" + echo -e " export KUBECONFIG=${MAIN_KUBECONFIG}" + echo -e "\nTo manage your remote clusters, run:" + echo -e " export KUBECONFIG=${MEMBER_CLUSTER_KUBECONFIG}" +} + +print_success \ No newline at end of file diff --git a/hack/e2e-test/install-kurator.sh b/hack/e2e-test/install-kurator.sh new file mode 100755 index 000000000..fbc8ebcbc --- /dev/null +++ b/hack/e2e-test/install-kurator.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +# This script installs Kurator cluster-operater and fleet-manager. + +KUBECONFIG_PATH=${KUBECONFIG_PATH:-"${HOME}/.kube"} +MAIN_KUBECONFIG=${MAIN_KUBECONFIG:-"${KUBECONFIG_PATH}/kurator-host.config"} +export KUBECONFIG=${MAIN_KUBECONFIG} +COMMIT_ID=$(git rev-parse --short HEAD) +VERSION=$(echo "$COMMIT_ID" | grep -o '^[0-9]') + +sleep 5s + +helm repo add jetstack https://charts.jetstack.io +helm repo update +kubectl create namespace cert-manager +helm install -n cert-manager cert-manager jetstack/cert-manager --set installCRDs=true + +helm repo add fluxcd-community https://fluxcd-community.github.io/helm-charts +cat < /dev/null" 300 kubectl config rename-context "kind-${context_name}" "${context_name}" --kubeconfig="${kubeconfig_path}" diff --git a/licenses/github.com/onsi/ginkgo/v2/LICENSE b/licenses/github.com/onsi/ginkgo/v2/LICENSE new file mode 100644 index 000000000..9415ee72c --- /dev/null +++ b/licenses/github.com/onsi/ginkgo/v2/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2013-2014 Onsi Fakhouri + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.