support workspace resource quota

Signed-off-by: hongming <talonwan@yunify.com>
This commit is contained in:
hongming
2021-01-27 11:06:23 +08:00
parent d412fdae98
commit 70fa24010c
68 changed files with 7397 additions and 31 deletions

18
pkg/apis/quota/group.go Normal file
View File

@@ -0,0 +1,18 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package quota contains quota API versions
package quota

View File

@@ -0,0 +1,23 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package v1alpha2 contains API Schema definitions for the quotas v1alpha2 API group
// +k8s:openapi-gen=true
// +k8s:deepcopy-gen=package,register
// +k8s:conversion-gen=kubesphere.io/kubesphere/pkg/apis/quota
// +k8s:defaulter-gen=TypeMeta
// +groupName=quota.kubesphere.io
package v1alpha2

View File

@@ -0,0 +1,46 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// NOTE: Boilerplate only. Ignore this file.
// Package v1alpha2 contains API Schema definitions for the quotas v1alpha2 API group
// +k8s:openapi-gen=true
// +k8s:deepcopy-gen=package,register
// +k8s:conversion-gen=kubesphere.io/kubesphere/pkg/apis/quota
// +k8s:defaulter-gen=TypeMeta
// +groupName=quota.kubesphere.io
package v1alpha2
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)
var (
// SchemeGroupVersion is group version used to register these objects
SchemeGroupVersion = schema.GroupVersion{Group: "quota.kubesphere.io", Version: "v1alpha2"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
// AddToScheme is required by pkg/client/...
AddToScheme = SchemeBuilder.AddToScheme
)
// Resource is required by pkg/client/listers/...
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}

View File

