diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 09f171b4b..783ca0fda 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -390,6 +390,7 @@ func (s *APIServer) waitForResourceSync(ctx context.Context) error { {Group: "", Version: "v1", Resource: "pods"}, {Group: "", Version: "v1", Resource: "services"}, {Group: "", Version: "v1", Resource: "persistentvolumeclaims"}, + {Group: "", Version: "v1", Resource: "persistentvolumes"}, {Group: "", Version: "v1", Resource: "secrets"}, {Group: "", Version: "v1", Resource: "configmaps"}, {Group: "", Version: "v1", Resource: "serviceaccounts"}, diff --git a/pkg/models/resources/v1alpha3/persistentvolume/persistentvolume.go b/pkg/models/resources/v1alpha3/persistentvolume/persistentvolume.go new file mode 100644 index 000000000..65b7f2658 --- /dev/null +++ b/pkg/models/resources/v1alpha3/persistentvolume/persistentvolume.go @@ -0,0 +1,87 @@ +/* +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 persistentvolume + +import ( + "strings" + + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +const ( + storageClassName = "storageClassName" +) + +type persistentVolumeGetter struct { + informers informers.SharedInformerFactory +} + +func New(informer informers.SharedInformerFactory) v1alpha3.Interface { + return &persistentVolumeGetter{informers: informer} +} + +func (p *persistentVolumeGetter) Get(namespace, name string) (runtime.Object, error) { + pv, err := p.informers.Core().V1().PersistentVolumes().Lister().Get(name) + if err != nil { + return pv, err + } + return pv, nil +} + +func (p *persistentVolumeGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { + all, err := p.informers.Core().V1().PersistentVolumes().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + var result []runtime.Object + for _, pv := range all { + result = append(result, pv) + } + return v1alpha3.DefaultList(result, query, p.compare, p.filter), nil +} + +func (p *persistentVolumeGetter) compare(obj1, obj2 runtime.Object, field query.Field) bool { + pv1, ok := obj1.(*v1.PersistentVolume) + if !ok { + return false + } + pv2, ok := obj2.(*v1.PersistentVolume) + if !ok { + return false + } + return v1alpha3.DefaultObjectMetaCompare(pv1.ObjectMeta, pv2.ObjectMeta, field) +} + +func (p *persistentVolumeGetter) filter(object runtime.Object, filter query.Filter) bool { + pv, ok := object.(*v1.PersistentVolume) + if !ok { + return false + } + switch filter.Field { + case query.FieldStatus: + return strings.EqualFold(string(pv.Status.Phase), string(filter.Value)) + case storageClassName: + return pv.Spec.StorageClassName != "" && pv.Spec.StorageClassName == string(filter.Value) + default: + return v1alpha3.DefaultObjectMetaFilter(pv.ObjectMeta, filter) + } +} diff --git a/pkg/models/resources/v1alpha3/persistentvolume/persistentvolume_test.go b/pkg/models/resources/v1alpha3/persistentvolume/persistentvolume_test.go new file mode 100644 index 000000000..9f7a01f7e --- /dev/null +++ b/pkg/models/resources/v1alpha3/persistentvolume/persistentvolume_test.go @@ -0,0 +1,140 @@ +/* +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 persistentvolume + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +var ( + testStorageClassName = "test-csi" +) + +func TestListPods(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 10, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{}, + }, + &api.ListResult{ + Items: []interface{}{pv3, pv2, pv1}, + TotalItems: len(persistentVolumes), + }, + nil, + }, + { + "test status filter", + "", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 10, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{ + query.FieldStatus: query.Value(pv1.Status.Phase), + }, + }, + &api.ListResult{ + Items: []interface{}{pv1}, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + got, err := getter.List(test.namespace, test.query) + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("[%s] %T differ (-got, +want): %s", test.description, test.expected, diff) + } + } +} + +var ( + pv1 = &corev1.PersistentVolume{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "pv-1", + }, + Status: corev1.PersistentVolumeStatus{ + Phase: "bound", + }, + } + pv2 = &corev1.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pv-2", + }, + Status: corev1.PersistentVolumeStatus{ + Phase: "available", + }, + Spec: corev1.PersistentVolumeSpec{ + StorageClassName: testStorageClassName, + }, + } + pv3 = &corev1.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pvc-3", + Namespace: "default", + }, + } + + persistentVolumes = []interface{}{pv1, pv2, pv3} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, pv := range persistentVolumes { + _ = informer.Core().V1().PersistentVolumes().Informer().GetIndexer().Add(pv) + } + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/resource/resource.go b/pkg/models/resources/v1alpha3/resource/resource.go index 0750bc171..275be51ee 100644 --- a/pkg/models/resources/v1alpha3/resource/resource.go +++ b/pkg/models/resources/v1alpha3/resource/resource.go @@ -18,6 +18,7 @@ package resource import ( "errors" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/persistentvolume" snapshotv1beta1 "github.com/kubernetes-csi/external-snapshotter/client/v3/apis/volumesnapshot/v1beta1" rbacv1 "k8s.io/api/rbac/v1" @@ -113,6 +114,7 @@ func NewResourceGetter(factory informers.InformerFactory, cache cache.Cache) *Re namespacedResourceGetters[schema.GroupVersionResource{Group: "batch", Version: "v1", Resource: "jobs"}] = job.New(factory.KubernetesSharedInformerFactory()) namespacedResourceGetters[schema.GroupVersionResource{Group: "app.k8s.io", Version: "v1beta1", Resource: "applications"}] = application.New(cache) namespacedResourceGetters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "persistentvolumeclaims"}] = persistentvolumeclaim.New(factory.KubernetesSharedInformerFactory(), factory.SnapshotSharedInformerFactory()) + namespacedResourceGetters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "persistentvolumes"}] = persistentvolume.New(factory.KubernetesSharedInformerFactory()) namespacedResourceGetters[snapshotv1beta1.SchemeGroupVersion.WithResource("volumesnapshots")] = volumesnapshot.New(factory.SnapshotSharedInformerFactory()) namespacedResourceGetters[rbacv1.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralRoleBinding)] = rolebinding.New(factory.KubernetesSharedInformerFactory()) namespacedResourceGetters[rbacv1.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralRole)] = role.New(factory.KubernetesSharedInformerFactory())