Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions pkg/webhook/fleetresourcehandler/fleetresourcehandler_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"

fleetv1alpha1 "go.goms.io/fleet/apis/v1alpha1"
fleetv1beta1 "go.goms.io/fleet/apis/placement/v1beta1"
"go.goms.io/fleet/pkg/webhook/validation"
)

Expand Down Expand Up @@ -48,8 +48,8 @@ func (v *fleetResourceValidator) Handle(ctx context.Context, req admission.Reque
case createCRDGVK():
klog.V(2).InfoS("handling CRD resource", "GVK", createCRDGVK())
response = v.handleCRD(req)
case createMemberClusterGVK():
klog.V(2).InfoS("handling Member cluster resource", "GVK", createMemberClusterGVK())
case createV1beta1MemberClusterGVK():
klog.V(2).InfoS("handling Member cluster resource", "GVK", createV1beta1MemberClusterGVK())
response = v.handleMemberCluster(ctx, req)
default:
klog.V(2).InfoS("resource is not monitored by fleet resource validator webhook", "GVK", req.Kind.String())
Expand All @@ -74,11 +74,10 @@ func (v *fleetResourceValidator) handleCRD(req admission.Request) admission.Resp
}

func (v *fleetResourceValidator) handleMemberCluster(ctx context.Context, req admission.Request) admission.Response {
var mc fleetv1alpha1.MemberCluster
var mc fleetv1beta1.MemberCluster
if err := v.decodeRequestObject(req, &mc); err != nil {
return admission.Errored(http.StatusBadRequest, err)
}

if !validation.ValidateUserForFleetCR(ctx, v.client, v.whiteListedUsers, req.UserInfo) {
return admission.Denied(fmt.Sprintf("failed to validate user: %s in groups: %v to modify member cluster CR: %s", req.UserInfo.Username, req.UserInfo.Groups, mc.Name))
}
Expand Down Expand Up @@ -115,10 +114,10 @@ func createCRDGVK() metav1.GroupVersionKind {
}
}

