From bd22856dbc71a10fd503f4b818050f9f60b4adb2 Mon Sep 17 00:00:00 2001 From: hongming Date: Thu, 16 Jul 2020 18:34:36 +0800 Subject: [PATCH] fix: migration some resource API to v1alpha3 Signed-off-by: hongming --- .../resources/v1alpha3/daemonset/daemonset.go | 76 ++++++++++ .../v1alpha3/daemonset/daemonset_test.go | 113 ++++++++++++++ .../resources/v1alpha3/resource/resource.go | 6 + .../resources/v1alpha3/service/services.go | 77 ++++++++++ .../v1alpha3/service/services_test.go | 114 ++++++++++++++ .../v1alpha3/statefulset/statefulsets.go | 102 +++++++++++++ .../v1alpha3/statefulset/statefulsets_test.go | 142 ++++++++++++++++++ 7 files changed, 630 insertions(+) create mode 100644 pkg/models/resources/v1alpha3/daemonset/daemonset.go create mode 100644 pkg/models/resources/v1alpha3/daemonset/daemonset_test.go create mode 100644 pkg/models/resources/v1alpha3/service/services.go create mode 100644 pkg/models/resources/v1alpha3/service/services_test.go create mode 100644 pkg/models/resources/v1alpha3/statefulset/statefulsets.go create mode 100644 pkg/models/resources/v1alpha3/statefulset/statefulsets_test.go diff --git a/pkg/models/resources/v1alpha3/daemonset/daemonset.go b/pkg/models/resources/v1alpha3/daemonset/daemonset.go new file mode 100644 index 00000000..f124985b --- /dev/null +++ b/pkg/models/resources/v1alpha3/daemonset/daemonset.go @@ -0,0 +1,76 @@ +/* +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 daemonset + +import ( + appsv1 "k8s.io/api/apps/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" +) + +type daemonSetGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &daemonSetGetter{sharedInformers: sharedInformers} +} + +func (d *daemonSetGetter) Get(namespace, name string) (runtime.Object, error) { + return d.sharedInformers.Apps().V1().DaemonSets().Lister().DaemonSets(namespace).Get(name) +} + +func (d *daemonSetGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { + // first retrieves all daemonSets within given namespace + daemonSets, err := d.sharedInformers.Apps().V1().DaemonSets().Lister().DaemonSets(namespace).List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, daemonSet := range daemonSets { + result = append(result, daemonSet) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *daemonSetGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftDaemonSet, ok := left.(*appsv1.DaemonSet) + if !ok { + return false + } + + rightDaemonSet, ok := right.(*appsv1.DaemonSet) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftDaemonSet.ObjectMeta, rightDaemonSet.ObjectMeta, field) +} + +func (d *daemonSetGetter) filter(object runtime.Object, filter query.Filter) bool { + daemonSet, ok := object.(*appsv1.DaemonSet) + if !ok { + return false + } + return v1alpha3.DefaultObjectMetaFilter(daemonSet.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/daemonset/daemonset_test.go b/pkg/models/resources/v1alpha3/daemonset/daemonset_test.go new file mode 100644 index 00000000..ec0740c9 --- /dev/null +++ b/pkg/models/resources/v1alpha3/daemonset/daemonset_test.go @@ -0,0 +1,113 @@ +/* +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 daemonset + +import ( + "github.com/google/go-cmp/cmp" + appsv1 "k8s.io/api/apps/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" + "testing" +) + +func TestListDaemonSets(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + 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("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + Namespace: "bar", + }, + } + foo2 = &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + Namespace: "bar", + }, + } + bar1 = &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + Namespace: "bar", + }, + } + + daemonSets = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, daemonSet := range daemonSets { + informer.Apps().V1().DaemonSets().Informer().GetIndexer().Add(daemonSet) + } + + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/resource/resource.go b/pkg/models/resources/v1alpha3/resource/resource.go index 384fd00e..9a84c8c8 100644 --- a/pkg/models/resources/v1alpha3/resource/resource.go +++ b/pkg/models/resources/v1alpha3/resource/resource.go @@ -36,6 +36,7 @@ import ( "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/clusterrolebinding" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/configmap" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/customresourcedefinition" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/daemonset" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/deployment" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/devops" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/globalrole" @@ -47,6 +48,8 @@ import ( "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/pod" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/role" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/rolebinding" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/service" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/statefulset" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/user" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/volumesnapshot" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/workspace" @@ -65,6 +68,9 @@ func NewResourceGetter(factory informers.InformerFactory) *ResourceGetter { getters := make(map[schema.GroupVersionResource]v1alpha3.Interface) getters[schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}] = deployment.New(factory.KubernetesSharedInformerFactory()) + getters[schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "daemonsets"}] = daemonset.New(factory.KubernetesSharedInformerFactory()) + getters[schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "statefulsets"}] = statefulset.New(factory.KubernetesSharedInformerFactory()) + getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services"}] = service.New(factory.KubernetesSharedInformerFactory()) getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"}] = namespace.New(factory.KubernetesSharedInformerFactory()) getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}] = configmap.New(factory.KubernetesSharedInformerFactory()) getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}] = pod.New(factory.KubernetesSharedInformerFactory()) diff --git a/pkg/models/resources/v1alpha3/service/services.go b/pkg/models/resources/v1alpha3/service/services.go new file mode 100644 index 00000000..2dd8babb --- /dev/null +++ b/pkg/models/resources/v1alpha3/service/services.go @@ -0,0 +1,77 @@ +/* +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 service + +import ( + corev1 "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" +) + +type servicesGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &servicesGetter{sharedInformers: sharedInformers} +} + +func (d *servicesGetter) Get(namespace, name string) (runtime.Object, error) { + return d.sharedInformers.Core().V1().Services().Lister().Services(namespace).Get(name) +} + +func (d *servicesGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { + // first retrieves all services within given namespace + services, err := d.sharedInformers.Core().V1().Services().Lister().Services(namespace).List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deployment := range services { + result = append(result, deployment) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *servicesGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftService, ok := left.(*corev1.Service) + if !ok { + return false + } + + rightService, ok := right.(*corev1.Service) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftService.ObjectMeta, rightService.ObjectMeta, field) +} + +func (d *servicesGetter) filter(object runtime.Object, filter query.Filter) bool { + service, ok := object.(*corev1.Service) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(service.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/service/services_test.go b/pkg/models/resources/v1alpha3/service/services_test.go new file mode 100644 index 00000000..df467c19 --- /dev/null +++ b/pkg/models/resources/v1alpha3/service/services_test.go @@ -0,0 +1,114 @@ +/* +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 service + +import ( + "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" + "testing" +) + +func TestListServices(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + 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("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + Namespace: "bar", + }, + } + + foo2 = &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + Namespace: "bar", + }, + } + bar1 = &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + Namespace: "bar", + }, + } + + services = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, service := range services { + informer.Core().V1().Services().Informer().GetIndexer().Add(service) + } + + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/statefulset/statefulsets.go b/pkg/models/resources/v1alpha3/statefulset/statefulsets.go new file mode 100644 index 00000000..84c4736c --- /dev/null +++ b/pkg/models/resources/v1alpha3/statefulset/statefulsets.go @@ -0,0 +1,102 @@ +/* +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 statefulset + +import ( + appsv1 "k8s.io/api/apps/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 ( + statusStopped = "stopped" + statusRunning = "running" + statusUpdating = "updating" +) + +type statefulSetGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &statefulSetGetter{sharedInformers: sharedInformers} +} + +func (d *statefulSetGetter) Get(namespace, name string) (runtime.Object, error) { + return d.sharedInformers.Apps().V1().StatefulSets().Lister().StatefulSets(namespace).Get(name) +} + +func (d *statefulSetGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { + // first retrieves all statefulSets within given namespace + statefulSets, err := d.sharedInformers.Apps().V1().StatefulSets().Lister().StatefulSets(namespace).List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deployment := range statefulSets { + result = append(result, deployment) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *statefulSetGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftStatefulSet, ok := left.(*appsv1.StatefulSet) + if !ok { + return false + } + + rightStatefulSet, ok := right.(*appsv1.StatefulSet) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftStatefulSet.ObjectMeta, rightStatefulSet.ObjectMeta, field) +} + +func (d *statefulSetGetter) filter(object runtime.Object, filter query.Filter) bool { + statefulSet, ok := object.(*appsv1.StatefulSet) + if !ok { + return false + } + + switch filter.Field { + case query.FieldStatus: + return statefulSetStatus(statefulSet) == string(filter.Value) + default: + return v1alpha3.DefaultObjectMetaFilter(statefulSet.ObjectMeta, filter) + } + +} + +func statefulSetStatus(item *appsv1.StatefulSet) string { + if item.Spec.Replicas != nil { + if item.Status.ReadyReplicas == 0 && *item.Spec.Replicas == 0 { + return statusStopped + } else if item.Status.ReadyReplicas == *item.Spec.Replicas { + return statusRunning + } else { + return statusUpdating + } + } + return statusStopped +} diff --git a/pkg/models/resources/v1alpha3/statefulset/statefulsets_test.go b/pkg/models/resources/v1alpha3/statefulset/statefulsets_test.go new file mode 100644 index 00000000..6f0afa61 --- /dev/null +++ b/pkg/models/resources/v1alpha3/statefulset/statefulsets_test.go @@ -0,0 +1,142 @@ +/* +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 statefulset + +import ( + "github.com/google/go-cmp/cmp" + appsv1 "k8s.io/api/apps/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" + "testing" +) + +func TestListStatefulSets(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + { + "test status filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldStatus: query.Value(statusRunning)}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + 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("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + Namespace: "bar", + }, + } + + replicas = int32(1) + + foo2 = &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + Namespace: "bar", + }, + Spec: appsv1.StatefulSetSpec{ + Replicas: &replicas, + }, + Status: appsv1.StatefulSetStatus{ + ReadyReplicas: 1, + }, + } + bar1 = &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + Namespace: "bar", + }, + } + + statefulSets = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, statefulSet := range statefulSets { + informer.Apps().V1().StatefulSets().Informer().GetIndexer().Add(statefulSet) + } + + return New(informer) +} -- GitLab