mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-05-06 01:16:44 +00:00
392 lines
11 KiB
Go
392 lines
11 KiB
Go
// Copyright 2020-2021 Clastix Labs
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package v1alpha1
|
|
|
|
import (
|
|
"sort"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
corev1 "k8s.io/api/core/v1"
|
|
networkingv1 "k8s.io/api/networking/v1"
|
|
rbacv1 "k8s.io/api/rbac/v1"
|
|
"k8s.io/apimachinery/pkg/api/resource"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/utils/pointer"
|
|
|
|
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
|
|
"github.com/clastix/capsule/pkg/api"
|
|
)
|
|
|
|
// nolint:maintidx
|
|
func generateTenantsSpecs() (Tenant, capsulev1beta1.Tenant) {
|
|
var namespaceQuota int32 = 5
|
|
|
|
nodeSelector := map[string]string{
|
|
"foo": "bar",
|
|
}
|
|
v1alpha1AdditionalMetadataSpec := &AdditionalMetadata{
|
|
Labels: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Annotations: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
}
|
|
v1alpha1AllowedListSpec := &api.AllowedListSpec{
|
|
Exact: []string{"foo", "bar"},
|
|
Regex: "^foo*",
|
|
}
|
|
v1beta1AdditionalMetadataSpec := &api.AdditionalMetadataSpec{
|
|
Labels: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Annotations: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
}
|
|
v1beta1NamespaceOptions := &capsulev1beta1.NamespaceOptions{
|
|
Quota: &namespaceQuota,
|
|
AdditionalMetadata: v1beta1AdditionalMetadataSpec,
|
|
}
|
|
v1beta1ServiceOptions := &api.ServiceOptions{
|
|
AdditionalMetadata: v1beta1AdditionalMetadataSpec,
|
|
AllowedServices: &api.AllowedServices{
|
|
NodePort: pointer.BoolPtr(false),
|
|
ExternalName: pointer.BoolPtr(false),
|
|
LoadBalancer: pointer.BoolPtr(false),
|
|
},
|
|
ExternalServiceIPs: &api.ExternalServiceIPsSpec{
|
|
Allowed: []api.AllowedIP{"192.168.0.1"},
|
|
},
|
|
}
|
|
v1beta1AllowedListSpec := &api.AllowedListSpec{
|
|
Exact: []string{"foo", "bar"},
|
|
Regex: "^foo*",
|
|
}
|
|
networkPolicies := []networkingv1.NetworkPolicySpec{
|
|
{
|
|
Ingress: []networkingv1.NetworkPolicyIngressRule{
|
|
{
|
|
From: []networkingv1.NetworkPolicyPeer{
|
|
{
|
|
NamespaceSelector: &metav1.LabelSelector{
|
|
MatchLabels: map[string]string{
|
|
"foo": "tenant-resources",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
PodSelector: &metav1.LabelSelector{},
|
|
},
|
|
{
|
|
IPBlock: &networkingv1.IPBlock{
|
|
CIDR: "192.168.0.0/12",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
limitRanges := []corev1.LimitRangeSpec{
|
|
{
|
|
Limits: []corev1.LimitRangeItem{
|
|
{
|
|
Type: corev1.LimitTypePod,
|
|
Min: map[corev1.ResourceName]resource.Quantity{
|
|
corev1.ResourceCPU: resource.MustParse("50m"),
|
|
corev1.ResourceMemory: resource.MustParse("5Mi"),
|
|
},
|
|
Max: map[corev1.ResourceName]resource.Quantity{
|
|
corev1.ResourceCPU: resource.MustParse("1"),
|
|
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
resourceQuotas := []corev1.ResourceQuotaSpec{
|
|
{
|
|
Hard: map[corev1.ResourceName]resource.Quantity{
|
|
corev1.ResourceLimitsCPU: resource.MustParse("8"),
|
|
corev1.ResourceLimitsMemory: resource.MustParse("16Gi"),
|
|
corev1.ResourceRequestsCPU: resource.MustParse("8"),
|
|
corev1.ResourceRequestsMemory: resource.MustParse("16Gi"),
|
|
},
|
|
Scopes: []corev1.ResourceQuotaScope{
|
|
corev1.ResourceQuotaScopeNotTerminating,
|
|
},
|
|
},
|
|
}
|
|
|
|
v1beta1Tnt := capsulev1beta1.Tenant{
|
|
TypeMeta: metav1.TypeMeta{},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "alice",
|
|
Labels: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Annotations: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
},
|
|
Spec: capsulev1beta1.TenantSpec{
|
|
Owners: capsulev1beta1.OwnerListSpec{
|
|
{
|
|
Kind: "User",
|
|
Name: "alice",
|
|
ProxyOperations: []capsulev1beta1.ProxySettings{
|
|
{
|
|
Kind: "IngressClasses",
|
|
Operations: []capsulev1beta1.ProxyOperation{"List", "Update", "Delete"},
|
|
},
|
|
{
|
|
Kind: "Nodes",
|
|
Operations: []capsulev1beta1.ProxyOperation{"Update", "Delete"},
|
|
},
|
|
{
|
|
Kind: "StorageClasses",
|
|
Operations: []capsulev1beta1.ProxyOperation{"Update", "Delete"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Kind: "User",
|
|
Name: "bob",
|
|
ProxyOperations: []capsulev1beta1.ProxySettings{
|
|
{
|
|
Kind: "IngressClasses",
|
|
Operations: []capsulev1beta1.ProxyOperation{"Update"},
|
|
},
|
|
{
|
|
Kind: "StorageClasses",
|
|
Operations: []capsulev1beta1.ProxyOperation{"List"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Kind: "User",
|
|
Name: "jack",
|
|
ProxyOperations: []capsulev1beta1.ProxySettings{
|
|
{
|
|
Kind: "IngressClasses",
|
|
Operations: []capsulev1beta1.ProxyOperation{"Delete"},
|
|
},
|
|
{
|
|
Kind: "Nodes",
|
|
Operations: []capsulev1beta1.ProxyOperation{"Delete"},
|
|
},
|
|
{
|
|
Kind: "StorageClasses",
|
|
Operations: []capsulev1beta1.ProxyOperation{"List"},
|
|
},
|
|
{
|
|
Kind: "PriorityClasses",
|
|
Operations: []capsulev1beta1.ProxyOperation{"List"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Kind: "Group",
|
|
Name: "owner-foo",
|
|
ProxyOperations: []capsulev1beta1.ProxySettings{
|
|
{
|
|
Kind: "IngressClasses",
|
|
Operations: []capsulev1beta1.ProxyOperation{"List"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Kind: "Group",
|
|
Name: "owner-bar",
|
|
ProxyOperations: []capsulev1beta1.ProxySettings{
|
|
{
|
|
Kind: "IngressClasses",
|
|
Operations: []capsulev1beta1.ProxyOperation{"List"},
|
|
},
|
|
{
|
|
Kind: "StorageClasses",
|
|
Operations: []capsulev1beta1.ProxyOperation{"Delete"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Kind: "ServiceAccount",
|
|
Name: "system:serviceaccount:oil-production:default",
|
|
ProxyOperations: []capsulev1beta1.ProxySettings{
|
|
{
|
|
Kind: "Nodes",
|
|
Operations: []capsulev1beta1.ProxyOperation{"Update"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Kind: "ServiceAccount",
|
|
Name: "system:serviceaccount:gas-production:gas",
|
|
ProxyOperations: []capsulev1beta1.ProxySettings{
|
|
{
|
|
Kind: "StorageClasses",
|
|
Operations: []capsulev1beta1.ProxyOperation{"Update"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
NamespaceOptions: v1beta1NamespaceOptions,
|
|
ServiceOptions: v1beta1ServiceOptions,
|
|
StorageClasses: v1beta1AllowedListSpec,
|
|
IngressOptions: capsulev1beta1.IngressOptions{
|
|
HostnameCollisionScope: api.HostnameCollisionScopeDisabled,
|
|
AllowedClasses: v1beta1AllowedListSpec,
|
|
AllowedHostnames: v1beta1AllowedListSpec,
|
|
},
|
|
ContainerRegistries: v1beta1AllowedListSpec,
|
|
NodeSelector: nodeSelector,
|
|
NetworkPolicies: api.NetworkPolicySpec{
|
|
Items: networkPolicies,
|
|
},
|
|
LimitRanges: api.LimitRangesSpec{
|
|
Items: limitRanges,
|
|
},
|
|
ResourceQuota: api.ResourceQuotaSpec{
|
|
Scope: api.ResourceQuotaScopeNamespace,
|
|
Items: resourceQuotas,
|
|
},
|
|
AdditionalRoleBindings: []api.AdditionalRoleBindingsSpec{
|
|
{
|
|
ClusterRoleName: "crds-rolebinding",
|
|
Subjects: []rbacv1.Subject{
|
|
{
|
|
Kind: "Group",
|
|
APIGroup: rbacv1.GroupName,
|
|
Name: "system:authenticated",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
ImagePullPolicies: []api.ImagePullPolicySpec{"Always", "IfNotPresent"},
|
|
PriorityClasses: &api.AllowedListSpec{
|
|
Exact: []string{"default"},
|
|
Regex: "^tier-.*$",
|
|
},
|
|
},
|
|
Status: capsulev1beta1.TenantStatus{
|
|
Size: 1,
|
|
Namespaces: []string{"foo", "bar"},
|
|
},
|
|
}
|
|
|
|
v1alpha1Tnt := Tenant{
|
|
TypeMeta: metav1.TypeMeta{},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "alice",
|
|
Labels: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Annotations: map[string]string{
|
|
"foo": "bar",
|
|
podAllowedImagePullPolicyAnnotation: "Always,IfNotPresent",
|
|
enableExternalNameAnnotation: "false",
|
|
enableNodePortsAnnotation: "false",
|
|
enableLoadBalancerAnnotation: "false",
|
|
podPriorityAllowedAnnotation: "default",
|
|
podPriorityAllowedRegexAnnotation: "^tier-.*$",
|
|
ownerGroupsAnnotation: "owner-foo,owner-bar",
|
|
ownerUsersAnnotation: "bob,jack",
|
|
ownerServiceAccountAnnotation: "system:serviceaccount:oil-production:default,system:serviceaccount:gas-production:gas",
|
|
enableNodeUpdateAnnotation: "alice,system:serviceaccount:oil-production:default",
|
|
enableNodeDeletionAnnotation: "alice,jack",
|
|
enableStorageClassListingAnnotation: "bob,jack",
|
|
enableStorageClassUpdateAnnotation: "alice,system:serviceaccount:gas-production:gas",
|
|
enableStorageClassDeletionAnnotation: "alice,owner-bar",
|
|
enableIngressClassListingAnnotation: "alice,owner-foo,owner-bar",
|
|
enableIngressClassUpdateAnnotation: "alice,bob",
|
|
enableIngressClassDeletionAnnotation: "alice,jack",
|
|
enablePriorityClassListingAnnotation: "jack",
|
|
resourceQuotaScopeAnnotation: "Namespace",
|
|
ingressHostnameCollisionScope: "Disabled",
|
|
},
|
|
},
|
|
Spec: TenantSpec{
|
|
Owner: OwnerSpec{
|
|
Name: "alice",
|
|
Kind: "User",
|
|
},
|
|
NamespaceQuota: &namespaceQuota,
|
|
NamespacesMetadata: v1alpha1AdditionalMetadataSpec,
|
|
ServicesMetadata: v1alpha1AdditionalMetadataSpec,
|
|
StorageClasses: v1alpha1AllowedListSpec,
|
|
IngressClasses: v1alpha1AllowedListSpec,
|
|
IngressHostnames: v1alpha1AllowedListSpec,
|
|
ContainerRegistries: v1alpha1AllowedListSpec,
|
|
NodeSelector: nodeSelector,
|
|
NetworkPolicies: networkPolicies,
|
|
LimitRanges: limitRanges,
|
|
ResourceQuota: resourceQuotas,
|
|
AdditionalRoleBindings: []api.AdditionalRoleBindingsSpec{
|
|
{
|
|
ClusterRoleName: "crds-rolebinding",
|
|
Subjects: []rbacv1.Subject{
|
|
{
|
|
Kind: "Group",
|
|
APIGroup: rbacv1.GroupName,
|
|
Name: "system:authenticated",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
ExternalServiceIPs: &api.ExternalServiceIPsSpec{
|
|
Allowed: []api.AllowedIP{"192.168.0.1"},
|
|
},
|
|
},
|
|
Status: TenantStatus{
|
|
Size: 1,
|
|
Namespaces: []string{"foo", "bar"},
|
|
},
|
|
}
|
|
|
|
return v1alpha1Tnt, v1beta1Tnt
|
|
}
|
|
|
|
func TestConversionHub_ConvertTo(t *testing.T) {
|
|
v1beta1ConvertedTnt := capsulev1beta1.Tenant{}
|
|
|
|
v1alpha1Tnt, v1beta1tnt := generateTenantsSpecs()
|
|
err := v1alpha1Tnt.ConvertTo(&v1beta1ConvertedTnt)
|
|
|
|
if assert.NoError(t, err) {
|
|
sort.Slice(v1beta1tnt.Spec.Owners, func(i, j int) bool {
|
|
return v1beta1tnt.Spec.Owners[i].Name < v1beta1tnt.Spec.Owners[j].Name
|
|
})
|
|
sort.Slice(v1beta1ConvertedTnt.Spec.Owners, func(i, j int) bool {
|
|
return v1beta1ConvertedTnt.Spec.Owners[i].Name < v1beta1ConvertedTnt.Spec.Owners[j].Name
|
|
})
|
|
|
|
for _, owner := range v1beta1tnt.Spec.Owners {
|
|
sort.Slice(owner.ProxyOperations, func(i, j int) bool {
|
|
return owner.ProxyOperations[i].Kind < owner.ProxyOperations[j].Kind
|
|
})
|
|
}
|
|
|
|
for _, owner := range v1beta1ConvertedTnt.Spec.Owners {
|
|
sort.Slice(owner.ProxyOperations, func(i, j int) bool {
|
|
return owner.ProxyOperations[i].Kind < owner.ProxyOperations[j].Kind
|
|
})
|
|
}
|
|
|
|
assert.Equal(t, v1beta1tnt, v1beta1ConvertedTnt)
|
|
}
|
|
}
|
|
|
|
func TestConversionHub_ConvertFrom(t *testing.T) {
|
|
v1alpha1ConvertedTnt := Tenant{}
|
|
|
|
v1alpha1Tnt, v1beta1tnt := generateTenantsSpecs()
|
|
|
|
err := v1alpha1ConvertedTnt.ConvertFrom(&v1beta1tnt)
|
|
if assert.NoError(t, err) {
|
|
assert.EqualValues(t, v1alpha1Tnt, v1alpha1ConvertedTnt)
|
|
}
|
|
}
|