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:
Jian Qiu
2024-12-16 21:59:55 +08:00
committed by GitHub
parent 3493630ad2
commit 25ea10bcbf
17 changed files with 3315 additions and 6 deletions

View File

@@ -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,

View 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())
})
})
})

File diff suppressed because it is too large Load Diff