From eb4364d8e4f654d98ade2776f24f4c7614959b62 Mon Sep 17 00:00:00 2001 From: Sebastian Widmer Date: Tue, 23 May 2023 11:18:17 +0200 Subject: [PATCH] Create `UsageProfile` CRD (#165) See https://kb.vshn.ch/appuio-cloud/references/architecture/control-api-usage-profile.html --- apis/v1/usage_profile_types.go | 46 +++++++++ apis/v1/zz_generated.deepcopy.go | 98 ++++++++++++++++++- .../v1/base/appuio.io_usageprofiles.yaml | 60 ++++++++++++ .../v1/kustomization.yaml | 1 + config/examples/usageprofile.yaml | 72 ++++++++++++++ 5 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 apis/v1/usage_profile_types.go create mode 100644 config/crd/apiextensions.k8s.io/v1/base/appuio.io_usageprofiles.yaml create mode 100644 config/examples/usageprofile.yaml diff --git a/apis/v1/usage_profile_types.go b/apis/v1/usage_profile_types.go new file mode 100644 index 00000000..7c3f0b81 --- /dev/null +++ b/apis/v1/usage_profile_types.go @@ -0,0 +1,46 @@ +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope=Cluster +// +kubebuilder:subresource:status + +// UsageProfile is a representation of an APPUiO Cloud usage profile +type UsageProfile struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec UsageProfileSpec `json:"spec,omitempty"` + Status UsageProfileStatus `json:"status,omitempty"` +} + +// UsageProfileSpec contains the desired state of the usage profile +type UsageProfileSpec struct { + // NamespaceCount is the number of namespaces an organization with this usage profile can create per zone. + NamespaceCount int `json:"namespaceCount,omitempty"` + // Resources is the set of resources which are created in each namespace for which the usage profile is applied. + // The key is used as the name of the resource and the value is the resource definition. + Resources map[string]runtime.RawExtension `json:"resources,omitempty"` +} + +// UsageProfileStatus contains the actual state of the usage profile +type UsageProfileStatus struct { +} + +// +kubebuilder:object:root=true + +// UsageProfileList contains a list of UsageProfiles. +type UsageProfileList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []UsageProfile `json:"items"` +} + +func init() { + SchemeBuilder.Register(&UsageProfile{}, &UsageProfileList{}) +} diff --git a/apis/v1/zz_generated.deepcopy.go b/apis/v1/zz_generated.deepcopy.go index 7c8842c1..2a31c127 100644 --- a/apis/v1/zz_generated.deepcopy.go +++ b/apis/v1/zz_generated.deepcopy.go @@ -6,7 +6,7 @@ package v1 import ( - runtime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. @@ -285,6 +285,102 @@ func (in URLMap) DeepCopy() URLMap { return *out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UsageProfile) DeepCopyInto(out *UsageProfile) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UsageProfile. +func (in *UsageProfile) DeepCopy() *UsageProfile { + if in == nil { + return nil + } + out := new(UsageProfile) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *UsageProfile) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UsageProfileList) DeepCopyInto(out *UsageProfileList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]UsageProfile, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UsageProfileList. +func (in *UsageProfileList) DeepCopy() *UsageProfileList { + if in == nil { + return nil + } + out := new(UsageProfileList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *UsageProfileList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UsageProfileSpec) DeepCopyInto(out *UsageProfileSpec) { + *out = *in + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = make(map[string]runtime.RawExtension, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UsageProfileSpec. +func (in *UsageProfileSpec) DeepCopy() *UsageProfileSpec { + if in == nil { + return nil + } + out := new(UsageProfileSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UsageProfileStatus) DeepCopyInto(out *UsageProfileStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UsageProfileStatus. +func (in *UsageProfileStatus) DeepCopy() *UsageProfileStatus { + if in == nil { + return nil + } + out := new(UsageProfileStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *User) DeepCopyInto(out *User) { *out = *in diff --git a/config/crd/apiextensions.k8s.io/v1/base/appuio.io_usageprofiles.yaml b/config/crd/apiextensions.k8s.io/v1/base/appuio.io_usageprofiles.yaml new file mode 100644 index 00000000..7a20c1f6 --- /dev/null +++ b/config/crd/apiextensions.k8s.io/v1/base/appuio.io_usageprofiles.yaml @@ -0,0 +1,60 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.11.3 + creationTimestamp: null + name: usageprofiles.appuio.io +spec: + group: appuio.io + names: + kind: UsageProfile + listKind: UsageProfileList + plural: usageprofiles + singular: usageprofile + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + description: UsageProfile is a representation of an APPUiO Cloud usage profile + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: UsageProfileSpec contains the desired state of the usage + profile + properties: + namespaceCount: + description: NamespaceCount is the number of namespaces an organization + with this usage profile can create per zone. + type: integer + resources: + additionalProperties: + type: object + x-kubernetes-preserve-unknown-fields: true + description: Resources is the set of resources which are created in + each namespace for which the usage profile is applied. The key is + used as the name of the resource and the value is the resource definition. + type: object + type: object + status: + description: UsageProfileStatus contains the actual state of the usage + profile + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/apiextensions.k8s.io/v1/kustomization.yaml b/config/crd/apiextensions.k8s.io/v1/kustomization.yaml index 3e8a9fff..645b70bc 100644 --- a/config/crd/apiextensions.k8s.io/v1/kustomization.yaml +++ b/config/crd/apiextensions.k8s.io/v1/kustomization.yaml @@ -6,6 +6,7 @@ resources: - base/appuio.io_organizationmembers.yaml - base/appuio.io_users.yaml - base/appuio.io_teams.yaml +- base/appuio.io_usageprofiles.yaml # +kubebuilder:scaffold:crdkustomizeresource # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/examples/usageprofile.yaml b/config/examples/usageprofile.yaml new file mode 100644 index 00000000..dd343374 --- /dev/null +++ b/config/examples/usageprofile.yaml @@ -0,0 +1,72 @@ +apiVersion: appuio.io/v1 +kind: UsageProfile +metadata: + name: example +spec: + namespaceCount: 20 + resources: + organization-compute: + apiVersion: v1 + kind: ResourceQuota + spec: + hard: + limits.cpu: "8" + limits.memory: 20Gi + pods: "45" + requests.cpu: "4" + requests.memory: 4Gi + scopes: + - NotTerminating + organization-compute-terminating: + apiVersion: v1 + kind: ResourceQuota + metadata: + labels: + foo: bar + spec: + hard: + limits.cpu: "4" + limits.memory: 4Gi + pods: "5" + requests.cpu: 500m + requests.memory: 2Gi + scopes: + - Terminating + organization-objects: + apiVersion: v1 + kind: ResourceQuota + spec: + hard: + cephfs-fspool-cluster.storageclass.storage.k8s.io/requests.storage: 25Gi + count/configmaps: "150" + count/jobs.batch: "150" + count/replicationcontrollers: "100" + count/secrets: "150" + count/services: "20" + count/services.loadbalancers: "0" + count/services.nodeports: "0" + limits.ephemeral-storage: 500Mi + localblock-storage.storageclass.storage.k8s.io/persistentvolumeclaims: "0" + openshift.io/imagestreams: "20" + openshift.io/imagestreamtags: "50" + persistentvolumeclaims: "10" + rbd-storagepool-cluster.storageclass.storage.k8s.io/requests.storage: 25Gi + requests.ephemeral-storage: 250Mi + requests.storage: 1000Gi + deny-egress-tcp-25: + apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + annotations: + description: "Deny egress traffic to all hosts on ports 25" + spec: + podSelector: {} + egress: + - to: + - ipBlock: + cidr: 0.0.0.0/0 + ports: + - protocol: TCP + port: 25 + policyTypes: + - Egress