func createMemberClusterGVK() metav1.GroupVersionKind {
func createV1beta1MemberClusterGVK() metav1.GroupVersionKind {
return metav1.GroupVersionKind{
Group: fleetv1alpha1.GroupVersion.Group,
Version: fleetv1alpha1.GroupVersion.Version,
Group: fleetv1beta1.GroupVersion.Group,
Version: fleetv1beta1.GroupVersion.Version,
Kind: memberClusterKind,
}
}
9 changes: 5 additions & 4 deletions pkg/webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/manager"

fleetv1beta1 "go.goms.io/fleet/apis/placement/v1beta1"
fleetv1alpha1 "go.goms.io/fleet/apis/v1alpha1"
"go.goms.io/fleet/cmd/hubagent/options"
"go.goms.io/fleet/pkg/webhook/clusterresourceplacement"
Expand Down Expand Up @@ -214,7 +215,7 @@ func (w *Config) createFleetWebhookConfiguration(ctx context.Context) error {
},
{
Name: "fleet.membercluster.validating",
ClientConfig: w.createClientConfig(fleetv1alpha1.MemberCluster{}),
ClientConfig: w.createClientConfig(fleetv1beta1.MemberCluster{}),
FailurePolicy: &failPolicy,
SideEffects: &sideEffortsNone,
AdmissionReviewVersions: admissionReviewVersions,
Expand All @@ -226,8 +227,8 @@ func (w *Config) createFleetWebhookConfiguration(ctx context.Context) error {
admv1.Delete,
},
Rule: admv1.Rule{
APIGroups: []string{fleetv1alpha1.GroupVersion.Group},
APIVersions: []string{fleetv1alpha1.GroupVersion.Version},
APIGroups: []string{fleetv1beta1.GroupVersion.Group},
APIVersions: []string{fleetv1beta1.GroupVersion.Version},
Resources: []string{memberClusterResourceName},
Scope: &clusterScope,
},
Expand Down Expand Up @@ -284,7 +285,7 @@ func (w *Config) createClientConfig(webhookInterface interface{}) admv1.WebhookC
case v1.CustomResourceDefinition:
serviceEndpoint = w.serviceURL + fleetresourcehandler.ValidationPath
serviceRef.Path = pointer.String(fleetresourcehandler.ValidationPath)
case fleetv1alpha1.MemberCluster:
case fleetv1beta1.MemberCluster:
serviceEndpoint = w.serviceURL + fleetresourcehandler.ValidationPath
serviceRef.Path = pointer.String(fleetresourcehandler.ValidationPath)
}
Expand Down
68 changes: 35 additions & 33 deletions test/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ import (
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
workv1alpha1 "sigs.k8s.io/work-api/pkg/apis/v1alpha1"

"go.goms.io/fleet/apis/v1alpha1"
fleetv1beta1 "go.goms.io/fleet/apis/placement/v1beta1"
fleetv1alpha1 "go.goms.io/fleet/apis/v1alpha1"
"go.goms.io/fleet/pkg/utils"
"go.goms.io/fleet/test/e2e/framework"
testutils "go.goms.io/fleet/test/e2e/utils"
Expand All @@ -39,8 +40,8 @@ var (
MemberCluster = framework.NewCluster(memberClusterName, scheme)
hubURL string
scheme = runtime.NewScheme()
mc *v1alpha1.MemberCluster
imc *v1alpha1.InternalMemberCluster
mc *fleetv1alpha1.MemberCluster
imc *fleetv1alpha1.InternalMemberCluster
ctx context.Context

// The fleet-system namespace.
Expand Down Expand Up @@ -73,52 +74,52 @@ var (

sortOption = cmpopts.SortSlices(func(ref1, ref2 metav1.Condition) bool { return ref1.Type < ref2.Type })
imcStatusCmpOptions = []cmp.Option{
cmpopts.IgnoreTypes(v1alpha1.ResourceUsage{}),
cmpopts.IgnoreTypes(fleetv1alpha1.ResourceUsage{}),
cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime", "ObservedGeneration"),
cmpopts.IgnoreFields(v1alpha1.AgentStatus{}, "LastReceivedHeartbeat"),
cmpopts.IgnoreFields(fleetv1alpha1.AgentStatus{}, "LastReceivedHeartbeat"),
sortOption,
}
mcStatusCmpOptions = []cmp.Option{
cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime", "ObservedGeneration"),
cmpopts.IgnoreFields(v1alpha1.AgentStatus{}, "LastReceivedHeartbeat"),
cmpopts.IgnoreFields(v1alpha1.ResourceUsage{}, "ObservationTime"),
cmpopts.IgnoreFields(fleetv1alpha1.AgentStatus{}, "LastReceivedHeartbeat"),
cmpopts.IgnoreFields(fleetv1alpha1.ResourceUsage{}, "ObservationTime"),
sortOption,
}
crpStatusCmpOptions = []cmp.Option{
cmpopts.IgnoreFields(metav1.Condition{}, "ObservedGeneration", "LastTransitionTime", "Message"),
sortOption,
}

imcJoinedAgentStatus = []v1alpha1.AgentStatus{
imcJoinedAgentStatus = []fleetv1alpha1.AgentStatus{
{
Type: v1alpha1.MemberAgent,
Type: fleetv1alpha1.MemberAgent,
Conditions: []metav1.Condition{
{
Reason: "InternalMemberClusterHealthy",
Status: metav1.ConditionTrue,
Type: string(v1alpha1.AgentHealthy),
Type: string(fleetv1alpha1.AgentHealthy),
},
{
Reason: "InternalMemberClusterJoined",
Status: metav1.ConditionTrue,
Type: string(v1alpha1.AgentJoined),
Type: string(fleetv1alpha1.AgentJoined),
},
},
},
}
imcLeftAgentStatus = []v1alpha1.AgentStatus{
imcLeftAgentStatus = []fleetv1alpha1.AgentStatus{
{
Type: v1alpha1.MemberAgent,
Type: fleetv1alpha1.MemberAgent,
Conditions: []metav1.Condition{
{
Reason: "InternalMemberClusterHealthy",
Status: metav1.ConditionTrue,
Type: string(v1alpha1.AgentHealthy),
Type: string(fleetv1alpha1.AgentHealthy),
},
{
Reason: "InternalMemberClusterLeft",
Status: metav1.ConditionFalse,
Type: string(v1alpha1.AgentJoined),
Type: string(fleetv1alpha1.AgentJoined),
},
},
},
Expand All @@ -128,25 +129,25 @@ var (
{
Reason: "MemberClusterReadyToJoin",
Status: metav1.ConditionTrue,
Type: string(v1alpha1.ConditionTypeMemberClusterReadyToJoin),
Type: string(fleetv1alpha1.ConditionTypeMemberClusterReadyToJoin),
},
{
Reason: "MemberClusterJoined",
Status: metav1.ConditionTrue,
Type: string(v1alpha1.ConditionTypeMemberClusterJoined),
Type: string(fleetv1alpha1.ConditionTypeMemberClusterJoined),
},
}

mcLeftConditions = []metav1.Condition{
{
Reason: "MemberClusterNotReadyToJoin",
Status: metav1.ConditionFalse,
Type: string(v1alpha1.ConditionTypeMemberClusterReadyToJoin),
Type: string(fleetv1alpha1.ConditionTypeMemberClusterReadyToJoin),
},
{
Reason: "MemberClusterLeft",
Status: metav1.ConditionFalse,
Type: string(v1alpha1.ConditionTypeMemberClusterJoined),
Type: string(fleetv1alpha1.ConditionTypeMemberClusterJoined),
},
}

Expand All @@ -159,7 +160,8 @@ var (

func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(v1alpha1.AddToScheme(scheme))
utilruntime.Must(fleetv1alpha1.AddToScheme(scheme))
utilruntime.Must(fleetv1beta1.AddToScheme(scheme))
utilruntime.Must(workv1alpha1.AddToScheme(scheme))
utilruntime.Must(apiextensionsv1.AddToScheme(scheme))
}
Expand Down Expand Up @@ -190,13 +192,13 @@ var _ = BeforeSuite(func() {
Kind: "ServiceAccount",
Namespace: utils.FleetSystemNamespace,
}
mc = &v1alpha1.MemberCluster{
mc = &fleetv1alpha1.MemberCluster{
ObjectMeta: metav1.ObjectMeta{
Name: MemberCluster.ClusterName,
},
Spec: v1alpha1.MemberClusterSpec{
Spec: fleetv1alpha1.MemberClusterSpec{
Identity: identity,
State: v1alpha1.ClusterStateJoin,
State: fleetv1alpha1.ClusterStateJoin,
HeartbeatPeriodSeconds: 60,
},
}
Expand All @@ -205,7 +207,7 @@ var _ = BeforeSuite(func() {
}, testutils.PollTimeout, testutils.PollInterval).Should(Succeed(), "Failed to wait for member cluster %s to be created in %s cluster", mc.Name, HubCluster.ClusterName)

By("check if internal member cluster created in the hub cluster")
imc = &v1alpha1.InternalMemberCluster{
imc = &fleetv1alpha1.InternalMemberCluster{
ObjectMeta: metav1.ObjectMeta{
Name: MemberCluster.ClusterName,
Namespace: memberNamespace.Name,
Expand All @@ -216,38 +218,38 @@ var _ = BeforeSuite(func() {
}, testutils.PollTimeout, testutils.PollInterval).Should(Succeed(), "Failed to wait for internal member cluster %s to be synced in %s cluster", imc.Name, HubCluster.ClusterName)

By("check if internal member cluster status is updated to Joined")
wantIMCStatus := v1alpha1.InternalMemberClusterStatus{AgentStatus: imcJoinedAgentStatus}
wantIMCStatus := fleetv1alpha1.InternalMemberClusterStatus{AgentStatus: imcJoinedAgentStatus}
testutils.CheckInternalMemberClusterStatus(ctx, *HubCluster, &types.NamespacedName{Name: imc.Name, Namespace: imc.Namespace}, wantIMCStatus, imcStatusCmpOptions)

By("check if member cluster status is updated to Joined")
Expect(HubCluster.KubeClient.Get(ctx, types.NamespacedName{Name: imc.Name, Namespace: imc.Namespace}, imc)).Should(Succeed(), "Failed to retrieve internal member cluster %s in %s cluster", imc.Name, HubCluster.ClusterName)
wantMCStatus := v1alpha1.MemberClusterStatus{
wantMCStatus := fleetv1alpha1.MemberClusterStatus{
AgentStatus: imc.Status.AgentStatus,
Conditions: mcJoinedConditions,
ResourceUsage: imc.Status.ResourceUsage,
}
testutils.CheckMemberClusterStatus(ctx, *HubCluster, &types.NamespacedName{Name: mc.Name}, wantMCStatus, mcStatusCmpOptions)

By("create cluster role binding and cluster roles for webhook e2e")
testutils.CreateClusterRoleAndClusterRoleBindingsForWebHookE2E(ctx, HubCluster)
By("create member cluster, cluster role binding, cluster roles for webhook e2e")
testutils.CreateResourcesForWebHookE2E(ctx, HubCluster)
})

var _ = AfterSuite(func() {
By("delete cluster role binding and cluster roles for webhook e2e")
testutils.DeleteClusterRoleAndClusterRoleBindingForWebHookE2E(ctx, HubCluster)
By("delete member cluster, cluster role binding and cluster roles for webhook e2e")
testutils.DeleteResourcesForWebHookE2E(ctx, HubCluster)

By("update member cluster in the hub cluster")
Expect(HubCluster.KubeClient.Get(ctx, types.NamespacedName{Name: mc.Name}, mc)).Should(Succeed(), "Failed to retrieve member cluster %s in %s cluster", mc.Name, HubCluster.ClusterName)
mc.Spec.State = v1alpha1.ClusterStateLeave
mc.Spec.State = fleetv1alpha1.ClusterStateLeave
Expect(HubCluster.KubeClient.Update(ctx, mc)).Should(Succeed(), "Failed to update member cluster %s in %s cluster", mc.Name, HubCluster.ClusterName)

By("check if internal member cluster status is updated to Left")
wantIMCStatus := v1alpha1.InternalMemberClusterStatus{AgentStatus: imcLeftAgentStatus}
wantIMCStatus := fleetv1alpha1.InternalMemberClusterStatus{AgentStatus: imcLeftAgentStatus}
testutils.CheckInternalMemberClusterStatus(ctx, *HubCluster, &types.NamespacedName{Name: imc.Name, Namespace: imc.Namespace}, wantIMCStatus, imcStatusCmpOptions)

By("check if member cluster status is updated to Left")
Expect(HubCluster.KubeClient.Get(ctx, types.NamespacedName{Name: imc.Name, Namespace: imc.Namespace}, imc)).Should(Succeed(), "Failed to retrieve internal member cluster %s in %s cluster", imc.Name, HubCluster.ClusterName)
wantMCStatus := v1alpha1.MemberClusterStatus{
wantMCStatus := fleetv1alpha1.MemberClusterStatus{
AgentStatus: imc.Status.AgentStatus,
Conditions: mcLeftConditions,
ResourceUsage: imc.Status.ResourceUsage,
Expand Down
43 changes: 36 additions & 7 deletions test/e2e/utils/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"k8s.io/klog/v2"
workapi "sigs.k8s.io/work-api/pkg/apis/v1alpha1"

fleetv1beta1 "go.goms.io/fleet/apis/placement/v1beta1"
fleetv1alpha1 "go.goms.io/fleet/apis/v1alpha1"
"go.goms.io/fleet/pkg/utils"
"go.goms.io/fleet/test/e2e/framework"
Expand Down Expand Up @@ -196,7 +197,27 @@ func GenerateCRDObjectFromFile(cluster framework.Cluster, fs embed.FS, filepath
return obj, gvk, mapping.Resource
}

func CreateClusterRoleAndClusterRoleBindingsForWebHookE2E(ctx context.Context, hubCluster *framework.Cluster) {
func CreateResourcesForWebHookE2E(ctx context.Context, hubCluster *framework.Cluster) {
// create v1beta1 member cluster CR
mc := fleetv1beta1.MemberCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "test-member-cluster",
},
Spec: fleetv1beta1.MemberClusterSpec{
State: fleetv1beta1.ClusterStateJoin,
Identity: rbacv1.Subject{
Kind: "User",
APIGroup: "",
Name: "test-subject",
Namespace: "fleet-system",
},
},
}

gomega.Eventually(func() error {
return hubCluster.KubeClient.Create(ctx, &mc)
}, PollTimeout, PollInterval).Should(gomega.Succeed(), "failed to create member cluster %s", mc.Name)

cr := rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: crdClusterRole,
Expand Down Expand Up @@ -241,7 +262,7 @@ func CreateClusterRoleAndClusterRoleBindingsForWebHookE2E(ctx context.Context, h
},
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{fleetv1alpha1.GroupVersion.Group},
APIGroups: []string{fleetv1beta1.GroupVersion.Group},
Verbs: []string{"*"},
Resources: []string{"*"},
},
Expand Down Expand Up @@ -273,32 +294,40 @@ func CreateClusterRoleAndClusterRoleBindingsForWebHookE2E(ctx context.Context, h
}, PollTimeout, PollInterval).Should(gomega.Succeed(), "failed to create cluster role binding %s to modify CRDs", crb.Name)
}

func DeleteClusterRoleAndClusterRoleBindingForWebHookE2E(ctx context.Context, hubCluster *framework.Cluster) {
func DeleteResourcesForWebHookE2E(ctx context.Context, hubCluster *framework.Cluster) {
crb := rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: crClusterRoleBinding,
},
}
gomega.Expect(hubCluster.KubeClient.Delete(ctx, &crb)).Should(gomega.Succeed())
gomega.Expect(hubCluster.KubeClient.Delete(ctx, &crb)).Should(gomega.Succeed(), "failed to delete CR cluster role binding %s", crb.Name)

cr := rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: crClusterRole,
},
}
gomega.Expect(hubCluster.KubeClient.Delete(ctx, &cr)).Should(gomega.Succeed())
gomega.Expect(hubCluster.KubeClient.Delete(ctx, &cr)).Should(gomega.Succeed(), "failed to delete CR cluster role %s", cr.Name)

crb = rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: crdClusterRoleBinding,
},
}
gomega.Expect(hubCluster.KubeClient.Delete(ctx, &crb)).Should(gomega.Succeed())
gomega.Expect(hubCluster.KubeClient.Delete(ctx, &crb)).Should(gomega.Succeed(), "failed to delete CRD cluster role binding %s", crb.Name)

cr = rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: crdClusterRole,
},
}
gomega.Expect(hubCluster.KubeClient.Delete(ctx, &cr)).Should(gomega.Succeed())
gomega.Expect(hubCluster.KubeClient.Delete(ctx, &cr)).Should(gomega.Succeed(), "failed to delete CRD cluster role %s", cr.Name)

// delete v1beta1 member cluster CR
mc := fleetv1beta1.MemberCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "test-member-cluster",
},
}
gomega.Expect(hubCluster.KubeClient.Delete(ctx, &mc)).Should(gomega.Succeed(), "failed to delete member cluster %s", mc.Name)
}
Loading