diff --git a/pkg/controllers/clusterresourceplacement/resource_selector.go b/pkg/controllers/clusterresourceplacement/resource_selector.go index 542a222dc..38b041b9a 100644 --- a/pkg/controllers/clusterresourceplacement/resource_selector.go +++ b/pkg/controllers/clusterresourceplacement/resource_selector.go @@ -97,9 +97,20 @@ func (r *Reconciler) gatherSelectedResource(placement string, selectors []fleetv } // sort the resources in strict order so that we will get the stable list of manifest so that // the generated work object doesn't change between reconcile loops + sortResources(resources) + + return resources, nil +} +func sortResources(resources []runtime.Object) { sort.Slice(resources, func(i, j int) bool { obj1 := resources[i].DeepCopyObject().(*unstructured.Unstructured) obj2 := resources[j].DeepCopyObject().(*unstructured.Unstructured) + if obj1.GetObjectKind().GroupVersionKind().String() == utils.NamespaceMetaGVK.String() || obj2.GetObjectKind().GroupVersionKind().String() == utils.NamespaceMetaGVK.String() { + return obj1.GetObjectKind().GroupVersionKind().String() == utils.NamespaceMetaGVK.String() + } + if obj1.GetObjectKind().GroupVersionKind().String() == utils.CRDMetaGVK.String() || obj2.GetObjectKind().GroupVersionKind().String() == utils.CRDMetaGVK.String() { + return obj1.GetObjectKind().GroupVersionKind().String() == utils.CRDMetaGVK.String() + } // compare group/version;kind gvkComp := strings.Compare(obj1.GroupVersionKind().String(), obj2.GroupVersionKind().String()) if gvkComp > 0 { @@ -112,7 +123,6 @@ func (r *Reconciler) gatherSelectedResource(placement string, selectors []fleetv return strings.Compare(fmt.Sprintf("%s/%s", obj1.GetNamespace(), obj1.GetName()), fmt.Sprintf("%s/%s", obj2.GetNamespace(), obj2.GetName())) > 0 }) - return resources, nil } // fetchClusterScopedResources retrieves the objects based on the selector. diff --git a/pkg/controllers/clusterresourceplacement/resource_selector_test.go b/pkg/controllers/clusterresourceplacement/resource_selector_test.go index d1139a7d9..0d7921a1d 100644 --- a/pkg/controllers/clusterresourceplacement/resource_selector_test.go +++ b/pkg/controllers/clusterresourceplacement/resource_selector_test.go @@ -794,3 +794,80 @@ func createResourceContentForTest(t *testing.T, obj interface{}) *fleetv1beta1.R }, } } + +func TestSortResource(t *testing.T) { + // Create the Namespace object + namespace := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v1", + "kind": "Namespace", + "metadata": map[string]interface{}{ + "name": "test", + }, + }, + } + + // Create the Deployment object + deployment := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "test-nginx", + "namespace": "test", + }, + }, + } + + // Create the CustomResourceDefinition object + crd := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apiextensions.k8s.io/v1", + "kind": "CustomResourceDefinition", + "metadata": map[string]interface{}{ + "name": "test-crd", + }, + }, + } + + // Create the ClusterRole object + clusterRole := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "ClusterRole", + "metadata": map[string]interface{}{ + "name": "test-clusterrole", + }, + }, + } + + tests := map[string]struct { + resources []runtime.Object + want []runtime.Object + }{ + "should gather selected resources with Namespace in front": { + resources: []runtime.Object{deployment, namespace}, + want: []runtime.Object{namespace, deployment}, + }, + "should gather selected resources with CRD in front": { + resources: []runtime.Object{clusterRole, crd}, + want: []runtime.Object{crd, clusterRole}, + }, + "should gather selected resources with CRD or Namespace in front": { + resources: []runtime.Object{deployment, clusterRole, crd, namespace}, + want: []runtime.Object{namespace, crd, clusterRole, deployment}, + }, + } + + for testName, tt := range tests { + t.Run(testName, func(t *testing.T) { + sortResources(tt.resources) + + // Check that the returned resources match the expected resources + diff := cmp.Diff(tt.want, tt.resources) + if diff != "" { + t.Errorf("sortResources() mismatch (-want +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/utils/common.go b/pkg/utils/common.go index 77fd19d3c..ab5c159a3 100644 --- a/pkg/utils/common.go +++ b/pkg/utils/common.go @@ -15,7 +15,9 @@ import ( corev1 "k8s.io/api/core/v1" discoveryv1 "k8s.io/api/discovery/v1" rbacv1 "k8s.io/api/rbac/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -24,6 +26,7 @@ import ( "k8s.io/klog/v2" workv1alpha1 "sigs.k8s.io/work-api/pkg/apis/v1alpha1" + fleetnetworkingv1alpha1 "go.goms.io/fleet-networking/api/v1alpha1" clusterv1beta1 "go.goms.io/fleet/apis/cluster/v1beta1" placementv1beta1 "go.goms.io/fleet/apis/placement/v1beta1" fleetv1alpha1 "go.goms.io/fleet/apis/v1alpha1" @@ -166,6 +169,102 @@ var ( Version: corev1.SchemeGroupVersion.Version, Kind: "ConfigMap", } + + NamespaceMetaGVK = metav1.GroupVersionKind{ + Group: corev1.GroupName, + Version: corev1.SchemeGroupVersion.Version, + Kind: "Namespace", + } + + RoleMetaGVK = metav1.GroupVersionKind{ + Group: rbacv1.SchemeGroupVersion.Group, + Version: rbacv1.SchemeGroupVersion.Version, + Kind: "Role", + } + + RoleBindingMetaGVK = metav1.GroupVersionKind{ + Group: rbacv1.SchemeGroupVersion.Group, + Version: rbacv1.SchemeGroupVersion.Version, + Kind: "RoleBinding", + } + + PodMetaGVK = metav1.GroupVersionKind{ + Group: corev1.SchemeGroupVersion.Group, + Version: corev1.SchemeGroupVersion.Version, + Kind: "Pod", + } + + CRDMetaGVK = metav1.GroupVersionKind{ + Group: apiextensionsv1.SchemeGroupVersion.Group, + Version: apiextensionsv1.SchemeGroupVersion.Version, + Kind: "CustomResourceDefinition", + } + + V1Alpha1MCMetaGVK = metav1.GroupVersionKind{ + Group: fleetv1alpha1.GroupVersion.Group, + Version: fleetv1alpha1.GroupVersion.Version, + Kind: "MemberCluster", + } + + V1Alpha1IMCMetaGVK = metav1.GroupVersionKind{ + Group: fleetv1alpha1.GroupVersion.Group, + Version: fleetv1alpha1.GroupVersion.Version, + Kind: "InternalMemberCluster", + } + + V1Alpha1WorkMetaGVK = metav1.GroupVersionKind{ + Group: workv1alpha1.GroupVersion.Group, + Version: workv1alpha1.GroupVersion.Version, + Kind: "Work", + } + + MCMetaGVK = metav1.GroupVersionKind{ + Group: clusterv1beta1.GroupVersion.Group, + Version: clusterv1beta1.GroupVersion.Version, + Kind: "MemberCluster", + } + + IMCMetaGVK = metav1.GroupVersionKind{ + Group: clusterv1beta1.GroupVersion.Group, + Version: clusterv1beta1.GroupVersion.Version, + Kind: "InternalMemberCluster", + } + + WorkV1Beta1MetaGVK = metav1.GroupVersionKind{ + Group: placementv1beta1.GroupVersion.Group, + Version: placementv1beta1.GroupVersion.Version, + Kind: "Work", + } + + EventMetaGVK = metav1.GroupVersionKind{ + Group: corev1.SchemeGroupVersion.Group, + Version: corev1.SchemeGroupVersion.Version, + Kind: "Event", + } + + EndpointSliceExportMetaGVK = metav1.GroupVersionKind{ + Group: fleetnetworkingv1alpha1.GroupVersion.Group, + Version: fleetnetworkingv1alpha1.GroupVersion.Version, + Kind: "EndpointSliceExport", + } + + EndpointSliceImportMetaGVK = metav1.GroupVersionKind{ + Group: fleetnetworkingv1alpha1.GroupVersion.Group, + Version: fleetnetworkingv1alpha1.GroupVersion.Version, + Kind: "EndpointSliceImport", + } + + InternalServiceExportMetaGVK = metav1.GroupVersionKind{ + Group: fleetnetworkingv1alpha1.GroupVersion.Group, + Version: fleetnetworkingv1alpha1.GroupVersion.Version, + Kind: "InternalServiceExport", + } + + InternalServiceImportMetaGVK = metav1.GroupVersionKind{ + Group: fleetnetworkingv1alpha1.GroupVersion.Group, + Version: fleetnetworkingv1alpha1.GroupVersion.Version, + Kind: "InternalServiceImport", + } ) // RandSecureInt returns a uniform random value in [1, max] or panic. diff --git a/pkg/webhook/fleetresourcehandler/fleetresourcehandler_webhook.go b/pkg/webhook/fleetresourcehandler/fleetresourcehandler_webhook.go index f140003b6..40db28253 100644 --- a/pkg/webhook/fleetresourcehandler/fleetresourcehandler_webhook.go +++ b/pkg/webhook/fleetresourcehandler/fleetresourcehandler_webhook.go @@ -55,22 +55,22 @@ func (v *fleetResourceValidator) Handle(ctx context.Context, req admission.Reque var response admission.Response if req.Operation == admissionv1.Create || req.Operation == admissionv1.Update || req.Operation == admissionv1.Delete { switch { - case req.Kind == validation.CRDGVK: + case req.Kind == utils.CRDMetaGVK: klog.V(2).InfoS("handling CRD resource", "name", req.Name, "operation", req.Operation, "subResource", req.SubResource) response = v.handleCRD(req) - case req.Kind == validation.V1Alpha1MCGVK: + case req.Kind == utils.V1Alpha1MCMetaGVK: klog.V(2).InfoS("handling v1alpha1 member cluster resource", "name", req.Name, "operation", req.Operation, "subResource", req.SubResource) response = v.handleV1Alpha1MemberCluster(req) - case req.Kind == validation.MCGVK: + case req.Kind == utils.MCMetaGVK: klog.V(2).InfoS("handling member cluster resource", "name", req.Name, "operation", req.Operation, "subResource", req.SubResource) response = v.handleMemberCluster(req) - case req.Kind == validation.NamespaceGVK: + case req.Kind == utils.NamespaceMetaGVK: klog.V(2).InfoS("handling namespace resource", "name", req.Name, "operation", req.Operation, "subResource", req.SubResource) response = v.handleNamespace(req) - case req.Kind == validation.V1Alpha1IMCGVK || req.Kind == validation.V1Alpha1WorkGVK || req.Kind == validation.IMCGVK || req.Kind == validation.WorkGVK || req.Kind == validation.EndpointSliceExportGVK || req.Kind == validation.EndpointSliceImportGVK || req.Kind == validation.InternalServiceExportGVK || req.Kind == validation.InternalServiceImportGVK: + case req.Kind == utils.V1Alpha1IMCMetaGVK || req.Kind == utils.V1Alpha1WorkMetaGVK || req.Kind == utils.IMCMetaGVK || req.Kind == utils.WorkV1Beta1MetaGVK || req.Kind == utils.EndpointSliceExportMetaGVK || req.Kind == utils.EndpointSliceImportMetaGVK || req.Kind == utils.InternalServiceExportMetaGVK || req.Kind == utils.InternalServiceImportMetaGVK: klog.V(2).InfoS("handling fleet owned namespaced resource in fleet reserved namespaces", "GVK", req.RequestKind, "namespacedName", namespacedName, "operation", req.Operation, "subResource", req.SubResource) response = v.handleFleetReservedNamespacedResource(ctx, req) - case req.Kind == validation.EventGVK: + case req.Kind == utils.EventMetaGVK: klog.V(3).InfoS("handling event resource", "namespacedName", namespacedName, "operation", req.Operation, "subResource", req.SubResource) response = v.handleEvent(ctx, req) case req.Namespace != "": diff --git a/pkg/webhook/fleetresourcehandler/fleetresourcehandler_webhook_test.go b/pkg/webhook/fleetresourcehandler/fleetresourcehandler_webhook_test.go index 50225a2a8..1a84e0080 100644 --- a/pkg/webhook/fleetresourcehandler/fleetresourcehandler_webhook_test.go +++ b/pkg/webhook/fleetresourcehandler/fleetresourcehandler_webhook_test.go @@ -43,12 +43,12 @@ func TestHandleCRD(t *testing.T) { Username: "test-user", Groups: []string{"test-group"}, }, - RequestKind: &validation.CRDGVK, + RequestKind: &utils.CRDMetaGVK, Operation: admissionv1.Create, }, }, resourceValidator: fleetResourceValidator{}, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Create, &validation.CRDGVK, "", types.NamespacedName{Name: "test-crd"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Create, &utils.CRDMetaGVK, "", types.NamespacedName{Name: "test-crd"})), }, "allow user in system:masters group to modify fleet CRD": { req: admission.Request{ @@ -58,12 +58,12 @@ func TestHandleCRD(t *testing.T) { Username: "test-user", Groups: []string{"system:masters"}, }, - RequestKind: &validation.CRDGVK, + RequestKind: &utils.CRDMetaGVK, Operation: admissionv1.Update, }, }, resourceValidator: fleetResourceValidator{}, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"system:masters"}), admissionv1.Update, &validation.CRDGVK, "", types.NamespacedName{Name: "memberclusters.fleet.azure.com"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"system:masters"}), admissionv1.Update, &utils.CRDMetaGVK, "", types.NamespacedName{Name: "memberclusters.fleet.azure.com"})), }, "allow white listed user to modify fleet CRD": { req: admission.Request{ @@ -73,14 +73,14 @@ func TestHandleCRD(t *testing.T) { Username: "test-user", Groups: []string{"test-group"}, }, - RequestKind: &validation.CRDGVK, + RequestKind: &utils.CRDMetaGVK, Operation: admissionv1.Delete, }, }, resourceValidator: fleetResourceValidator{ whiteListedUsers: []string{"test-user"}, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Delete, &validation.CRDGVK, "", types.NamespacedName{Name: "memberclusters.fleet.azure.com"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Delete, &utils.CRDMetaGVK, "", types.NamespacedName{Name: "memberclusters.fleet.azure.com"})), }, "deny user not in system:masters group to modify fleet CRD": { req: admission.Request{ @@ -90,11 +90,11 @@ func TestHandleCRD(t *testing.T) { Username: "test-user", Groups: []string{"test-group"}, }, - RequestKind: &validation.CRDGVK, + RequestKind: &utils.CRDMetaGVK, Operation: admissionv1.Create, }, }, - wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Create, &validation.CRDGVK, "", types.NamespacedName{Name: "memberclusters.fleet.azure.com"})), + wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Create, &utils.CRDMetaGVK, "", types.NamespacedName{Name: "memberclusters.fleet.azure.com"})), }, } @@ -195,14 +195,14 @@ func TestHandleV1Alpha1MemberCluster(t *testing.T) { Username: "test-user", Groups: []string{"system:masters"}, }, - RequestKind: &validation.V1Alpha1MCGVK, + RequestKind: &utils.V1Alpha1MCMetaGVK, Operation: admissionv1.Create, }, }, resourceValidator: fleetResourceValidator{ decoder: decoder, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"system:masters"}), admissionv1.Create, &validation.V1Alpha1MCGVK, "", types.NamespacedName{Name: "test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"system:masters"}), admissionv1.Create, &utils.V1Alpha1MCMetaGVK, "", types.NamespacedName{Name: "test-mc"})), }, "allow any user to modify MC labels": { req: admission.Request{ @@ -220,14 +220,14 @@ func TestHandleV1Alpha1MemberCluster(t *testing.T) { Username: "test-user", Groups: []string{"test-group"}, }, - RequestKind: &validation.V1Alpha1MCGVK, + RequestKind: &utils.V1Alpha1MCMetaGVK, Operation: admissionv1.Update, }, }, resourceValidator: fleetResourceValidator{ decoder: decoder, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &validation.V1Alpha1MCGVK, "", types.NamespacedName{Name: "test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &utils.V1Alpha1MCMetaGVK, "", types.NamespacedName{Name: "test-mc"})), }, "allow any user to modify MC annotations": { req: admission.Request{ @@ -245,14 +245,14 @@ func TestHandleV1Alpha1MemberCluster(t *testing.T) { Username: "test-user", Groups: []string{"test-group"}, }, - RequestKind: &validation.V1Alpha1MCGVK, + RequestKind: &utils.V1Alpha1MCMetaGVK, Operation: admissionv1.Update, }, }, resourceValidator: fleetResourceValidator{ decoder: decoder, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &validation.V1Alpha1MCGVK, "", types.NamespacedName{Name: "test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &utils.V1Alpha1MCMetaGVK, "", types.NamespacedName{Name: "test-mc"})), }, "allow system:masters group user to modify MC spec": { req: admission.Request{ @@ -270,14 +270,14 @@ func TestHandleV1Alpha1MemberCluster(t *testing.T) { Username: "test-user", Groups: []string{"system:masters"}, }, - RequestKind: &validation.V1Alpha1MCGVK, + RequestKind: &utils.V1Alpha1MCMetaGVK, Operation: admissionv1.Update, }, }, resourceValidator: fleetResourceValidator{ decoder: decoder, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"system:masters"}), admissionv1.Update, &validation.V1Alpha1MCGVK, "", types.NamespacedName{Name: "test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"system:masters"}), admissionv1.Update, &utils.V1Alpha1MCMetaGVK, "", types.NamespacedName{Name: "test-mc"})), }, "allow system:masters group user to modify MC status": { req: admission.Request{ @@ -295,7 +295,7 @@ func TestHandleV1Alpha1MemberCluster(t *testing.T) { Username: "test-user", Groups: []string{"system:masters"}, }, - RequestKind: &validation.V1Alpha1MCGVK, + RequestKind: &utils.V1Alpha1MCMetaGVK, Operation: admissionv1.Update, SubResource: "status", }, @@ -303,7 +303,7 @@ func TestHandleV1Alpha1MemberCluster(t *testing.T) { resourceValidator: fleetResourceValidator{ decoder: decoder, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"system:masters"}), admissionv1.Update, &validation.V1Alpha1MCGVK, "status", types.NamespacedName{Name: "test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"system:masters"}), admissionv1.Update, &utils.V1Alpha1MCMetaGVK, "status", types.NamespacedName{Name: "test-mc"})), }, "allow whitelisted user to modify MC status": { req: admission.Request{ @@ -321,7 +321,7 @@ func TestHandleV1Alpha1MemberCluster(t *testing.T) { Username: "test-user", Groups: []string{"test-group"}, }, - RequestKind: &validation.V1Alpha1MCGVK, + RequestKind: &utils.V1Alpha1MCMetaGVK, Operation: admissionv1.Update, SubResource: "status", }, @@ -330,7 +330,7 @@ func TestHandleV1Alpha1MemberCluster(t *testing.T) { decoder: decoder, whiteListedUsers: []string{"test-user"}, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &validation.V1Alpha1MCGVK, "status", types.NamespacedName{Name: "test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &utils.V1Alpha1MCMetaGVK, "status", types.NamespacedName{Name: "test-mc"})), }, "deny update of member cluster spec by non system:masters group": { req: admission.Request{ @@ -348,14 +348,14 @@ func TestHandleV1Alpha1MemberCluster(t *testing.T) { Username: "test-user", Groups: []string{"test-group"}, }, - RequestKind: &validation.V1Alpha1MCGVK, + RequestKind: &utils.V1Alpha1MCMetaGVK, Operation: admissionv1.Update, }, }, resourceValidator: fleetResourceValidator{ decoder: decoder, }, - wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &validation.V1Alpha1MCGVK, "", types.NamespacedName{Name: "test-mc"})), + wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &utils.V1Alpha1MCMetaGVK, "", types.NamespacedName{Name: "test-mc"})), }, "deny update of member cluster spec by non whitelisted user ": { req: admission.Request{ @@ -373,7 +373,7 @@ func TestHandleV1Alpha1MemberCluster(t *testing.T) { Username: "test-user", Groups: []string{"test-group"}, }, - RequestKind: &validation.V1Alpha1MCGVK, + RequestKind: &utils.V1Alpha1MCMetaGVK, Operation: admissionv1.Update, }, }, @@ -381,7 +381,7 @@ func TestHandleV1Alpha1MemberCluster(t *testing.T) { decoder: decoder, whiteListedUsers: []string{"test-user1"}, }, - wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &validation.V1Alpha1MCGVK, "", types.NamespacedName{Name: "test-mc"})), + wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &utils.V1Alpha1MCMetaGVK, "", types.NamespacedName{Name: "test-mc"})), }, } @@ -501,14 +501,14 @@ func TestHandleMemberCluster(t *testing.T) { Username: "test-user", Groups: []string{"system:masters"}, }, - RequestKind: &validation.MCGVK, + RequestKind: &utils.MCMetaGVK, Operation: admissionv1.Create, }, }, resourceValidator: fleetResourceValidator{ decoder: decoder, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"system:masters"}), admissionv1.Create, &validation.MCGVK, "", types.NamespacedName{Name: "test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"system:masters"}), admissionv1.Create, &utils.MCMetaGVK, "", types.NamespacedName{Name: "test-mc"})), }, "allow any user to modify MC labels": { req: admission.Request{ @@ -526,14 +526,14 @@ func TestHandleMemberCluster(t *testing.T) { Username: "test-user", Groups: []string{"test-group"}, }, - RequestKind: &validation.MCGVK, + RequestKind: &utils.MCMetaGVK, Operation: admissionv1.Update, }, }, resourceValidator: fleetResourceValidator{ decoder: decoder, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &validation.MCGVK, "", types.NamespacedName{Name: "test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &utils.MCMetaGVK, "", types.NamespacedName{Name: "test-mc"})), }, "allow any user to modify MC annotations": { req: admission.Request{ @@ -551,14 +551,14 @@ func TestHandleMemberCluster(t *testing.T) { Username: "test-user", Groups: []string{"test-group"}, }, - RequestKind: &validation.MCGVK, + RequestKind: &utils.MCMetaGVK, Operation: admissionv1.Update, }, }, resourceValidator: fleetResourceValidator{ decoder: decoder, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &validation.MCGVK, "", types.NamespacedName{Name: "test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &utils.MCMetaGVK, "", types.NamespacedName{Name: "test-mc"})), }, "allow any user to modify MC taints": { req: admission.Request{ @@ -576,14 +576,14 @@ func TestHandleMemberCluster(t *testing.T) { Username: "test-user", Groups: []string{"test-group"}, }, - RequestKind: &validation.MCGVK, + RequestKind: &utils.MCMetaGVK, Operation: admissionv1.Update, }, }, resourceValidator: fleetResourceValidator{ decoder: decoder, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &validation.MCGVK, "", types.NamespacedName{Name: "test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &utils.MCMetaGVK, "", types.NamespacedName{Name: "test-mc"})), }, "allow system:masters group user to modify MC spec": { req: admission.Request{ @@ -601,14 +601,14 @@ func TestHandleMemberCluster(t *testing.T) { Username: "test-user", Groups: []string{"system:masters"}, }, - RequestKind: &validation.MCGVK, + RequestKind: &utils.MCMetaGVK, Operation: admissionv1.Update, }, }, resourceValidator: fleetResourceValidator{ decoder: decoder, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"system:masters"}), admissionv1.Update, &validation.MCGVK, "", types.NamespacedName{Name: "test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"system:masters"}), admissionv1.Update, &utils.MCMetaGVK, "", types.NamespacedName{Name: "test-mc"})), }, "allow system:masters group user to modify MC status": { req: admission.Request{ @@ -626,7 +626,7 @@ func TestHandleMemberCluster(t *testing.T) { Username: "test-user", Groups: []string{"system:masters"}, }, - RequestKind: &validation.MCGVK, + RequestKind: &utils.MCMetaGVK, Operation: admissionv1.Update, SubResource: "status", }, @@ -634,7 +634,7 @@ func TestHandleMemberCluster(t *testing.T) { resourceValidator: fleetResourceValidator{ decoder: decoder, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"system:masters"}), admissionv1.Update, &validation.MCGVK, "status", types.NamespacedName{Name: "test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"system:masters"}), admissionv1.Update, &utils.MCMetaGVK, "status", types.NamespacedName{Name: "test-mc"})), }, "allow whitelisted user to modify MC status": { req: admission.Request{ @@ -652,7 +652,7 @@ func TestHandleMemberCluster(t *testing.T) { Username: "test-user", Groups: []string{"test-group"}, }, - RequestKind: &validation.MCGVK, + RequestKind: &utils.MCMetaGVK, Operation: admissionv1.Update, SubResource: "status", }, @@ -661,7 +661,7 @@ func TestHandleMemberCluster(t *testing.T) { decoder: decoder, whiteListedUsers: []string{"test-user"}, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &validation.MCGVK, "status", types.NamespacedName{Name: "test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &utils.MCMetaGVK, "status", types.NamespacedName{Name: "test-mc"})), }, "deny update of member cluster spec by non system:masters group": { req: admission.Request{ @@ -679,14 +679,14 @@ func TestHandleMemberCluster(t *testing.T) { Username: "test-user", Groups: []string{"test-group"}, }, - RequestKind: &validation.MCGVK, + RequestKind: &utils.MCMetaGVK, Operation: admissionv1.Update, }, }, resourceValidator: fleetResourceValidator{ decoder: decoder, }, - wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &validation.MCGVK, "", types.NamespacedName{Name: "test-mc"})), + wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &utils.MCMetaGVK, "", types.NamespacedName{Name: "test-mc"})), }, "deny update of member cluster spec by non whitelisted user ": { req: admission.Request{ @@ -704,7 +704,7 @@ func TestHandleMemberCluster(t *testing.T) { Username: "test-user", Groups: []string{"test-group"}, }, - RequestKind: &validation.MCGVK, + RequestKind: &utils.MCMetaGVK, Operation: admissionv1.Update, }, }, @@ -712,7 +712,7 @@ func TestHandleMemberCluster(t *testing.T) { decoder: decoder, whiteListedUsers: []string{"test-user1"}, }, - wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &validation.MCGVK, "", types.NamespacedName{Name: "test-mc"})), + wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &utils.MCMetaGVK, "", types.NamespacedName{Name: "test-mc"})), }, } @@ -773,7 +773,7 @@ func TestHandleFleetReservedNamespacedResource(t *testing.T) { AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-mc", Namespace: "test-ns", - RequestKind: &validation.V1Alpha1IMCGVK, + RequestKind: &utils.V1Alpha1IMCMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "testUser", Groups: []string{"testGroup"}, @@ -788,7 +788,7 @@ func TestHandleFleetReservedNamespacedResource(t *testing.T) { AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-mc", Namespace: "fleet-member-test-mc", - RequestKind: &validation.V1Alpha1IMCGVK, + RequestKind: &utils.V1Alpha1IMCMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "system:serviceaccount:fleet-system:hub-agent-sa", Groups: []string{"system:serviceaccounts"}, @@ -814,14 +814,14 @@ func TestHandleFleetReservedNamespacedResource(t *testing.T) { }, }, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "system:serviceaccount:fleet-system:hub-agent-sa", utils.GenerateGroupString([]string{"system:serviceaccounts"}), admissionv1.Create, &validation.V1Alpha1IMCGVK, "", types.NamespacedName{Name: "test-mc", Namespace: "fleet-member-test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "system:serviceaccount:fleet-system:hub-agent-sa", utils.GenerateGroupString([]string{"system:serviceaccounts"}), admissionv1.Create, &utils.V1Alpha1IMCMetaGVK, "", types.NamespacedName{Name: "test-mc", Namespace: "fleet-member-test-mc"})), }, "allow user in MC identity with create in fleet member cluster namespace with internalServiceExport with v1alpha1 client": { req: admission.Request{ AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-ise", Namespace: "fleet-member-test-mc", - RequestKind: &validation.InternalServiceExportGVK, + RequestKind: &utils.InternalServiceExportMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "test-identity", Groups: []string{"system:authenticated"}, @@ -832,14 +832,14 @@ func TestHandleFleetReservedNamespacedResource(t *testing.T) { resourceValidator: fleetResourceValidator{ client: v1Alpha1MockClient, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-identity", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Create, &validation.InternalServiceExportGVK, "", types.NamespacedName{Name: "test-ise", Namespace: "fleet-member-test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-identity", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Create, &utils.InternalServiceExportMetaGVK, "", types.NamespacedName{Name: "test-ise", Namespace: "fleet-member-test-mc"})), }, "allow user in MC identity with create in fleet member cluster namespace with internalServiceExport with v1beta1 client": { req: admission.Request{ AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-ise", Namespace: "fleet-member-test-mc", - RequestKind: &validation.InternalServiceExportGVK, + RequestKind: &utils.InternalServiceExportMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "test-identity", Groups: []string{"system:authenticated"}, @@ -851,14 +851,14 @@ func TestHandleFleetReservedNamespacedResource(t *testing.T) { client: mockClient, isFleetV1Beta1API: true, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-identity", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Create, &validation.InternalServiceExportGVK, "", types.NamespacedName{Name: "test-ise", Namespace: "fleet-member-test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-identity", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Create, &utils.InternalServiceExportMetaGVK, "", types.NamespacedName{Name: "test-ise", Namespace: "fleet-member-test-mc"})), }, "allow user in system:masters group with update in fleet member cluster namespace with v1alpha1 Work": { req: admission.Request{ AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-work", Namespace: "fleet-member-test-mc", - RequestKind: &validation.V1Alpha1WorkGVK, + RequestKind: &utils.V1Alpha1WorkMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "testUser", Groups: []string{"system:masters"}, @@ -866,14 +866,14 @@ func TestHandleFleetReservedNamespacedResource(t *testing.T) { Operation: admissionv1.Update, }, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "testUser", utils.GenerateGroupString([]string{"system:masters"}), admissionv1.Update, &validation.V1Alpha1WorkGVK, "", types.NamespacedName{Name: "test-work", Namespace: "fleet-member-test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "testUser", utils.GenerateGroupString([]string{"system:masters"}), admissionv1.Update, &utils.V1Alpha1WorkMetaGVK, "", types.NamespacedName{Name: "test-work", Namespace: "fleet-member-test-mc"})), }, "allow user in MC identity with update in fleet member cluster namespace with v1alpha1 IMC": { req: admission.Request{ AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-mc", Namespace: "fleet-member-test-mc", - RequestKind: &validation.V1Alpha1IMCGVK, + RequestKind: &utils.V1Alpha1IMCMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "test-identity", Groups: []string{"system:authenticated"}, @@ -884,14 +884,14 @@ func TestHandleFleetReservedNamespacedResource(t *testing.T) { resourceValidator: fleetResourceValidator{ client: v1Alpha1MockClient, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-identity", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Update, &validation.V1Alpha1IMCGVK, "", types.NamespacedName{Name: "test-mc", Namespace: "fleet-member-test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-identity", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Update, &utils.V1Alpha1IMCMetaGVK, "", types.NamespacedName{Name: "test-mc", Namespace: "fleet-member-test-mc"})), }, "allow user in MC identity with update in fleet member cluster namespace with v1alpha1 Work": { req: admission.Request{ AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-mc", Namespace: "fleet-member-test-mc", - RequestKind: &validation.V1Alpha1WorkGVK, + RequestKind: &utils.V1Alpha1WorkMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "test-identity", Groups: []string{"system:authenticated"}, @@ -902,14 +902,14 @@ func TestHandleFleetReservedNamespacedResource(t *testing.T) { resourceValidator: fleetResourceValidator{ client: v1Alpha1MockClient, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-identity", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Update, &validation.V1Alpha1WorkGVK, "", types.NamespacedName{Name: "test-mc", Namespace: "fleet-member-test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-identity", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Update, &utils.V1Alpha1WorkMetaGVK, "", types.NamespacedName{Name: "test-mc", Namespace: "fleet-member-test-mc"})), }, "allow request if get MC failed with internal server error with v1alpha1 Work": { req: admission.Request{ AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-work", Namespace: "fleet-member-test-mc1", - RequestKind: &validation.V1Alpha1WorkGVK, + RequestKind: &utils.V1Alpha1WorkMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "testUser", Groups: []string{"testGroup"}, @@ -920,14 +920,14 @@ func TestHandleFleetReservedNamespacedResource(t *testing.T) { resourceValidator: fleetResourceValidator{ client: v1Alpha1MockClient, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedGetMCFailed, "testUser", utils.GenerateGroupString([]string{"testGroup"}), admissionv1.Update, &validation.V1Alpha1WorkGVK, "", types.NamespacedName{Name: "test-work", Namespace: "fleet-member-test-mc1"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedGetMCFailed, "testUser", utils.GenerateGroupString([]string{"testGroup"}), admissionv1.Update, &utils.V1Alpha1WorkMetaGVK, "", types.NamespacedName{Name: "test-work", Namespace: "fleet-member-test-mc1"})), }, "allow user in MC identity with update in fleet member cluster namespace with v1beta1 IMC": { req: admission.Request{ AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-mc", Namespace: "fleet-member-test-mc", - RequestKind: &validation.IMCGVK, + RequestKind: &utils.IMCMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "test-identity", Groups: []string{"system:authenticated"}, @@ -939,14 +939,14 @@ func TestHandleFleetReservedNamespacedResource(t *testing.T) { client: mockClient, isFleetV1Beta1API: true, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-identity", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Update, &validation.IMCGVK, "", types.NamespacedName{Name: "test-mc", Namespace: "fleet-member-test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-identity", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Update, &utils.IMCMetaGVK, "", types.NamespacedName{Name: "test-mc", Namespace: "fleet-member-test-mc"})), }, "allow user in MC identity with update in fleet member cluster namespace with v1beta1 Work": { req: admission.Request{ AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-mc", Namespace: "fleet-member-test-mc", - RequestKind: &validation.WorkGVK, + RequestKind: &utils.WorkV1Beta1MetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "test-identity", Groups: []string{"system:authenticated"}, @@ -958,14 +958,14 @@ func TestHandleFleetReservedNamespacedResource(t *testing.T) { client: mockClient, isFleetV1Beta1API: true, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-identity", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Update, &validation.WorkGVK, "", types.NamespacedName{Name: "test-mc", Namespace: "fleet-member-test-mc"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-identity", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Update, &utils.WorkV1Beta1MetaGVK, "", types.NamespacedName{Name: "test-mc", Namespace: "fleet-member-test-mc"})), }, "deny user not in MC identity with update in fleet member cluster namespace with v1beta1 IMC": { req: admission.Request{ AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-mc", Namespace: "fleet-member-test-mc", - RequestKind: &validation.IMCGVK, + RequestKind: &utils.IMCMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "test-user", Groups: []string{"system:authenticated"}, @@ -977,14 +977,14 @@ func TestHandleFleetReservedNamespacedResource(t *testing.T) { client: mockClient, isFleetV1Beta1API: true, }, - wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "test-user", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Update, &validation.IMCGVK, "", types.NamespacedName{Name: "test-mc", Namespace: "fleet-member-test-mc"})), + wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "test-user", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Update, &utils.IMCMetaGVK, "", types.NamespacedName{Name: "test-mc", Namespace: "fleet-member-test-mc"})), }, "allow request if get MC failed with internal server error with v1beta1 Work": { req: admission.Request{ AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-work", Namespace: "fleet-member-test-mc1", - RequestKind: &validation.V1Alpha1WorkGVK, + RequestKind: &utils.V1Alpha1WorkMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "testUser", Groups: []string{"testGroup"}, @@ -996,14 +996,14 @@ func TestHandleFleetReservedNamespacedResource(t *testing.T) { client: mockClient, isFleetV1Beta1API: true, }, - wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedGetMCFailed, "testUser", utils.GenerateGroupString([]string{"testGroup"}), admissionv1.Update, &validation.V1Alpha1WorkGVK, "", types.NamespacedName{Name: "test-work", Namespace: "fleet-member-test-mc1"})), + wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedGetMCFailed, "testUser", utils.GenerateGroupString([]string{"testGroup"}), admissionv1.Update, &utils.V1Alpha1WorkMetaGVK, "", types.NamespacedName{Name: "test-work", Namespace: "fleet-member-test-mc1"})), }, "deny request to create in fleet-system if user is not validated user with endPointSliceExport": { req: admission.Request{ AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-net-eps", Namespace: "fleet-system", - RequestKind: &validation.EndpointSliceExportGVK, + RequestKind: &utils.EndpointSliceExportMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "testUser", Groups: []string{"testGroup"}, @@ -1015,7 +1015,7 @@ func TestHandleFleetReservedNamespacedResource(t *testing.T) { client: mockClient, isFleetV1Beta1API: true, }, - wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "testUser", utils.GenerateGroupString([]string{"testGroup"}), admissionv1.Create, &validation.EndpointSliceExportGVK, "", types.NamespacedName{Name: "test-net-eps", Namespace: "fleet-system"})), + wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "testUser", utils.GenerateGroupString([]string{"testGroup"}), admissionv1.Create, &utils.EndpointSliceExportMetaGVK, "", types.NamespacedName{Name: "test-net-eps", Namespace: "fleet-system"})), }, } for testName, testCase := range testCases { @@ -1049,11 +1049,11 @@ func TestHandleNamespace(t *testing.T) { Username: "testUser", Groups: []string{"testGroup"}, }, - RequestKind: &validation.NamespaceGVK, + RequestKind: &utils.NamespaceMetaGVK, Operation: admissionv1.Update, }, }, - wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "testUser", utils.GenerateGroupString([]string{"testGroup"}), admissionv1.Update, &validation.NamespaceGVK, "", types.NamespacedName{Name: "fleet-system"})), + wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "testUser", utils.GenerateGroupString([]string{"testGroup"}), admissionv1.Update, &utils.NamespaceMetaGVK, "", types.NamespacedName{Name: "fleet-system"})), }, "deny user not in system:masters group to modify kube-system namespace": { req: admission.Request{ @@ -1063,11 +1063,11 @@ func TestHandleNamespace(t *testing.T) { Username: "testUser", Groups: []string{"testGroup"}, }, - RequestKind: &validation.NamespaceGVK, + RequestKind: &utils.NamespaceMetaGVK, Operation: admissionv1.Update, }, }, - wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "testUser", utils.GenerateGroupString([]string{"testGroup"}), admissionv1.Update, &validation.NamespaceGVK, "", types.NamespacedName{Name: "kube-system"})), + wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "testUser", utils.GenerateGroupString([]string{"testGroup"}), admissionv1.Update, &utils.NamespaceMetaGVK, "", types.NamespacedName{Name: "kube-system"})), }, } diff --git a/pkg/webhook/validation/uservalidation.go b/pkg/webhook/validation/uservalidation.go index c8b9bfa71..803b14cff 100644 --- a/pkg/webhook/validation/uservalidation.go +++ b/pkg/webhook/validation/uservalidation.go @@ -8,19 +8,14 @@ import ( "reflect" authenticationv1 "k8s.io/api/authentication/v1" - corev1 "k8s.io/api/core/v1" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/klog/v2" "k8s.io/utils/strings/slices" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - workv1alpha1 "sigs.k8s.io/work-api/pkg/apis/v1alpha1" - fleetnetworkingv1alpha1 "go.goms.io/fleet-networking/api/v1alpha1" clusterv1beta1 "go.goms.io/fleet/apis/cluster/v1beta1" - placementv1beta1 "go.goms.io/fleet/apis/placement/v1beta1" fleetv1alpha1 "go.goms.io/fleet/apis/v1alpha1" "go.goms.io/fleet/pkg/utils" ) @@ -42,20 +37,7 @@ const ( ) var ( - fleetCRDGroups = []string{"networking.fleet.azure.com", "fleet.azure.com", "multicluster.x-k8s.io", "cluster.kubernetes-fleet.io", "placement.kubernetes-fleet.io"} - CRDGVK = metav1.GroupVersionKind{Group: apiextensionsv1.SchemeGroupVersion.Group, Version: apiextensionsv1.SchemeGroupVersion.Version, Kind: "CustomResourceDefinition"} - V1Alpha1MCGVK = metav1.GroupVersionKind{Group: fleetv1alpha1.GroupVersion.Group, Version: fleetv1alpha1.GroupVersion.Version, Kind: "MemberCluster"} - V1Alpha1IMCGVK = metav1.GroupVersionKind{Group: fleetv1alpha1.GroupVersion.Group, Version: fleetv1alpha1.GroupVersion.Version, Kind: "InternalMemberCluster"} - V1Alpha1WorkGVK = metav1.GroupVersionKind{Group: workv1alpha1.GroupVersion.Group, Version: workv1alpha1.GroupVersion.Version, Kind: "Work"} - MCGVK = metav1.GroupVersionKind{Group: clusterv1beta1.GroupVersion.Group, Version: clusterv1beta1.GroupVersion.Version, Kind: "MemberCluster"} - IMCGVK = metav1.GroupVersionKind{Group: clusterv1beta1.GroupVersion.Group, Version: clusterv1beta1.GroupVersion.Version, Kind: "InternalMemberCluster"} - WorkGVK = metav1.GroupVersionKind{Group: placementv1beta1.GroupVersion.Group, Version: placementv1beta1.GroupVersion.Version, Kind: "Work"} - NamespaceGVK = metav1.GroupVersionKind{Group: corev1.SchemeGroupVersion.Group, Version: corev1.SchemeGroupVersion.Version, Kind: "Namespace"} - EventGVK = metav1.GroupVersionKind{Group: corev1.SchemeGroupVersion.Group, Version: corev1.SchemeGroupVersion.Version, Kind: "Event"} - EndpointSliceExportGVK = metav1.GroupVersionKind{Group: fleetnetworkingv1alpha1.GroupVersion.Group, Version: fleetnetworkingv1alpha1.GroupVersion.Version, Kind: "EndpointSliceExport"} - EndpointSliceImportGVK = metav1.GroupVersionKind{Group: fleetnetworkingv1alpha1.GroupVersion.Group, Version: fleetnetworkingv1alpha1.GroupVersion.Version, Kind: "EndpointSliceImport"} - InternalServiceExportGVK = metav1.GroupVersionKind{Group: fleetnetworkingv1alpha1.GroupVersion.Group, Version: fleetnetworkingv1alpha1.GroupVersion.Version, Kind: "InternalServiceExport"} - InternalServiceImportGVK = metav1.GroupVersionKind{Group: fleetnetworkingv1alpha1.GroupVersion.Group, Version: fleetnetworkingv1alpha1.GroupVersion.Version, Kind: "InternalServiceImport"} + fleetCRDGroups = []string{"networking.fleet.azure.com", "fleet.azure.com", "multicluster.x-k8s.io", "cluster.kubernetes-fleet.io", "placement.kubernetes-fleet.io"} ) // ValidateUserForFleetCRD checks to see if user is not allowed to modify fleet CRDs. diff --git a/pkg/webhook/validation/uservalidation_test.go b/pkg/webhook/validation/uservalidation_test.go index 11ff73b88..ac92f41a5 100644 --- a/pkg/webhook/validation/uservalidation_test.go +++ b/pkg/webhook/validation/uservalidation_test.go @@ -8,19 +8,12 @@ import ( "github.com/stretchr/testify/assert" admissionv1 "k8s.io/api/admission/v1" authenticationv1 "k8s.io/api/authentication/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "go.goms.io/fleet/pkg/utils" ) -var ( - roleGVK = metav1.GroupVersionKind{Group: metav1.SchemeGroupVersion.Group, Version: metav1.SchemeGroupVersion.Version, Kind: "Role"} - roleBindingGVK = metav1.GroupVersionKind{Group: metav1.SchemeGroupVersion.Group, Version: metav1.SchemeGroupVersion.Version, Kind: "RoleBinding"} - podGVK = metav1.GroupVersionKind{Group: metav1.SchemeGroupVersion.Group, Version: metav1.SchemeGroupVersion.Version, Kind: "Pod"} -) - func TestValidateUserForResource(t *testing.T) { manyGroups := []string{mastersGroup, "random0", "random1", "random2", "random3", "random4", "random5", "random6", "random7", "random8", "random9"} sort.Strings(manyGroups) @@ -34,7 +27,7 @@ func TestValidateUserForResource(t *testing.T) { AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-role", Namespace: "test-namespace", - RequestKind: &roleGVK, + RequestKind: &utils.RoleMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "test-user", Groups: []string{mastersGroup}, @@ -42,7 +35,7 @@ func TestValidateUserForResource(t *testing.T) { Operation: admissionv1.Create, }, }, - wantResponse: admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{mastersGroup}), admissionv1.Create, &roleGVK, "", types.NamespacedName{Name: "test-role", Namespace: "test-namespace"})), + wantResponse: admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{mastersGroup}), admissionv1.Create, &utils.RoleMetaGVK, "", types.NamespacedName{Name: "test-role", Namespace: "test-namespace"})), }, // UT to test GenerateGroupString in pkg/utils/common.gp "allow user in system:masters group along with 10 other groups": { @@ -50,7 +43,7 @@ func TestValidateUserForResource(t *testing.T) { AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-role", Namespace: "test-namespace", - RequestKind: &roleGVK, + RequestKind: &utils.RoleMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "test-user", Groups: manyGroups, @@ -58,14 +51,14 @@ func TestValidateUserForResource(t *testing.T) { Operation: admissionv1.Create, }, }, - wantResponse: admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, "test-user", "groups: [random0, random1, random2,......]", admissionv1.Create, &roleGVK, "", types.NamespacedName{Name: "test-role", Namespace: "test-namespace"})), + wantResponse: admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, "test-user", "groups: [random0, random1, random2,......]", admissionv1.Create, &utils.RoleMetaGVK, "", types.NamespacedName{Name: "test-role", Namespace: "test-namespace"})), }, "allow white listed user not in system:masters group": { req: admission.Request{ AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-role-binding", Namespace: "test-namespace", - RequestKind: &roleBindingGVK, + RequestKind: &utils.RoleBindingMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "test-user", Groups: []string{"test-group"}, @@ -74,14 +67,14 @@ func TestValidateUserForResource(t *testing.T) { }, }, whiteListedUsers: []string{"test-user"}, - wantResponse: admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &roleBindingGVK, "", types.NamespacedName{Name: "test-role-binding", Namespace: "test-namespace"})), + wantResponse: admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &utils.RoleBindingMetaGVK, "", types.NamespacedName{Name: "test-role-binding", Namespace: "test-namespace"})), }, "allow valid service account": { req: admission.Request{ AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-role-binding", Namespace: "test-namespace", - RequestKind: &roleBindingGVK, + RequestKind: &utils.RoleBindingMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "test-user", Groups: []string{serviceAccountsGroup}, @@ -89,14 +82,14 @@ func TestValidateUserForResource(t *testing.T) { Operation: admissionv1.Delete, }, }, - wantResponse: admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{serviceAccountsGroup}), admissionv1.Delete, &roleBindingGVK, "", types.NamespacedName{Name: "test-role-binding", Namespace: "test-namespace"})), + wantResponse: admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{serviceAccountsGroup}), admissionv1.Delete, &utils.RoleBindingMetaGVK, "", types.NamespacedName{Name: "test-role-binding", Namespace: "test-namespace"})), }, "allow user in system:node group": { req: admission.Request{ AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-pod", Namespace: "test-namespace", - RequestKind: &podGVK, + RequestKind: &utils.PodMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "test-user", Groups: []string{nodeGroup}, @@ -104,14 +97,14 @@ func TestValidateUserForResource(t *testing.T) { Operation: admissionv1.Create, }, }, - wantResponse: admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{nodeGroup}), admissionv1.Create, &podGVK, "", types.NamespacedName{Name: "test-pod", Namespace: "test-namespace"})), + wantResponse: admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{nodeGroup}), admissionv1.Create, &utils.PodMetaGVK, "", types.NamespacedName{Name: "test-pod", Namespace: "test-namespace"})), }, "allow system:kube-scheduler user": { req: admission.Request{ AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-pod", Namespace: "test-namespace", - RequestKind: &podGVK, + RequestKind: &utils.PodMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "system:kube-scheduler", Groups: []string{"system:authenticated"}, @@ -119,14 +112,14 @@ func TestValidateUserForResource(t *testing.T) { Operation: admissionv1.Update, }, }, - wantResponse: admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, "system:kube-scheduler", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Update, &podGVK, "", types.NamespacedName{Name: "test-pod", Namespace: "test-namespace"})), + wantResponse: admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, "system:kube-scheduler", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Update, &utils.PodMetaGVK, "", types.NamespacedName{Name: "test-pod", Namespace: "test-namespace"})), }, "allow system:kube-controller-manager user": { req: admission.Request{ AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-pod", Namespace: "test-namespace", - RequestKind: &podGVK, + RequestKind: &utils.PodMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "system:kube-controller-manager", Groups: []string{"system:authenticated"}, @@ -134,14 +127,14 @@ func TestValidateUserForResource(t *testing.T) { Operation: admissionv1.Delete, }, }, - wantResponse: admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, "system:kube-controller-manager", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Delete, &podGVK, "", types.NamespacedName{Name: "test-pod", Namespace: "test-namespace"})), + wantResponse: admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, "system:kube-controller-manager", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Delete, &utils.PodMetaGVK, "", types.NamespacedName{Name: "test-pod", Namespace: "test-namespace"})), }, "fail to validate user with invalid username, groups": { req: admission.Request{ AdmissionRequest: admissionv1.AdmissionRequest{ Name: "test-role", Namespace: "test-namespace", - RequestKind: &roleGVK, + RequestKind: &utils.RoleMetaGVK, UserInfo: authenticationv1.UserInfo{ Username: "test-user", Groups: []string{"test-group"}, @@ -149,7 +142,7 @@ func TestValidateUserForResource(t *testing.T) { Operation: admissionv1.Delete, }, }, - wantResponse: admission.Denied(fmt.Sprintf(ResourceDeniedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Delete, &roleGVK, "", types.NamespacedName{Name: "test-role", Namespace: "test-namespace"})), + wantResponse: admission.Denied(fmt.Sprintf(ResourceDeniedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Delete, &utils.RoleMetaGVK, "", types.NamespacedName{Name: "test-role", Namespace: "test-namespace"})), }, } diff --git a/test/e2e/v1alpha1/webhook_test.go b/test/e2e/v1alpha1/webhook_test.go index 7dcf99298..53aa57f01 100644 --- a/test/e2e/v1alpha1/webhook_test.go +++ b/test/e2e/v1alpha1/webhook_test.go @@ -1196,7 +1196,7 @@ var _ = Describe("Fleet's Reserved Namespace Handler webhook tests", func() { It("should allow create/update/delete operation on namespace without fleet/kube prefix for user not in system:masters group", func() { ns := corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-namespace", + Name: "test-mynamespace", }, } Expect(HubCluster.ImpersonateKubeClient.Create(ctx, &ns)).Should(Succeed()) diff --git a/test/e2e/v1alpha1/work_load_test.go b/test/e2e/v1alpha1/work_load_test.go index 67aeb00b8..b3ac3c5a1 100644 --- a/test/e2e/v1alpha1/work_load_test.go +++ b/test/e2e/v1alpha1/work_load_test.go @@ -241,6 +241,11 @@ var _ = Describe("workload orchestration testing", func() { }, }, SelectedResources: []v1alpha1.ResourceIdentifier{ + { + Version: "v1", + Kind: "Namespace", + Name: namespace1.Name, + }, { Group: "rbac.authorization.k8s.io", Version: "v1", @@ -255,11 +260,6 @@ var _ = Describe("workload orchestration testing", func() { Name: role.Name, Namespace: role.Namespace, }, - { - Version: "v1", - Kind: "Namespace", - Name: namespace1.Name, - }, }, TargetClusters: []string{"kind-member-testing"}, } @@ -372,17 +372,17 @@ var _ = Describe("workload orchestration testing", func() { }, }, SelectedResources: []v1alpha1.ResourceIdentifier{ + { + Version: "v1", + Kind: "Namespace", + Name: namespace.Name, + }, { Version: "v1", Kind: "Secret", Name: testSmallSecret.Name, Namespace: testSmallSecret.Namespace, }, - { - Version: "v1", - Kind: "Namespace", - Name: namespace.Name, - }, }, TargetClusters: []string{"kind-member-testing"}, } diff --git a/test/integration/cluster_placement_test.go b/test/integration/cluster_placement_test.go index 206a7934d..c0b64bad6 100644 --- a/test/integration/cluster_placement_test.go +++ b/test/integration/cluster_placement_test.go @@ -1747,7 +1747,7 @@ var _ = Describe("Test Cluster Resource Placement Controller", func() { Reason: clusterresourceplacement.ApplyFailedReason, }, }, - SelectedResources: []fleetv1alpha1.ResourceIdentifier{fleetResourceIdentifier1, fleetResourceIdentifier2}, + SelectedResources: []fleetv1alpha1.ResourceIdentifier{fleetResourceIdentifier2, fleetResourceIdentifier1}, TargetClusters: []string{clusterA.Name}, FailedResourcePlacements: []fleetv1alpha1.FailedResourcePlacement{ { @@ -1852,7 +1852,7 @@ var _ = Describe("Test Cluster Resource Placement Controller", func() { Reason: clusterresourceplacement.ApplySucceededReason, }, }, - SelectedResources: []fleetv1alpha1.ResourceIdentifier{fleetResourceIdentifier1, fleetResourceIdentifier2, fleetResourceIdentifier3}, + SelectedResources: []fleetv1alpha1.ResourceIdentifier{fleetResourceIdentifier3, fleetResourceIdentifier2, fleetResourceIdentifier1}, TargetClusters: []string{clusterA.Name}, }