@@ -0,0 +1,99 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha2
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
ResourceKindCluster = "ResourceQuota"
ResourcesSingularCluster = "resourcequota"
ResourcesPluralCluster = "resourcequotas"
)
func init() {
SchemeBuilder.Register(&ResourceQuota{}, &ResourceQuotaList{})
}
// +genclient
// +genclient:nonNamespaced
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:openapi-gen=true
// WorkspaceResourceQuota sets aggregate quota restrictions enforced per workspace
// +kubebuilder:resource:categories="quota",scope="Cluster"
// +kubebuilder:subresource:status
type ResourceQuota struct {
metav1.TypeMeta `json:",inline"`
// Standard object's metadata.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
// +optional
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Spec defines the desired quota
Spec ResourceQuotaSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"`
// Status defines the actual enforced quota and its current usage
// +optional
Status ResourceQuotaStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
// ResourceQuotaSpec defines the desired quota restrictions
type ResourceQuotaSpec struct {
// LabelSelector is used to select projects by label.
LabelSelector map[string]string `json:"selector" protobuf:"bytes,1,opt,name=selector"`
// Quota defines the desired quota
Quota corev1.ResourceQuotaSpec `json:"quota" protobuf:"bytes,2,opt,name=quota"`
}
// ResourceQuotaStatus defines the actual enforced quota and its current usage
type ResourceQuotaStatus struct {
// Total defines the actual enforced quota and its current usage across all projects
Total corev1.ResourceQuotaStatus `json:"total" protobuf:"bytes,1,opt,name=total"`
// Namespaces slices the usage by project.
Namespaces ResourceQuotasStatusByNamespace `json:"namespaces" protobuf:"bytes,2,rep,name=namespaces"`
}
// ResourceQuotasStatusByNamespace bundles multiple ResourceQuotaStatusByNamespace
type ResourceQuotasStatusByNamespace []ResourceQuotaStatusByNamespace
// ResourceQuotaStatusByNamespace gives status for a particular project
type ResourceQuotaStatusByNamespace struct {
corev1.ResourceQuotaStatus `json:",inline"`
// Namespace the project this status applies to
Namespace string `json:"namespace" protobuf:"bytes,1,opt,name=namespace"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ResourceQuotaList is a list of WorkspaceResourceQuota items.
type ResourceQuotaList struct {
metav1.TypeMeta `json:",inline"`
// Standard list metadata.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
// +optional
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Items is a list of WorkspaceResourceQuota objects.
// More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/
Items []ResourceQuota `json:"items" protobuf:"bytes,2,rep,name=items"`
}

View File

@@ -0,0 +1,64 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha2
import (
"testing"
"github.com/onsi/gomega"
"golang.org/x/net/context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
func TestStorageResourceQuota(t *testing.T) {
key := types.NamespacedName{
Name: "foo",
}
created := &ResourceQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: ResourceQuotaSpec{
LabelSelector: map[string]string{},
},
}
g := gomega.NewGomegaWithT(t)
// Test Create
fetched := &ResourceQuota{
Spec: ResourceQuotaSpec{
LabelSelector: map[string]string{},
},
}
g.Expect(c.Create(context.TODO(), created)).To(gomega.Succeed())
g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.Succeed())
g.Expect(fetched).To(gomega.Equal(created))
// Test Updating the Labels
updated := fetched.DeepCopy()
updated.Labels = map[string]string{"hello": "world"}
g.Expect(c.Update(context.TODO(), updated)).To(gomega.Succeed())
g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.Succeed())
g.Expect(fetched).To(gomega.Equal(updated))
// Test Delete
g.Expect(c.Delete(context.TODO(), fetched)).To(gomega.Succeed())
g.Expect(c.Get(context.TODO(), key, fetched)).ToNot(gomega.Succeed())
}

View File

@@ -0,0 +1,55 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha2
import (
"log"
"os"
"path/filepath"
"testing"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
)
var cfg *rest.Config
var c client.Client
func TestMain(m *testing.M) {
t := &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crds")},
}
err := SchemeBuilder.AddToScheme(scheme.Scheme)
if err != nil {
log.Fatal(err)
}
if cfg, err = t.Start(); err != nil {
log.Fatal(err)
}
if c, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}); err != nil {
log.Fatal(err)
}
code := m.Run()
t.Stop()
os.Exit(code)
}

View File

@@ -0,0 +1,167 @@
// +build !ignore_autogenerated
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by controller-gen. DO NOT EDIT.
package v1alpha2
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceQuota) DeepCopyInto(out *ResourceQuota) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceQuota.
func (in *ResourceQuota) DeepCopy() *ResourceQuota {
if in == nil {
return nil
}
out := new(ResourceQuota)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ResourceQuota) 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 *ResourceQuotaList) DeepCopyInto(out *ResourceQuotaList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ResourceQuota, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceQuotaList.
func (in *ResourceQuotaList) DeepCopy() *ResourceQuotaList {
if in == nil {
return nil
}
out := new(ResourceQuotaList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ResourceQuotaList) 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 *ResourceQuotaSpec) DeepCopyInto(out *ResourceQuotaSpec) {
*out = *in
if in.LabelSelector != nil {
in, out := &in.LabelSelector, &out.LabelSelector
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
in.Quota.DeepCopyInto(&out.Quota)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceQuotaSpec.
func (in *ResourceQuotaSpec) DeepCopy() *ResourceQuotaSpec {
if in == nil {
return nil
}
out := new(ResourceQuotaSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceQuotaStatus) DeepCopyInto(out *ResourceQuotaStatus) {
*out = *in
in.Total.DeepCopyInto(&out.Total)
if in.Namespaces != nil {
in, out := &in.Namespaces, &out.Namespaces
*out = make(ResourceQuotasStatusByNamespace, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceQuotaStatus.
func (in *ResourceQuotaStatus) DeepCopy() *ResourceQuotaStatus {
if in == nil {
return nil
}
out := new(ResourceQuotaStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceQuotaStatusByNamespace) DeepCopyInto(out *ResourceQuotaStatusByNamespace) {
*out = *in
in.ResourceQuotaStatus.DeepCopyInto(&out.ResourceQuotaStatus)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceQuotaStatusByNamespace.
func (in *ResourceQuotaStatusByNamespace) DeepCopy() *ResourceQuotaStatusByNamespace {
if in == nil {
return nil
}
out := new(ResourceQuotaStatusByNamespace)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in ResourceQuotasStatusByNamespace) DeepCopyInto(out *ResourceQuotasStatusByNamespace) {
{
in := &in
*out = make(ResourceQuotasStatusByNamespace, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceQuotasStatusByNamespace.
func (in ResourceQuotasStatusByNamespace) DeepCopy() ResourceQuotasStatusByNamespace {
if in == nil {
return nil
}
out := new(ResourceQuotasStatusByNamespace)
in.DeepCopyInto(out)
return *out
}