mirror of
https://github.com/open-cluster-management-io/ocm.git
synced 2026-05-20 08:04:52 +00:00
Merge pull request #28 from qiujian16/empty-server
Fix issue when externalserver is not set
This commit is contained in:
1
go.sum
1
go.sum
@@ -51,6 +51,7 @@ github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL
|
||||
github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/etcd v3.3.15+incompatible h1:+9RjdC18gMxNQVvSiXvObLu29mOFmkgdsB4cRTlV+EE=
|
||||
github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
|
||||
|
||||
@@ -25,7 +25,9 @@ spec:
|
||||
- "agent"
|
||||
- "--cluster-name={{ .ClusterName }}"
|
||||
- "--bootstrap-kubeconfig=/spoke/bootstrap/kubeconfig"
|
||||
{{if .ExternalServerURL}}
|
||||
- "--spoke-external-server-urls={{ .ExternalServerURL }}"
|
||||
{{end}}
|
||||
volumeMounts:
|
||||
- name: bootstrap-secret
|
||||
mountPath: "/spoke/bootstrap"
|
||||
|
||||
@@ -150,7 +150,9 @@ spec:
|
||||
- "agent"
|
||||
- "--cluster-name={{ .ClusterName }}"
|
||||
- "--bootstrap-kubeconfig=/spoke/bootstrap/kubeconfig"
|
||||
{{if .ExternalServerURL}}
|
||||
- "--spoke-external-server-urls={{ .ExternalServerURL }}"
|
||||
{{end}}
|
||||
volumeMounts:
|
||||
- name: bootstrap-secret
|
||||
mountPath: "/spoke/bootstrap"
|
||||
|
||||
@@ -251,6 +251,14 @@ func (n *klusterletController) sync(ctx context.Context, controllerContext facto
|
||||
// TODO store this in the status of the klusterlet itself
|
||||
n.registrationGeneration = generation
|
||||
|
||||
// If cluster name is empty, read cluster name from hub config secret
|
||||
if config.ClusterName == "" {
|
||||
clusterName := hubSecret.Data["cluster-name"]
|
||||
if clusterName != nil {
|
||||
config.ClusterName = string(clusterName)
|
||||
}
|
||||
}
|
||||
|
||||
// Deploy work agent
|
||||
generation, err = helpers.ApplyDeployment(
|
||||
n.kubeClient,
|
||||
@@ -281,24 +289,20 @@ func (n *klusterletController) sync(ctx context.Context, controllerContext facto
|
||||
// TODO this should be moved into a separate loop since it is independent of the application of the eventually consistent
|
||||
// resources above
|
||||
|
||||
// If cluster name is empty, read cluster name from hub config secret
|
||||
// If cluster name is empty, return err
|
||||
if config.ClusterName == "" {
|
||||
clusterName := hubSecret.Data["cluster-name"]
|
||||
if clusterName == nil {
|
||||
helpers.UpdateKlusterletStatus(ctx, n.klusterletClient, klusterletName, helpers.UpdateKlusterletConditionFn(operatorapiv1.StatusCondition{
|
||||
Type: klusterletRegistrationDegraded, Status: metav1.ConditionTrue, Reason: "ClusterNameMissing",
|
||||
Message: fmt.Sprintf("Failed to get cluster name from `kubectl get secret -n %q %q -ojsonpath='{.data.cluster-name}`. This is set by the klusterlet registration deployment.", hubSecret.Namespace, hubSecret.Name),
|
||||
}))
|
||||
return fmt.Errorf("Failed to get cluster name")
|
||||
}
|
||||
config.ClusterName = string(clusterName)
|
||||
helpers.UpdateKlusterletStatus(ctx, n.klusterletClient, klusterletName, helpers.UpdateKlusterletConditionFn(operatorapiv1.StatusCondition{
|
||||
Type: klusterletRegistrationDegraded, Status: metav1.ConditionTrue, Reason: "ClusterNameMissing",
|
||||
Message: fmt.Sprintf("Failed to get cluster name from `kubectl get secret -n %q %q -ojsonpath='{.data.cluster-name}`. This is set by the klusterlet registration deployment.", hubSecret.Namespace, hubSecret.Name),
|
||||
}))
|
||||
return fmt.Errorf("Failed to get cluster name")
|
||||
}
|
||||
|
||||
// If hub kubeconfig does not exist, return err.
|
||||
if hubSecret.Data["kubeconfig"] == nil {
|
||||
helpers.UpdateKlusterletStatus(ctx, n.klusterletClient, klusterletName, helpers.UpdateKlusterletConditionFn(operatorapiv1.StatusCondition{
|
||||
Type: klusterletRegistrationDegraded, Status: metav1.ConditionTrue, Reason: "HubKubeconfigMissing",
|
||||
Message: fmt.Sprintf("Failed to get cluster name from `kubectl get secret -n %q %q -ojsonpath='{.data.kubeconfig}`. This is set by the klusterlet registration deployment, but the CSR must be approved by the cluster-admin on the hub.", hubSecret.Namespace, hubSecret.Name),
|
||||
Message: fmt.Sprintf("Failed to get kubeconfig from `kubectl get secret -n %q %q -ojsonpath='{.data.kubeconfig}`. This is set by the klusterlet registration deployment, but the CSR must be approved by the cluster-admin on the hub.", hubSecret.Namespace, hubSecret.Name),
|
||||
}))
|
||||
return fmt.Errorf("Failed to get kubeconfig from hub kubeconfig secret")
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
fakekube "k8s.io/client-go/kubernetes/fake"
|
||||
clienttesting "k8s.io/client-go/testing"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
)
|
||||
|
||||
@@ -29,6 +30,7 @@ type testController struct {
|
||||
controller *klusterletController
|
||||
kubeClient *fakekube.Clientset
|
||||
operatorClient *fakeoperatorclient.Clientset
|
||||
operatorStore cache.Store
|
||||
}
|
||||
|
||||
type fakeSyncContext struct {
|
||||
@@ -101,6 +103,7 @@ func newTestController(klusterlet *opratorapiv1.Klusterlet, objects ...runtime.O
|
||||
controller: hubController,
|
||||
kubeClient: fakeKubeClient,
|
||||
operatorClient: fakeOperatorClient,
|
||||
operatorStore: store,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,6 +157,95 @@ func ensureNameNamespace(t *testing.T, actualName, actualNamespace, name, namesp
|
||||
}
|
||||
}
|
||||
|
||||
func ensureDeployments(t *testing.T, actions []clienttesting.Action, verb, serverURL, registrationClusterName, workClusterName string) {
|
||||
deployments := []*appsv1.Deployment{}
|
||||
for _, action := range actions {
|
||||
if action.GetVerb() != verb || action.GetResource().Resource != "deployments" {
|
||||
continue
|
||||
}
|
||||
|
||||
if verb == "create" {
|
||||
object := action.(clienttesting.CreateActionImpl).Object
|
||||
deployments = append(deployments, object.(*appsv1.Deployment))
|
||||
}
|
||||
|
||||
if verb == "update" {
|
||||
object := action.(clienttesting.UpdateActionImpl).Object
|
||||
deployments = append(deployments, object.(*appsv1.Deployment))
|
||||
}
|
||||
}
|
||||
|
||||
if len(deployments) != 2 {
|
||||
t.Errorf("Expect %s 2 deployment, actual %d", verb, len(deployments))
|
||||
}
|
||||
|
||||
for _, deployment := range deployments {
|
||||
if strings.HasSuffix(deployment.Name, "registration-agent") {
|
||||
ensureRegistrationDeployment(t, deployment, serverURL, registrationClusterName)
|
||||
} else if strings.HasSuffix(deployment.Name, "work-agent") {
|
||||
ensureWorkDeployment(t, deployment, workClusterName)
|
||||
} else {
|
||||
t.Errorf("Unexpected deployment name %s", deployment.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ensureRegistrationDeployment(t *testing.T, deployment *appsv1.Deployment, serverURL, clusterName string) {
|
||||
if len(deployment.Spec.Template.Spec.Containers) != 1 {
|
||||
t.Errorf("Expect 1 containers in deployment spec, actual %d", len(deployment.Spec.Template.Spec.Containers))
|
||||
}
|
||||
args := deployment.Spec.Template.Spec.Containers[0].Args
|
||||
if serverURL == "" && len(args) != 4 {
|
||||
t.Errorf("Expect 4 args in container spec, actual %d", len(args))
|
||||
}
|
||||
if serverURL != "" && len(deployment.Spec.Template.Spec.Containers[0].Args) != 5 {
|
||||
t.Errorf("Expect 5 args in container spec, actual %d", len(args))
|
||||
}
|
||||
clusterNameArg := ""
|
||||
serverURLArg := ""
|
||||
for _, arg := range args {
|
||||
if strings.HasPrefix(arg, "--cluster-name=") {
|
||||
clusterNameArg = arg
|
||||
}
|
||||
if strings.HasPrefix(arg, "--spoke-external-server-urls=") {
|
||||
serverURLArg = arg
|
||||
}
|
||||
}
|
||||
|
||||
desiredServerURLArg := ""
|
||||
if serverURL != "" {
|
||||
desiredServerURLArg = fmt.Sprintf("--spoke-external-server-urls=%s", serverURL)
|
||||
}
|
||||
if serverURLArg != desiredServerURLArg {
|
||||
t.Errorf("Server url args not correct, expect %q, actual %q", desiredServerURLArg, serverURLArg)
|
||||
}
|
||||
|
||||
desiredClusterNameArg := fmt.Sprintf("--cluster-name=%s", clusterName)
|
||||
if clusterNameArg != desiredClusterNameArg {
|
||||
t.Errorf("Cluster name arg not correct, expect %q, actual %q", desiredClusterNameArg, clusterNameArg)
|
||||
}
|
||||
}
|
||||
|
||||
func ensureWorkDeployment(t *testing.T, deployment *appsv1.Deployment, clusterName string) {
|
||||
if len(deployment.Spec.Template.Spec.Containers) != 1 {
|
||||
t.Errorf("Expect 1 containers in deployment spec, actual %d", len(deployment.Spec.Template.Spec.Containers))
|
||||
}
|
||||
args := deployment.Spec.Template.Spec.Containers[0].Args
|
||||
if len(args) != 4 {
|
||||
t.Errorf("Expect 4 args in container spec, actual %d", len(args))
|
||||
}
|
||||
clusterNameArg := ""
|
||||
for _, arg := range args {
|
||||
if strings.HasPrefix(arg, "--spoke-cluster-name") {
|
||||
clusterNameArg = arg
|
||||
}
|
||||
}
|
||||
desiredClusterNameArg := fmt.Sprintf("--spoke-cluster-name=%s", clusterName)
|
||||
if desiredClusterNameArg != clusterNameArg {
|
||||
t.Errorf("Expect cluster namee arg is %q, actual %q", desiredClusterNameArg, clusterNameArg)
|
||||
}
|
||||
}
|
||||
|
||||
func ensureObject(t *testing.T, object runtime.Object, klusterlet *opratorapiv1.Klusterlet) {
|
||||
access, err := meta.Accessor(object)
|
||||
if err != nil {
|
||||
@@ -390,3 +482,75 @@ func TestGetServersFromKlusterlet(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClusterNameChange(t *testing.T) {
|
||||
klusterlet := newKlusterlet("klusterlet", "testns", "cluster1")
|
||||
namespace := newNamespace("testns")
|
||||
bootStrapSecret := newSecret(bootstrapHubKubeConfigSecret, "testns")
|
||||
hubSecret := newSecret(hubKubeConfigSecret, "testns")
|
||||
hubSecret.Data["kubeconfig"] = []byte("dummuykubeconnfig")
|
||||
hubSecret.Data["cluster-name"] = []byte("cluster1")
|
||||
controller := newTestController(klusterlet, bootStrapSecret, hubSecret, namespace)
|
||||
syncContext := newFakeSyncContext(t, "klusterlet")
|
||||
err := controller.controller.sync(nil, syncContext)
|
||||
if err != nil {
|
||||
t.Errorf("Expected non error when sync, %v", err)
|
||||
}
|
||||
|
||||
// Check if deployment has the right cluster name set
|
||||
ensureDeployments(t, controller.kubeClient.Actions(), "create", "", "cluster1", "cluster1")
|
||||
|
||||
// Update klusterlet with unset cluster name and rerun sync
|
||||
controller.kubeClient.ClearActions()
|
||||
klusterlet = newKlusterlet("klusterlet", "testns", "")
|
||||
controller.operatorStore.Update(klusterlet)
|
||||
// Set generation to another number so we can force the update, we will read generatioin from status in the future
|
||||
controller.controller.registrationGeneration = 100
|
||||
controller.controller.workGeneration = 100
|
||||
|
||||
err = controller.controller.sync(nil, syncContext)
|
||||
if err != nil {
|
||||
t.Errorf("Expected non error when sync, %v", err)
|
||||
}
|
||||
ensureDeployments(t, controller.kubeClient.Actions(), "update", "", "", "cluster1")
|
||||
|
||||
// Update hubconfigsecret and sync again
|
||||
hubSecret.Data["cluster-name"] = []byte("cluster2")
|
||||
controller.kubeClient.PrependReactor("get", "secrets", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
if action.GetVerb() != "get" {
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
getAction := action.(clienttesting.GetActionImpl)
|
||||
if getAction.Name != hubKubeConfigSecret {
|
||||
return false, nil, errors.NewNotFound(
|
||||
corev1.Resource("secrets"), hubKubeConfigSecret)
|
||||
}
|
||||
return true, hubSecret, nil
|
||||
})
|
||||
controller.kubeClient.ClearActions()
|
||||
// Set generation to another number so we can force the update, we will read generatioin from status in the future
|
||||
controller.controller.registrationGeneration = 100
|
||||
controller.controller.workGeneration = 100
|
||||
|
||||
err = controller.controller.sync(nil, syncContext)
|
||||
if err != nil {
|
||||
t.Errorf("Expected non error when sync, %v", err)
|
||||
}
|
||||
ensureDeployments(t, controller.kubeClient.Actions(), "update", "", "", "cluster2")
|
||||
|
||||
// Update klusterlet with different cluster name and rerun sync
|
||||
klusterlet = newKlusterlet("klusterlet", "testns", "cluster3")
|
||||
klusterlet.Spec.ExternalServerURLs = []opratorapiv1.ServerURL{{URL: "https://localhost"}}
|
||||
controller.kubeClient.ClearActions()
|
||||
controller.operatorStore.Update(klusterlet)
|
||||
// Set generation to another number so we can force the update, we will read generatioin from status in the future
|
||||
controller.controller.registrationGeneration = 100
|
||||
controller.controller.workGeneration = 100
|
||||
|
||||
err = controller.controller.sync(nil, syncContext)
|
||||
if err != nil {
|
||||
t.Errorf("Expected non error when sync, %v", err)
|
||||
}
|
||||
ensureDeployments(t, controller.kubeClient.Actions(), "update", "https://localhost", "cluster3", "cluster3")
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func startHubOperator(ctx context.Context) {
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
}
|
||||
|
||||
var _ = ginkgo.Describe("HubCore", func() {
|
||||
var _ = ginkgo.Describe("ClusterManager", func() {
|
||||
var cancel context.CancelFunc
|
||||
var err error
|
||||
var clusterManagerName string
|
||||
@@ -168,6 +168,8 @@ var _ = ginkgo.Describe("HubCore", func() {
|
||||
return true
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
|
||||
util.AssertClusterManagerCondition(clusterManagerName, operatorClient, "Applied", metav1.ConditionTrue, eventuallyTimeout, eventuallyInterval)
|
||||
|
||||
err := operatorClient.OperatorV1().ClusterManagers().Delete(context.Background(), clusterManagerName, metav1.DeleteOptions{})
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
|
||||
225
test/integration/klusterlet_test.go
Normal file
225
test/integration/klusterlet_test.go
Normal file
@@ -0,0 +1,225 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/onsi/ginkgo"
|
||||
"github.com/onsi/gomega"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/rand"
|
||||
|
||||
"github.com/openshift/library-go/pkg/controller/controllercmd"
|
||||
|
||||
operatorapiv1 "github.com/open-cluster-management/api/operator/v1"
|
||||
"github.com/open-cluster-management/registration-operator/pkg/operators"
|
||||
"github.com/open-cluster-management/registration-operator/test/integration/util"
|
||||
)
|
||||
|
||||
func startKlusterletOperator(ctx context.Context) {
|
||||
err := operators.RunKlusterletOperator(ctx, &controllercmd.ControllerContext{
|
||||
KubeConfig: restConfig,
|
||||
EventRecorder: util.NewIntegrationTestEventRecorder("integration"),
|
||||
})
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
}
|
||||
|
||||
var _ = ginkgo.Describe("Klusterlet", func() {
|
||||
var cancel context.CancelFunc
|
||||
var klusterlet *operatorapiv1.Klusterlet
|
||||
var klusterletNamespace string
|
||||
var registrationRoleName string
|
||||
var registrationDeploymentName string
|
||||
var registrationSAName string
|
||||
var workRoleName string
|
||||
var workDeploymentName string
|
||||
var workSAName string
|
||||
|
||||
ginkgo.BeforeEach(func() {
|
||||
var ctx context.Context
|
||||
|
||||
klusterletNamespace = fmt.Sprintf("open-cluster-manager-%s", rand.String(6))
|
||||
ns := &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: klusterletNamespace,
|
||||
},
|
||||
}
|
||||
_, err := kubeClient.CoreV1().Namespaces().Create(context.Background(), ns, metav1.CreateOptions{})
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
|
||||
// Create a dummy bootstrap secret
|
||||
bootStrapSecret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "bootstrap-hub-kubeconfig",
|
||||
Namespace: klusterletNamespace,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"kubeconfig": []byte("dummy"),
|
||||
},
|
||||
}
|
||||
_, err = kubeClient.CoreV1().Secrets(klusterletNamespace).Create(context.Background(), bootStrapSecret, metav1.CreateOptions{})
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
|
||||
klusterlet = &operatorapiv1.Klusterlet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("klusterlet-%s", rand.String(6)),
|
||||
},
|
||||
Spec: operatorapiv1.KlusterletSpec{
|
||||
RegistrationImagePullSpec: "quay.io/open-cluster-management/registration",
|
||||
WorkImagePullSpec: "quay.io/open-cluster-management/work",
|
||||
ExternalServerURLs: []operatorapiv1.ServerURL{
|
||||
{
|
||||
URL: "https://localhost",
|
||||
},
|
||||
},
|
||||
ClusterName: "testcluster",
|
||||
Namespace: klusterletNamespace,
|
||||
},
|
||||
}
|
||||
|
||||
ctx, cancel = context.WithCancel(context.Background())
|
||||
go startKlusterletOperator(ctx)
|
||||
})
|
||||
|
||||
ginkgo.AfterEach(func() {
|
||||
err := kubeClient.CoreV1().Namespaces().Delete(context.Background(), klusterletNamespace, metav1.DeleteOptions{})
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
|
||||
if cancel != nil {
|
||||
cancel()
|
||||
}
|
||||
})
|
||||
|
||||
ginkgo.Context("Deploy and clean klusterlet component", func() {
|
||||
ginkgo.BeforeEach(func() {
|
||||
registrationDeploymentName = fmt.Sprintf("%s-registration-agent", klusterlet.Name)
|
||||
workDeploymentName = fmt.Sprintf("%s-work-agent", klusterlet.Name)
|
||||
registrationRoleName = fmt.Sprintf("system:open-cluster-management:%s", registrationDeploymentName)
|
||||
workRoleName = fmt.Sprintf("system:open-cluster-management:%s", workDeploymentName)
|
||||
registrationSAName = fmt.Sprintf("%s-registration-sa", klusterlet.Name)
|
||||
workSAName = fmt.Sprintf("%s-work-sa", klusterlet.Name)
|
||||
})
|
||||
|
||||
ginkgo.It("should have expected resource created successfully", func() {
|
||||
_, err := operatorClient.OperatorV1().Klusterlets().Create(context.Background(), klusterlet, metav1.CreateOptions{})
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
|
||||
// Check clusterrole/clusterrolebinding
|
||||
gomega.Eventually(func() bool {
|
||||
if _, err := kubeClient.RbacV1().ClusterRoles().Get(context.Background(), registrationRoleName, metav1.GetOptions{}); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
gomega.Eventually(func() bool {
|
||||
if _, err := kubeClient.RbacV1().ClusterRoles().Get(context.Background(), workRoleName, metav1.GetOptions{}); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
gomega.Eventually(func() bool {
|
||||
if _, err := kubeClient.RbacV1().ClusterRoleBindings().Get(context.Background(), registrationRoleName, metav1.GetOptions{}); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
gomega.Eventually(func() bool {
|
||||
if _, err := kubeClient.RbacV1().ClusterRoleBindings().Get(context.Background(), workRoleName, metav1.GetOptions{}); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
|
||||
// Check role/rolebinding
|
||||
gomega.Eventually(func() bool {
|
||||
if _, err := kubeClient.RbacV1().Roles(klusterletNamespace).Get(context.Background(), registrationRoleName, metav1.GetOptions{}); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
gomega.Eventually(func() bool {
|
||||
if _, err := kubeClient.RbacV1().RoleBindings(klusterletNamespace).Get(context.Background(), registrationRoleName, metav1.GetOptions{}); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
|
||||
// Check service account
|
||||
gomega.Eventually(func() bool {
|
||||
if _, err := kubeClient.CoreV1().ServiceAccounts(klusterletNamespace).Get(context.Background(), registrationSAName, metav1.GetOptions{}); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
gomega.Eventually(func() bool {
|
||||
if _, err := kubeClient.CoreV1().ServiceAccounts(klusterletNamespace).Get(context.Background(), workSAName, metav1.GetOptions{}); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
|
||||
// Check deployment
|
||||
gomega.Eventually(func() bool {
|
||||
if _, err := kubeClient.AppsV1().Deployments(klusterletNamespace).Get(context.Background(), registrationDeploymentName, metav1.GetOptions{}); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
gomega.Eventually(func() bool {
|
||||
if _, err := kubeClient.AppsV1().Deployments(klusterletNamespace).Get(context.Background(), workDeploymentName, metav1.GetOptions{}); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
|
||||
util.AssertKlusterletCondition(klusterlet.Name, operatorClient, "Applied", metav1.ConditionTrue, eventuallyTimeout, eventuallyInterval)
|
||||
})
|
||||
|
||||
ginkgo.It("should have correct registration deployment when server url is empty", func() {
|
||||
klusterlet.Spec.ExternalServerURLs = []operatorapiv1.ServerURL{}
|
||||
_, err := operatorClient.OperatorV1().Klusterlets().Create(context.Background(), klusterlet, metav1.CreateOptions{})
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
|
||||
gomega.Eventually(func() bool {
|
||||
if _, err := kubeClient.AppsV1().Deployments(klusterletNamespace).Get(context.Background(), registrationDeploymentName, metav1.GetOptions{}); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
|
||||
deployment, err := kubeClient.AppsV1().Deployments(klusterletNamespace).Get(context.Background(), registrationDeploymentName, metav1.GetOptions{})
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
gomega.Expect(len(deployment.Spec.Template.Spec.Containers)).Should(gomega.Equal(1))
|
||||
// external-server-url should not be set
|
||||
for _, arg := range deployment.Spec.Template.Spec.Containers[0].Args {
|
||||
gomega.Expect(strings.Contains(arg, "--spoke-external-server-urls")).NotTo(gomega.BeTrue())
|
||||
}
|
||||
})
|
||||
|
||||
ginkgo.It("should have correct work deployment when clusterName is empty", func() {
|
||||
klusterlet.Spec.ClusterName = ""
|
||||
_, err := operatorClient.OperatorV1().Klusterlets().Create(context.Background(), klusterlet, metav1.CreateOptions{})
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
|
||||
gomega.Eventually(func() bool {
|
||||
if _, err := kubeClient.AppsV1().Deployments(klusterletNamespace).Get(context.Background(), workDeploymentName, metav1.GetOptions{}); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
|
||||
deployment, err := kubeClient.AppsV1().Deployments(klusterletNamespace).Get(context.Background(), workDeploymentName, metav1.GetOptions{})
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
gomega.Expect(len(deployment.Spec.Template.Spec.Containers)).Should(gomega.Equal(1))
|
||||
|
||||
for _, arg := range deployment.Spec.Template.Spec.Containers[0].Args {
|
||||
if strings.HasPrefix(arg, "--spoke-cluster-name") {
|
||||
gomega.Expect(arg).Should(gomega.Equal("--spoke-cluster-name="))
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
37
test/integration/util/assertion.go
Normal file
37
test/integration/util/assertion.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/onsi/gomega"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
operatorclientset "github.com/open-cluster-management/api/client/operator/clientset/versioned"
|
||||
)
|
||||
|
||||
func AssertKlusterletCondition(
|
||||
name string, operatorClient operatorclientset.Interface, expectedType string, expectedWorkStatus metav1.ConditionStatus, eventuallyTimeout, eventuallyInterval int) {
|
||||
gomega.Eventually(func() bool {
|
||||
klusterlet, err := operatorClient.OperatorV1().Klusterlets().Get(context.Background(), name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// check work status condition
|
||||
return HasCondition(klusterlet.Status.Conditions, expectedType, expectedWorkStatus)
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
}
|
||||
|
||||
func AssertClusterManagerCondition(
|
||||
name string, operatorClient operatorclientset.Interface, expectedType string, expectedWorkStatus metav1.ConditionStatus, eventuallyTimeout, eventuallyInterval int) {
|
||||
gomega.Eventually(func() bool {
|
||||
klusterlet, err := operatorClient.OperatorV1().ClusterManagers().Get(context.Background(), name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// check work status condition
|
||||
return HasCondition(klusterlet.Status.Conditions, expectedType, expectedWorkStatus)
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
}
|
||||
@@ -6,6 +6,9 @@ import (
|
||||
"github.com/onsi/ginkgo"
|
||||
|
||||
"github.com/openshift/library-go/pkg/operator/events"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
operatorapiv1 "github.com/open-cluster-management/api/operator/v1"
|
||||
)
|
||||
|
||||
func NewIntegrationTestEventRecorder(componet string) events.Recorder {
|
||||
@@ -43,3 +46,20 @@ func (r *IntegrationTestEventRecorder) Warning(reason, message string) {
|
||||
func (r *IntegrationTestEventRecorder) Warningf(reason, messageFmt string, args ...interface{}) {
|
||||
r.Warning(reason, fmt.Sprintf(messageFmt, args...))
|
||||
}
|
||||
|
||||
func HasCondition(conditions []operatorapiv1.StatusCondition, expectedType string, expectedStatus metav1.ConditionStatus) bool {
|
||||
found := false
|
||||
for _, condition := range conditions {
|
||||
if condition.Type != expectedType {
|
||||
continue
|
||||
}
|
||||
found = true
|
||||
|
||||
if condition.Status != expectedStatus {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user