mirror of
https://github.com/open-cluster-management-io/ocm.git
synced 2026-05-21 08:33:49 +00:00
✨ Add importer into registration (#753)
* Add importer into registraiton Signed-off-by: Jian Qiu <jqiu@redhat.com> * Add unit tests Signed-off-by: Jian Qiu <jqiu@redhat.com> * Add integration test Signed-off-by: Jian Qiu <jqiu@redhat.com> --------- Signed-off-by: Jian Qiu <jqiu@redhat.com>
This commit is contained in:
@@ -75,6 +75,8 @@ var CRDPaths = []string{
|
||||
"./vendor/open-cluster-management.io/api/cluster/v1beta2/0000_01_clusters.open-cluster-management.io_managedclustersetbindings.crd.yaml",
|
||||
// spoke
|
||||
"./vendor/open-cluster-management.io/api/cluster/v1alpha1/0000_02_clusters.open-cluster-management.io_clusterclaims.crd.yaml",
|
||||
// external API deps
|
||||
"./test/integration/testdeps/capi/cluster.x-k8s.io_clusters.yaml",
|
||||
}
|
||||
|
||||
func runAgent(name string, opt *spoke.SpokeAgentOptions, commOption *commonoptions.AgentOptions, cfg *rest.Config) context.CancelFunc {
|
||||
@@ -196,12 +198,17 @@ var _ = ginkgo.BeforeSuite(func() {
|
||||
err = features.HubMutableFeatureGate.Set("ResourceCleanup=true")
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
|
||||
// enable clusterImporter feature gate
|
||||
err = features.HubMutableFeatureGate.Set("ClusterImporter=true")
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
|
||||
// start hub controller
|
||||
var ctx context.Context
|
||||
startHub = func() {
|
||||
ctx, stopHub = context.WithCancel(context.Background())
|
||||
go func() {
|
||||
m := hub.NewHubManagerOptions()
|
||||
m.ImportOption.APIServerURL = cfg.Host
|
||||
m.ClusterAutoApprovalUsers = []string{util.AutoApprovalBootstrapUser}
|
||||
err := m.RunControllerManager(ctx, &controllercmd.ControllerContext{
|
||||
KubeConfig: cfg,
|
||||
|
||||
183
test/integration/registration/managedcluster_importer_test.go
Normal file
183
test/integration/registration/managedcluster_importer_test.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package registration_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/onsi/ginkgo/v2"
|
||||
"github.com/onsi/gomega"
|
||||
"github.com/openshift/api"
|
||||
"github.com/openshift/library-go/pkg/operator/events"
|
||||
"github.com/openshift/library-go/pkg/operator/resource/resourceapply"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/util/rand"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/client-go/dynamic"
|
||||
|
||||
operatorclient "open-cluster-management.io/api/client/operator/clientset/versioned"
|
||||
clusterv1 "open-cluster-management.io/api/cluster/v1"
|
||||
operatorv1 "open-cluster-management.io/api/operator/v1"
|
||||
|
||||
testingcommon "open-cluster-management.io/ocm/pkg/common/testing"
|
||||
"open-cluster-management.io/ocm/pkg/operator/helpers/chart"
|
||||
"open-cluster-management.io/ocm/pkg/registration/hub/importer"
|
||||
"open-cluster-management.io/ocm/pkg/registration/hub/importer/providers/capi"
|
||||
"open-cluster-management.io/ocm/test/integration/util"
|
||||
)
|
||||
|
||||
var (
|
||||
genericScheme = runtime.NewScheme()
|
||||
genericCodecs = serializer.NewCodecFactory(genericScheme)
|
||||
genericCodec = genericCodecs.UniversalDeserializer()
|
||||
)
|
||||
|
||||
func init() {
|
||||
utilruntime.Must(api.InstallKube(genericScheme))
|
||||
utilruntime.Must(apiextensionsv1.AddToScheme(genericScheme))
|
||||
utilruntime.Must(operatorv1.Install(genericScheme))
|
||||
}
|
||||
|
||||
var _ = ginkgo.Describe("Cluster Auto Importer", func() {
|
||||
var managedClusterName string
|
||||
var dynamicClient dynamic.Interface
|
||||
var operatorClient operatorclient.Interface
|
||||
ginkgo.BeforeEach(func() {
|
||||
suffix := rand.String(5)
|
||||
managedClusterName = fmt.Sprintf("managedcluster-%s", suffix)
|
||||
|
||||
ginkgo.By("Create bootstrap token")
|
||||
clusterManagerConfig := chart.NewDefaultClusterManagerChartConfig()
|
||||
clusterManagerConfig.CreateBootstrapSA = true
|
||||
clusterManagerConfig.CreateNamespace = true
|
||||
manifests, err := chart.RenderClusterManagerChart(clusterManagerConfig, "open-cluster-management")
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
recorder := events.NewInMemoryRecorder("importer-testing")
|
||||
for _, manifest := range manifests {
|
||||
requiredObj, _, err := genericCodec.Decode(manifest, nil, nil)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
switch t := requiredObj.(type) {
|
||||
case *corev1.Namespace:
|
||||
_, _, err = resourceapply.ApplyNamespace(context.TODO(), kubeClient.CoreV1(), recorder, t)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
case *corev1.ServiceAccount:
|
||||
_, _, err = resourceapply.ApplyServiceAccount(context.TODO(), kubeClient.CoreV1(), recorder, t)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
case *rbacv1.ClusterRole:
|
||||
_, _, err = resourceapply.ApplyClusterRole(context.TODO(), kubeClient.RbacV1(), recorder, t)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
case *rbacv1.ClusterRoleBinding:
|
||||
_, _, err = resourceapply.ApplyClusterRoleBinding(context.TODO(), kubeClient.RbacV1(), recorder, t)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
}
|
||||
}
|
||||
|
||||
dynamicClient, err = dynamic.NewForConfig(spokeCfg)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
|
||||
operatorClient, err = operatorclient.NewForConfig(spokeCfg)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
})
|
||||
|
||||
ginkgo.Context("Cluster API importer", func() {
|
||||
ginkgo.JustBeforeEach(func() {
|
||||
cluster := &clusterv1.ManagedCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: managedClusterName,
|
||||
},
|
||||
Spec: clusterv1.ManagedClusterSpec{
|
||||
HubAcceptsClient: true,
|
||||
},
|
||||
}
|
||||
_, err := clusterClient.ClusterV1().ManagedClusters().Create(context.TODO(), cluster, metav1.CreateOptions{})
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
})
|
||||
|
||||
ginkgo.JustAfterEach(func() {
|
||||
err := clusterClient.ClusterV1().ManagedClusters().Delete(context.TODO(), managedClusterName, metav1.DeleteOptions{})
|
||||
if !errors.IsNotFound(err) {
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
}
|
||||
})
|
||||
|
||||
ginkgo.It("Should import CAPI cluster", func() {
|
||||
ginkgo.By("Create CAPI cluster")
|
||||
capiCluster := testingcommon.NewUnstructured(
|
||||
"cluster.x-k8s.io/v1beta1", "Cluster", managedClusterName, managedClusterName)
|
||||
_, err := dynamicClient.Resource(capi.ClusterAPIGVR).Namespace(managedClusterName).Create(
|
||||
context.TODO(), capiCluster, metav1.CreateOptions{})
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
|
||||
gomega.Eventually(func() error {
|
||||
spokeCluster, err := util.GetManagedCluster(clusterClient, managedClusterName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !meta.IsStatusConditionFalse(
|
||||
spokeCluster.Status.Conditions, importer.ManagedClusterConditionImported) {
|
||||
return fmt.Errorf("cluster should have error when imported")
|
||||
}
|
||||
return nil
|
||||
}, eventuallyTimeout, eventuallyInterval).ShouldNot(gomega.HaveOccurred())
|
||||
|
||||
ginkgo.By("Create secret")
|
||||
capiSecret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: managedClusterName + "-kubeconfig",
|
||||
Namespace: managedClusterName,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"value": util.NewKubeConfig(spokeCfg),
|
||||
},
|
||||
}
|
||||
_, err = kubeClient.CoreV1().Secrets(managedClusterName).Create(context.TODO(), capiSecret, metav1.CreateOptions{})
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
|
||||
// trigger the capi cluster resource to reconcile again
|
||||
gomega.Eventually(func() error {
|
||||
capiCluster, err := dynamicClient.Resource(capi.ClusterAPIGVR).Namespace(managedClusterName).Get(
|
||||
context.TODO(), managedClusterName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
capiCluster.SetLabels(map[string]string{"reconcile": "trigger"})
|
||||
_, err = dynamicClient.Resource(capi.ClusterAPIGVR).Namespace(managedClusterName).Update(
|
||||
context.TODO(), capiCluster, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, eventuallyTimeout, eventuallyInterval).ShouldNot(gomega.HaveOccurred())
|
||||
|
||||
gomega.Eventually(func() error {
|
||||
spokeCluster, err := util.GetManagedCluster(clusterClient, managedClusterName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !meta.IsStatusConditionTrue(
|
||||
spokeCluster.Status.Conditions, importer.ManagedClusterConditionImported) {
|
||||
return fmt.Errorf("cluster should have imported")
|
||||
}
|
||||
_, err = operatorClient.OperatorV1().Klusterlets().Get(
|
||||
context.TODO(), "klusterlet", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, eventuallyTimeout, eventuallyInterval).ShouldNot(gomega.HaveOccurred())
|
||||
|
||||
err = dynamicClient.Resource(capi.ClusterAPIGVR).Namespace(managedClusterName).Delete(
|
||||
context.TODO(), managedClusterName, metav1.DeleteOptions{})
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
err = kubeClient.CoreV1().Secrets(managedClusterName).Delete(
|
||||
context.TODO(), managedClusterName+"-kubeconfig", metav1.DeleteOptions{})
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
})
|
||||
})
|
||||
})
|
||||
1939
test/integration/testdeps/capi/cluster.x-k8s.io_clusters.yaml
Normal file
1939
test/integration/testdeps/capi/cluster.x-k8s.io_clusters.yaml
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user