diff --git a/config/webhook/iam.yaml b/config/webhook/iam.yaml index a4418b750c0f44402e7b0d15d2a2ca5029b12aeb..c1ed3a1c928941a34925954a7d5d80e2349cb1fa 100644 --- a/config/webhook/iam.yaml +++ b/config/webhook/iam.yaml @@ -1,5 +1,5 @@ apiVersion: admissionregistration.k8s.io/v1beta1 -kind: MutatingWebhookConfiguration +kind: ValidatingWebhookConfiguration metadata: name: kubesphere-iam-validator webhooks: diff --git a/pkg/apis/iam/v1alpha2/types.go b/pkg/apis/iam/v1alpha2/types.go index 6fef890a38ed5eb0b48c5761734f60eaf6f67d88..872bac1c8f897547dd97398dac408d495f51ed6c 100644 --- a/pkg/apis/iam/v1alpha2/types.go +++ b/pkg/apis/iam/v1alpha2/types.go @@ -152,7 +152,6 @@ const ( ClusterScope Scope = "Cluster" WorkspaceScope Scope = "Workspace" NamespaceScope Scope = "Namespace" - TargetAll = "*" UserKind = "User" PolicyRuleKind = "PolicyRule" RoleKind = "Role" diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 99c4966a7682b5037ba36c6ee03eaefa843972d4..78f829e71d0ba26b92d47e1450e87a5961b1e126 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -37,6 +37,7 @@ import ( resourcesv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/resources/v1alpha2" resourcev1alpha3 "kubesphere.io/kubesphere/pkg/kapis/resources/v1alpha3" servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/servicemesh/metrics/v1alpha2" + tenantv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/tenant/v1alpha2" terminalv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/terminal/v1alpha2" "kubesphere.io/kubesphere/pkg/models/iam/am" "kubesphere.io/kubesphere/pkg/models/iam/im" @@ -141,6 +142,7 @@ func (s *APIServer) installKubeSphereAPIs() { urlruntime.Must(openpitrixv1.AddToContainer(s.container, s.InformerFactory, s.OpenpitrixClient)) urlruntime.Must(operationsv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes())) urlruntime.Must(resourcesv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.InformerFactory)) + urlruntime.Must(tenantv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.InformerFactory)) urlruntime.Must(terminalv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.KubernetesClient.Config())) urlruntime.Must(iamv1alpha2.AddToContainer(s.container, im.NewOperator(s.KubernetesClient.KubeSphere(), s.InformerFactory.KubeSphereSharedInformerFactory()), @@ -285,7 +287,6 @@ func (s *APIServer) waitForResourceSync(stopCh <-chan struct{}) error { {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "roles"}, {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "rolebindings"}, {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "policyrules"}, - {Group: "tower.kubesphere.io", Version: "v1alpha1", Resource: "agents"}, } devopsGVRs := []schema.GroupVersionResource{ diff --git a/pkg/apiserver/authorization/authorizerfactory/opa.go b/pkg/apiserver/authorization/authorizerfactory/opa.go index 6897f65552a02001d8e3020913014ba12a020bca..5139344567805148b5d332552046fd4d5e353b6f 100644 --- a/pkg/apiserver/authorization/authorizerfactory/opa.go +++ b/pkg/apiserver/authorization/authorizerfactory/opa.go @@ -51,7 +51,7 @@ func (o *opaAuthorizer) Authorize(attr authorizer.Attributes) (authorized author return authorizer.DecisionDeny, "", err } - // check platform role policy rules + // check global role policy rules if authorized, reason, err = o.makeDecision(globalRole, attr); authorized == authorizer.DecisionAllow { return authorized, reason, nil } diff --git a/pkg/apiserver/query/types.go b/pkg/apiserver/query/types.go index 7e101326c8d308f4df695ca8cf819f72829f32a3..91d13add5b54e83eba2f64258e298489fecd704e 100644 --- a/pkg/apiserver/query/types.go +++ b/pkg/apiserver/query/types.go @@ -48,11 +48,12 @@ func newPagination(limit int, offset int) *Pagination { } func (p *Pagination) GetValidPagination(total int) (startIndex, endIndex int) { + if p.Limit == NoPagination.Limit { return 0, total } - if p.Limit < 0 || p.Offset < 0 { + if p.Limit < 0 || p.Offset < 0 || total == 0 { return 0, 0 } diff --git a/pkg/kapis/openpitrix/v1/handler.go b/pkg/kapis/openpitrix/v1/handler.go index 8139a10f36f7597874ab7c057d1e6a95d99df6f9..09ca9c3e8a5c78a0b3c2705522e073f5fac4f0b1 100644 --- a/pkg/kapis/openpitrix/v1/handler.go +++ b/pkg/kapis/openpitrix/v1/handler.go @@ -36,7 +36,7 @@ func (h *openpitrixHandler) ListApplications(request *restful.Request, response limit, offset := params.ParsePaging(request) namespace := request.PathParameter("namespace") orderBy := params.GetStringValueWithDefault(request, params.OrderByParam, openpitrix.CreateTime) - reverse := params.GetBoolValueWithDefault(request, params.ReverseParam, true) + reverse := params.GetBoolValueWithDefault(request, params.ReverseParam, false) conditions, err := params.ParseConditions(request) if err != nil { @@ -301,7 +301,7 @@ func (h *openpitrixHandler) GetAppVersionFiles(req *restful.Request, resp *restf func (h *openpitrixHandler) ListAppVersionAudits(req *restful.Request, resp *restful.Response) { limit, offset := params.ParsePaging(req) orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, openpitrix.StatusTime) - reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, false) appId := req.PathParameter("app") versionId := req.PathParameter("version") conditions, err := params.ParseConditions(req) @@ -331,7 +331,7 @@ func (h *openpitrixHandler) ListAppVersionAudits(req *restful.Request, resp *res func (h *openpitrixHandler) ListReviews(req *restful.Request, resp *restful.Response) { limit, offset := params.ParsePaging(req) orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, openpitrix.StatusTime) - reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, false) conditions, err := params.ParseConditions(req) if err != nil { @@ -354,7 +354,7 @@ func (h *openpitrixHandler) ListReviews(req *restful.Request, resp *restful.Resp func (h *openpitrixHandler) ListAppVersions(req *restful.Request, resp *restful.Response) { limit, offset := params.ParsePaging(req) orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, openpitrix.CreateTime) - reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, false) appId := req.PathParameter("app") statistics := params.GetBoolValueWithDefault(req, "statistics", false) conditions, err := params.ParseConditions(req) @@ -394,7 +394,7 @@ func (h *openpitrixHandler) ListAppVersions(req *restful.Request, resp *restful. func (h *openpitrixHandler) ListApps(req *restful.Request, resp *restful.Response) { limit, offset := params.ParsePaging(req) orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, openpitrix.CreateTime) - reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, false) statistics := params.GetBoolValueWithDefault(req, "statistics", false) conditions, err := params.ParseConditions(req) @@ -697,7 +697,7 @@ func (h *openpitrixHandler) DescribeCategory(req *restful.Request, resp *restful func (h *openpitrixHandler) ListCategories(req *restful.Request, resp *restful.Response) { limit, offset := params.ParsePaging(req) orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, openpitrix.CreateTime) - reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, false) statistics := params.GetBoolValueWithDefault(req, "statistics", false) conditions, err := params.ParseConditions(req) @@ -836,7 +836,7 @@ func (h *openpitrixHandler) DescribeRepo(req *restful.Request, resp *restful.Res func (h *openpitrixHandler) ListRepos(req *restful.Request, resp *restful.Response) { limit, offset := params.ParsePaging(req) orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, openpitrix.CreateTime) - reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, false) conditions, err := params.ParseConditions(req) if err != nil { diff --git a/pkg/kapis/resources/v1alpha2/handler.go b/pkg/kapis/resources/v1alpha2/handler.go index 4eba100e1f0eff31805a5a181ccce708b0ab0bb8..793ae39180bd4f6be696a50adbc622d86bbc35d3 100644 --- a/pkg/kapis/resources/v1alpha2/handler.go +++ b/pkg/kapis/resources/v1alpha2/handler.go @@ -66,18 +66,20 @@ func (r *resourceHandler) handleListNamespaceResources(request *restful.Request, conditions, err := params.ParseConditions(request) if err != nil { - response.WriteHeaderAndEntity(http.StatusBadRequest, err) + klog.Error(err) + api.HandleBadRequest(response, request, err) return } result, err := r.resourcesGetter.ListResources(namespace, resource, conditions, orderBy, reverse, limit, offset) if err != nil { + klog.Error(err) api.HandleInternalError(response, nil, err) return } - response.WriteAsJson(result) + response.WriteEntity(result) } func (r *resourceHandler) handleGetSystemHealthStatus(_ *restful.Request, response *restful.Response) { diff --git a/pkg/kapis/resources/v1alpha3/handler.go b/pkg/kapis/resources/v1alpha3/handler.go index 5b59c5a1f57ac8ea4622cd103cf020935ceb2f3e..38316c3e502d4e4c1c6dca123fa1704ab91df9fe 100644 --- a/pkg/kapis/resources/v1alpha3/handler.go +++ b/pkg/kapis/resources/v1alpha3/handler.go @@ -2,34 +2,56 @@ package v1alpha3 import ( "github.com/emicklei/go-restful" + "k8s.io/klog" "kubesphere.io/kubesphere/pkg/api" "kubesphere.io/kubesphere/pkg/apiserver/query" "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models/components" - "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" - "net/http" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + resourcev1alpha2 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/resource" + resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" + "kubesphere.io/kubesphere/pkg/server/params" + "strings" ) type Handler struct { - namespacedResourceGetter *resource.ResourceGetter - componentsGetter components.ComponentsGetter + resourceGetterV1alpha3 *resourcev1alpha3.ResourceGetter + resourcesGetterV1alpha2 *resourcev1alpha2.ResourceGetter + componentsGetter components.ComponentsGetter } func New(factory informers.InformerFactory) *Handler { return &Handler{ - namespacedResourceGetter: resource.New(factory), - componentsGetter: components.NewComponentsGetter(factory.KubernetesSharedInformerFactory()), + resourceGetterV1alpha3: resourcev1alpha3.NewResourceGetter(factory), + resourcesGetterV1alpha2: resourcev1alpha2.NewResourceGetter(factory), + componentsGetter: components.NewComponentsGetter(factory.KubernetesSharedInformerFactory()), } } // handleListResources retrieves resources -func (h Handler) handleListResources(request *restful.Request, response *restful.Response) { +func (h *Handler) handleListResources(request *restful.Request, response *restful.Response) { query := query.ParseQueryParameter(request) - resource := request.PathParameter("resources") + resourceType := request.PathParameter("resources") namespace := request.PathParameter("namespace") - result, err := h.namespacedResourceGetter.List(resource, namespace, query) + result, err := h.resourceGetterV1alpha3.List(resourceType, namespace, query) + + if err == nil { + response.WriteEntity(result) + return + } + + if err != resourcev1alpha3.ErrResourceNotSupported { + klog.Error(err) + api.HandleInternalError(response, nil, err) + return + } + + // fallback to v1alpha2 + result, err = h.fallback(resourceType, namespace, query) + if err != nil { + klog.Error(err) api.HandleInternalError(response, nil, err) return } @@ -37,38 +59,96 @@ func (h Handler) handleListResources(request *restful.Request, response *restful response.WriteEntity(result) } -func (h Handler) handleGetComponentStatus(request *restful.Request, response *restful.Response) { +func (h *Handler) fallback(resourceType string, namespace string, q *query.Query) (*api.ListResult, error) { + orderBy := string(q.SortBy) + limit, offset := q.Pagination.Limit, q.Pagination.Offset + reverse := !q.Ascending + conditions := ¶ms.Conditions{} + for _, filter := range q.Filters { + switch filter.Field { + case query.FieldName: + conditions.Match[v1alpha2.Name] = string(filter.Value) + break + case query.FieldCreationTimeStamp: + conditions.Match[v1alpha2.CreateTime] = string(filter.Value) + break + case query.FieldLastUpdateTimestamp: + conditions.Match[v1alpha2.UpdateTime] = string(filter.Value) + break + case query.FieldLabel: + values := strings.SplitN(string(filter.Value), ":", 2) + if len(values) == 2 { + conditions.Match[values[0]] = values[1] + } else { + conditions.Match[v1alpha2.Label] = values[0] + } + break + case query.FieldAnnotation: + values := strings.SplitN(string(filter.Value), ":", 2) + if len(values) == 2 { + conditions.Match[v1alpha2.Annotation] = values[1] + } else { + conditions.Match[v1alpha2.Annotation] = values[0] + } + break + case query.FieldStatus: + conditions.Match[v1alpha2.Status] = string(filter.Value) + break + case query.FieldOwnerReference: + conditions.Match[v1alpha2.Owner] = string(filter.Value) + break + } + + } + + result, err := h.resourcesGetterV1alpha2.ListResources(namespace, resourceType, conditions, orderBy, reverse, limit, offset) + + if err != nil { + klog.Error(err) + return nil, err + } + + return &api.ListResult{ + Items: result.Items, + TotalItems: result.TotalCount, + }, nil +} + +func (h *Handler) handleGetComponentStatus(request *restful.Request, response *restful.Response) { component := request.PathParameter("component") result, err := h.componentsGetter.GetComponentStatus(component) if err != nil { + klog.Error(err) api.HandleInternalError(response, nil, err) return } - response.WriteHeaderAndEntity(http.StatusOK, result) + response.WriteEntity(result) } -func (h Handler) handleGetSystemHealthStatus(request *restful.Request, response *restful.Response) { +func (h *Handler) handleGetSystemHealthStatus(request *restful.Request, response *restful.Response) { result, err := h.componentsGetter.GetSystemHealthStatus() if err != nil { + klog.Error(err) api.HandleInternalError(response, nil, err) return } - response.WriteHeaderAndEntity(http.StatusOK, result) + response.WriteEntity(result) } // get all componentsHandler -func (h Handler) handleGetComponents(request *restful.Request, response *restful.Response) { +func (h *Handler) handleGetComponents(request *restful.Request, response *restful.Response) { result, err := h.componentsGetter.GetAllComponentsStatus() if err != nil { + klog.Error(err) api.HandleInternalError(response, nil, err) return } - response.WriteHeaderAndEntity(http.StatusOK, result) + response.WriteEntity(result) } diff --git a/pkg/kapis/resources/v1alpha3/handler_test.go b/pkg/kapis/resources/v1alpha3/handler_test.go index 6ef3f09c5aed8e8907c4d9a4c3a9ef303c3bc6a3..1fb67ebad342879d7f651e038e0bc8ba5919d42c 100644 --- a/pkg/kapis/resources/v1alpha3/handler_test.go +++ b/pkg/kapis/resources/v1alpha3/handler_test.go @@ -1,7 +1,191 @@ package v1alpha3 -import "testing" +import ( + "github.com/google/go-cmp/cmp" + fakeapp "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake" + fakeistio "istio.io/client-go/pkg/clientset/versioned/fake" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + fakek8s "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + fakeks "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + "kubesphere.io/kubesphere/pkg/informers" + resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" + "testing" +) -func TestComponentHandler(t *testing.T) { +func TestResourceV1alpha2Fallback(t *testing.T) { + tests := []struct { + description string + namespace string + resource string + query *query.Query + expectedError error + expected *api.ListResult + }{ + { + description: "list namespaces", + namespace: "default", + resource: "namespaces", + query: &query.Query{ + Pagination: &query.Pagination{ + Limit: 10, + Offset: 0, + }, + SortBy: "name", + Ascending: false, + Filters: nil, + }, + expectedError: nil, + expected: &api.ListResult{ + Items: []interface{}{kubesphereNamespace, defaultNamespace}, + TotalItems: 2, + }, + }, + { + description: "list secrets fallback", + namespace: "default", + resource: "secrets", + query: &query.Query{ + Pagination: &query.Pagination{ + Limit: 10, + Offset: 0, + }, + SortBy: "name", + Ascending: false, + Filters: nil, + }, + expectedError: nil, + expected: &api.ListResult{ + Items: []interface{}{secretFoo2, secretFoo1}, + TotalItems: 2, + }, + }, + } + factory, err := prepare() + if err != nil { + t.Fatal(err) + } + + handler := New(factory) + + for _, test := range tests { + got, err := listResources(test.namespace, test.resource, test.query, handler) + + if err != test.expectedError { + t.Fatalf("expected error: %s, got: %s", test.expectedError, err) + } + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + } +} + +func listResources(namespace, resourceType string, query *query.Query, h *Handler) (*api.ListResult, error) { + + result, err := h.resourceGetterV1alpha3.List(resourceType, namespace, query) + + if err == nil { + return result, nil + } + + if err != resourcev1alpha3.ErrResourceNotSupported { + return nil, err + } + + // fallback to v1alpha2 + return h.fallback(resourceType, namespace, query) +} + +var ( + defaultNamespace = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + Labels: map[string]string{"kubesphere.io/workspace": "system-workspace"}, + }, + } + kubesphereNamespace = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "kubesphere-system", + Labels: map[string]string{"kubesphere.io/workspace": "system-workspace"}, + }, + } + secretFoo1 = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + Namespace: "default", + }, + } + secretFoo2 = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + Namespace: "default", + }, + } + + replicas = int32(1) + + nginxDeployment = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "default", + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + }, + Status: appsv1.DeploymentStatus{ + ReadyReplicas: 1, + }, + } + redisDeployment = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "redis", + Namespace: "default", + Labels: map[string]string{"kubesphere.io/creator": "admin"}, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + }, + Status: appsv1.DeploymentStatus{ + ReadyReplicas: 0, + }, + } + deployments = []interface{}{redisDeployment, nginxDeployment} + namespaces = []interface{}{defaultNamespace, kubesphereNamespace} + secrets = []interface{}{secretFoo1, secretFoo2} +) + +func prepare() (informers.InformerFactory, error) { + + ksClient := fakeks.NewSimpleClientset() + k8sClient := fakek8s.NewSimpleClientset() + istioClient := fakeistio.NewSimpleClientset() + appClient := fakeapp.NewSimpleClientset() + fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, istioClient, appClient) + + k8sInformerFactory := fakeInformerFactory.KubernetesSharedInformerFactory() + + for _, namespace := range namespaces { + err := k8sInformerFactory.Core().V1().Namespaces().Informer().GetIndexer().Add(namespace) + if err != nil { + return nil, err + } + } + for _, deployment := range deployments { + err := k8sInformerFactory.Apps().V1().Deployments().Informer().GetIndexer().Add(deployment) + if err != nil { + return nil, err + } + } + for _, secret := range secrets { + err := k8sInformerFactory.Core().V1().Secrets().Informer().GetIndexer().Add(secret) + if err != nil { + return nil, err + } + } + + return fakeInformerFactory, nil } diff --git a/pkg/kapis/tenant/v1alpha2/handler.go b/pkg/kapis/tenant/v1alpha2/handler.go index a4414329da51b13cf27a27a2364b5d22330ff507..ebbe12d8078dd35d5c4b7be0b21471a2f8f17805 100644 --- a/pkg/kapis/tenant/v1alpha2/handler.go +++ b/pkg/kapis/tenant/v1alpha2/handler.go @@ -32,7 +32,7 @@ func (h *tenantHandler) ListWorkspaces(req *restful.Request, resp *restful.Respo return } - result, err := h.tenant.ListWorkspaces(user.GetName()) + result, err := h.tenant.ListWorkspaces(user) if err != nil { api.HandleInternalError(resp, nil, err) @@ -52,9 +52,9 @@ func (h *tenantHandler) ListNamespaces(req *restful.Request, resp *restful.Respo return } - worksapceName := req.PathParameter("workspace") + workspace := req.PathParameter("workspace") - result, err := h.tenant.ListNamespaces(worksapceName, user.GetName()) + result, err := h.tenant.ListNamespaces(user, workspace) if err != nil { api.HandleInternalError(resp, nil, err) diff --git a/pkg/models/iam/am/am.go b/pkg/models/iam/am/am.go index 6bf840141092a9b4112a6bca324dc8804db53095..5fbc6952313a3b7d5cbbe48c2260cdcf6a557e12 100644 --- a/pkg/models/iam/am/am.go +++ b/pkg/models/iam/am/am.go @@ -113,8 +113,7 @@ func (am *amOperator) GetRoleOfUserInTargetScope(scope iamv1alpha2.Scope, target return nil, err } for _, role := range roles { - if role.Target.Name == iamv1alpha2.TargetAll || - role.Target.Name == target { + if role.Target.Name == target { return &role, nil } } diff --git a/pkg/models/resources/v1alpha2/application/appplications.go b/pkg/models/resources/v1alpha2/application/appplications.go index d48c7c0655fe2f9745f0120a1ab1970dceaabeaa..c0994134e56d353f8808c48f09d2d8e55f681e6b 100644 --- a/pkg/models/resources/v1alpha2/application/appplications.go +++ b/pkg/models/resources/v1alpha2/application/appplications.go @@ -22,18 +22,9 @@ import ( "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1" "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions" "k8s.io/apimachinery/pkg/labels" - "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" "sort" - "strings" -) - -const ( - app = "app" - chart = "chart" - release = "release" ) type appSearcher struct { @@ -48,70 +39,24 @@ func (s *appSearcher) Get(namespace, name string) (interface{}, error) { return s.informer.App().V1beta1().Applications().Lister().Applications(namespace).Get(name) } -// exactly Match func (s *appSearcher) match(match map[string]string, item *v1beta1.Application) bool { for k, v := range match { - switch k { - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false } } return true } -// Fuzzy searchInNamespace -func (*appSearcher) fuzzy(fuzzy map[string]string, item *v1beta1.Application) bool { +func (s *appSearcher) fuzzy(fuzzy map[string]string, item *v1beta1.Application) bool { for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - case app: - if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) { - return false - } - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } return true } -func (*appSearcher) compare(a, b *v1beta1.Application, orderBy string) bool { - switch orderBy { - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case v1alpha2.Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - func (s *appSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { apps, err := s.informer.App().V1beta1().Applications().Lister().Applications(namespace).List(labels.Everything()) @@ -134,7 +79,7 @@ func (s *appSearcher) Search(namespace string, conditions *params.Conditions, or if reverse { i, j = j, i } - return s.compare(result[i], result[j], orderBy) + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) }) r := make([]interface{}, 0) diff --git a/pkg/models/resources/v1alpha2/clusterrole/clusterroles.go b/pkg/models/resources/v1alpha2/clusterrole/clusterroles.go index 7af194d9de5bb934fa83d9030c8ea16a5cd8fdf4..a1722813bd017f0c8e275cf5837eda04e3785483 100644 --- a/pkg/models/resources/v1alpha2/clusterrole/clusterroles.go +++ b/pkg/models/resources/v1alpha2/clusterrole/clusterroles.go @@ -18,17 +18,14 @@ package clusterrole import ( + rbac "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/informers" "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" "kubesphere.io/kubesphere/pkg/server/params" "kubesphere.io/kubesphere/pkg/utils/k8sutil" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" "sort" - "strings" - - rbac "k8s.io/api/rbac/v1" - "k8s.io/apimachinery/pkg/labels" ) type clusterRoleSearcher struct { @@ -43,7 +40,6 @@ func (s *clusterRoleSearcher) Get(namespace, name string) (interface{}, error) { return s.informer.Rbac().V1().ClusterRoles().Lister().Get(name) } -// exactly Match func (*clusterRoleSearcher) match(match map[string]string, item *rbac.ClusterRole) bool { for k, v := range match { switch k { @@ -55,15 +51,6 @@ func (*clusterRoleSearcher) match(match map[string]string, item *rbac.ClusterRol if !k8sutil.IsControlledBy(item.OwnerReferences, kind, name) { return false } - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } case v1alpha2.UserFacing: if v == "true" { if !isUserFacingClusterRole(item) { @@ -71,8 +58,7 @@ func (*clusterRoleSearcher) match(match map[string]string, item *rbac.ClusterRol } } default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { return false } } @@ -80,43 +66,15 @@ func (*clusterRoleSearcher) match(match map[string]string, item *rbac.ClusterRol return true } -// Fuzzy searchInNamespace -func (*clusterRoleSearcher) fuzzy(fuzzy map[string]string, item *rbac.ClusterRole) bool { +func (s *clusterRoleSearcher) fuzzy(fuzzy map[string]string, item *rbac.ClusterRole) bool { for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } return true } -func (*clusterRoleSearcher) compare(a, b *rbac.ClusterRole, orderBy string) bool { - switch orderBy { - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case v1alpha2.Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - func (s *clusterRoleSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { clusterRoles, err := s.informer.Rbac().V1().ClusterRoles().Lister().List(labels.Everything()) @@ -137,11 +95,9 @@ func (s *clusterRoleSearcher) Search(namespace string, conditions *params.Condit } sort.Slice(result, func(i, j int) bool { if reverse { - tmp := i - i = j - j = tmp + i, j = j, i } - return s.compare(result[i], result[j], orderBy) + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) }) r := make([]interface{}, 0) diff --git a/pkg/models/resources/v1alpha2/configmap/configmaps.go b/pkg/models/resources/v1alpha2/configmap/configmaps.go index 8ee068061d6e2a011f3a3fbf26c39fbad84594e3..bcb50fce5bd66e1d849c170505a860b9f96fe975 100644 --- a/pkg/models/resources/v1alpha2/configmap/configmaps.go +++ b/pkg/models/resources/v1alpha2/configmap/configmaps.go @@ -18,16 +18,12 @@ package configmap import ( + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/informers" - "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" "sort" - "strings" - - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" ) type configMapSearcher struct { @@ -42,70 +38,24 @@ func (s *configMapSearcher) Get(namespace, name string) (interface{}, error) { return s.informer.Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).Get(name) } -// exactly Match -func (*configMapSearcher) match(match map[string]string, item *v1.ConfigMap) bool { +func (s *configMapSearcher) match(match map[string]string, item *v1.ConfigMap) bool { for k, v := range match { - switch k { - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false } } return true } -// Fuzzy searchInNamespace -func (*configMapSearcher) fuzzy(fuzzy map[string]string, item *v1.ConfigMap) bool { +func (s *configMapSearcher) fuzzy(fuzzy map[string]string, item *v1.ConfigMap) bool { for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - case v1alpha2.App: - if !strings.Contains(item.Labels[v1alpha2.Chart], v) && !strings.Contains(item.Labels[v1alpha2.Release], v) { - return false - } - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } return true } -func (*configMapSearcher) compare(a, b *v1.ConfigMap, orderBy string) bool { - switch orderBy { - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case v1alpha2.Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - func (s *configMapSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { configMaps, err := s.informer.Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).List(labels.Everything()) @@ -128,7 +78,7 @@ func (s *configMapSearcher) Search(namespace string, conditions *params.Conditio if reverse { i, j = j, i } - return s.compare(result[i], result[j], orderBy) + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) }) r := make([]interface{}, 0) diff --git a/pkg/models/resources/v1alpha2/cronjob/cronjobs.go b/pkg/models/resources/v1alpha2/cronjob/cronjobs.go index 3392cdf6ce6f11b4d828e9e2b880b55d271ab567..1fdbd029c394e99dc7412304e3b5cd656fce9135 100644 --- a/pkg/models/resources/v1alpha2/cronjob/cronjobs.go +++ b/pkg/models/resources/v1alpha2/cronjob/cronjobs.go @@ -18,15 +18,11 @@ package cronjob import ( + "k8s.io/api/batch/v1beta1" "k8s.io/client-go/informers" - "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" "sort" - "strings" - - "k8s.io/api/batch/v1beta1" "k8s.io/apimachinery/pkg/labels" ) @@ -50,26 +46,15 @@ func cronJobStatus(item *v1beta1.CronJob) string { return v1alpha2.StatusRunning } -// Exactly Match func (*cronJobSearcher) match(match map[string]string, item *v1beta1.CronJob) bool { for k, v := range match { switch k { - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } case v1alpha2.Status: if cronJobStatus(item) != v { return false } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { return false } } @@ -80,50 +65,26 @@ func (*cronJobSearcher) match(match map[string]string, item *v1beta1.CronJob) bo func (*cronJobSearcher) fuzzy(fuzzy map[string]string, item *v1beta1.CronJob) bool { for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - case v1alpha2.App: - if !strings.Contains(item.Labels[v1alpha2.Chart], v) && !strings.Contains(item.Labels[v1alpha2.Release], v) { - return false - } - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } return true } -func (*cronJobSearcher) compare(a, b *v1beta1.CronJob, orderBy string) bool { +func (*cronJobSearcher) compare(left, right *v1beta1.CronJob, orderBy string) bool { switch orderBy { case v1alpha2.LastScheduleTime: - if a.Status.LastScheduleTime == nil { + if left.Status.LastScheduleTime == nil { return true } - if b.Status.LastScheduleTime == nil { + if right.Status.LastScheduleTime == nil { return false } - return a.Status.LastScheduleTime.Before(b.Status.LastScheduleTime) - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) + return left.Status.LastScheduleTime.Before(right.Status.LastScheduleTime) default: - fallthrough - case v1alpha2.Name: - return strings.Compare(a.Name, b.Name) <= 0 + return v1alpha2.ObjectMetaCompare(left.ObjectMeta, right.ObjectMeta, orderBy) } } @@ -146,10 +107,7 @@ func (c *cronJobSearcher) Search(namespace string, conditions *params.Conditions } } sort.Slice(result, func(i, j int) bool { - if reverse { - i, j = j, i - } - return c.compare(result[i], result[j], orderBy) + return c.compare(result[i], result[j], orderBy) && !reverse }) r := make([]interface{}, 0) diff --git a/pkg/models/resources/v1alpha2/daemonset/daemonsets.go b/pkg/models/resources/v1alpha2/daemonset/daemonsets.go index 5f5f585ba4531dbee1b3de1f69218a6a9ff17f80..99705fa6fdde4b0707de3012252031ca27896780 100644 --- a/pkg/models/resources/v1alpha2/daemonset/daemonsets.go +++ b/pkg/models/resources/v1alpha2/daemonset/daemonsets.go @@ -18,16 +18,12 @@ package daemonset import ( + "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/informers" - "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" "sort" - "strings" - - "k8s.io/api/apps/v1" - "k8s.io/apimachinery/pkg/labels" ) type daemonSetSearcher struct { @@ -52,7 +48,6 @@ func daemonSetStatus(item *v1.DaemonSet) string { } } -// Exactly Match func (*daemonSetSearcher) match(match map[string]string, item *v1.DaemonSet) bool { for k, v := range match { switch k { @@ -60,18 +55,8 @@ func (*daemonSetSearcher) match(match map[string]string, item *v1.DaemonSet) boo if daemonSetStatus(item) != v { return false } - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { return false } } @@ -79,48 +64,15 @@ func (*daemonSetSearcher) match(match map[string]string, item *v1.DaemonSet) boo return true } -func (*daemonSetSearcher) fuzzy(fuzzy map[string]string, item *v1.DaemonSet) bool { - - for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } +func (*daemonSetSearcher) fuzzy(kv map[string]string, item *v1.DaemonSet) bool { + for k, v := range kv { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - case v1alpha2.App: - if !strings.Contains(item.Labels[v1alpha2.Chart], v) && !strings.Contains(item.Labels[v1alpha2.Release], v) { - return false - } - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } - return true } -func (*daemonSetSearcher) compare(a, b *v1.DaemonSet, orderBy string) bool { - switch orderBy { - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case v1alpha2.Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - func (c *daemonSetSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { daemonSets, err := c.informer.Apps().V1().DaemonSets().Lister().DaemonSets(namespace).List(labels.Everything()) @@ -143,7 +95,7 @@ func (c *daemonSetSearcher) Search(namespace string, conditions *params.Conditio if reverse { i, j = j, i } - return c.compare(result[i], result[j], orderBy) + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) }) r := make([]interface{}, 0) diff --git a/pkg/models/resources/v1alpha2/deployment/deployments.go b/pkg/models/resources/v1alpha2/deployment/deployments.go new file mode 100644 index 0000000000000000000000000000000000000000..f7bdf783aa45933a53e1bac38a81889b31851324 --- /dev/null +++ b/pkg/models/resources/v1alpha2/deployment/deployments.go @@ -0,0 +1,155 @@ +/* + * + * 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. + * / + */ + +/* + + 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 deployment + +import ( + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" + "strings" + "time" + + "k8s.io/apimachinery/pkg/labels" + + "k8s.io/api/apps/v1" +) + +type deploymentSearcher struct { + informer informers.SharedInformerFactory +} + +func NewDeploymentSetSearcher(informers informers.SharedInformerFactory) v1alpha2.Interface { + return &deploymentSearcher{informer: informers} +} + +func (s *deploymentSearcher) Get(namespace, name string) (interface{}, error) { + return s.informer.Apps().V1().Deployments().Lister().Deployments(namespace).Get(name) +} + +func deploymentStatus(item *v1.Deployment) string { + if item.Spec.Replicas != nil { + if item.Status.ReadyReplicas == 0 && *item.Spec.Replicas == 0 { + return v1alpha2.StatusStopped + } else if item.Status.ReadyReplicas == *item.Spec.Replicas { + return v1alpha2.StatusRunning + } else { + return v1alpha2.StatusUpdating + } + } + return v1alpha2.StatusStopped +} + +func (*deploymentSearcher) match(kv map[string]string, item *v1.Deployment) bool { + for k, v := range kv { + switch k { + case v1alpha2.Status: + if deploymentStatus(item) != v { + return false + } + default: + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + } + return true +} + +func (*deploymentSearcher) fuzzy(kv map[string]string, item *v1.Deployment) bool { + for k, v := range kv { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *deploymentSearcher) compare(a, b *v1.Deployment, orderBy string) bool { + switch orderBy { + case v1alpha2.UpdateTime: + aLastUpdateTime := s.lastUpdateTime(a) + bLastUpdateTime := s.lastUpdateTime(b) + if aLastUpdateTime.Equal(bLastUpdateTime) { + return strings.Compare(a.Name, b.Name) <= 0 + } + return aLastUpdateTime.Before(bLastUpdateTime) + default: + return v1alpha2.ObjectMetaCompare(a.ObjectMeta, b.ObjectMeta, orderBy) + } +} + +func (s *deploymentSearcher) lastUpdateTime(deployment *v1.Deployment) time.Time { + lastUpdateTime := deployment.CreationTimestamp.Time + for _, condition := range deployment.Status.Conditions { + if condition.LastUpdateTime.After(lastUpdateTime) { + lastUpdateTime = condition.LastUpdateTime.Time + } + } + return lastUpdateTime +} + +func (s *deploymentSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + deployments, err := s.informer.Apps().V1().Deployments().Lister().Deployments(namespace).List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*v1.Deployment, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = deployments + } else { + for _, item := range deployments { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return s.compare(result[i], result[j], orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} diff --git a/pkg/models/resources/v1alpha2/hpa/horizontalpodautoscalers.go b/pkg/models/resources/v1alpha2/hpa/horizontalpodautoscalers.go index f1010cd76600af1be0fb7a619edcfbd754c7badd..7aa11977644729ede7d64434c85af9314dea050b 100644 --- a/pkg/models/resources/v1alpha2/hpa/horizontalpodautoscalers.go +++ b/pkg/models/resources/v1alpha2/hpa/horizontalpodautoscalers.go @@ -19,15 +19,11 @@ package hpa import ( autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2" + "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/informers" - "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" "sort" - "strings" - - "k8s.io/apimachinery/pkg/labels" ) type hpaSearcher struct { @@ -58,17 +54,8 @@ func (*hpaSearcher) match(match map[string]string, item *autoscalingv2beta2.Hori if !hpaTargetMatch(item, kind, name) { return false } - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } default: - if item.Labels[k] != v { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { return false } } @@ -76,47 +63,15 @@ func (*hpaSearcher) match(match map[string]string, item *autoscalingv2beta2.Hori return true } -// Fuzzy searchInNamespace func (*hpaSearcher) fuzzy(fuzzy map[string]string, item *autoscalingv2beta2.HorizontalPodAutoscaler) bool { for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - case v1alpha2.App: - if !strings.Contains(item.Labels[v1alpha2.Chart], v) && !strings.Contains(item.Labels[v1alpha2.Release], v) { - return false - } - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) && !v1alpha2.SearchFuzzy(item.Annotations, k, v) { - return false - } } } return true } -func (*hpaSearcher) compare(a, b *autoscalingv2beta2.HorizontalPodAutoscaler, orderBy string) bool { - switch orderBy { - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case v1alpha2.Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - func (s *hpaSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { horizontalPodAutoscalers, err := s.informers.Autoscaling().V2beta2().HorizontalPodAutoscalers().Lister().HorizontalPodAutoscalers(namespace).List(labels.Everything()) @@ -138,11 +93,9 @@ func (s *hpaSearcher) Search(namespace string, conditions *params.Conditions, or } sort.Slice(result, func(i, j int) bool { if reverse { - tmp := i - i = j - j = tmp + i, j = j, i } - return s.compare(result[i], result[j], orderBy) + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) }) r := make([]interface{}, 0) diff --git a/pkg/models/resources/v1alpha2/ingress/ingresses.go b/pkg/models/resources/v1alpha2/ingress/ingresses.go index 387e5d0b7f6af0d9917ca935cf1aae49d5bcabd1..9bfc3e9eb184810a48a0ba8aa5b603a6e9468c29 100644 --- a/pkg/models/resources/v1alpha2/ingress/ingresses.go +++ b/pkg/models/resources/v1alpha2/ingress/ingresses.go @@ -18,15 +18,11 @@ package ingress import ( + "k8s.io/api/extensions/v1beta1" "k8s.io/client-go/informers" - "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" "sort" - "strings" - - "k8s.io/api/extensions/v1beta1" "k8s.io/apimachinery/pkg/labels" ) @@ -43,70 +39,24 @@ func (s *ingressSearcher) Get(namespace, name string) (interface{}, error) { return s.informers.Extensions().V1beta1().Ingresses().Lister().Ingresses(namespace).Get(name) } -// exactly Match func (*ingressSearcher) match(match map[string]string, item *v1beta1.Ingress) bool { for k, v := range match { - switch k { - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false } } return true } -// Fuzzy searchInNamespace func (*ingressSearcher) fuzzy(fuzzy map[string]string, item *v1beta1.Ingress) bool { for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - case v1alpha2.App: - if !strings.Contains(item.Labels[v1alpha2.Chart], v) && !strings.Contains(item.Labels[v1alpha2.Release], v) { - return false - } - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } return true } -func (*ingressSearcher) compare(a, b *v1beta1.Ingress, orderBy string) bool { - switch orderBy { - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case v1alpha2.Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - func (s *ingressSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { ingresses, err := s.informers.Extensions().V1beta1().Ingresses().Lister().Ingresses(namespace).List(labels.Everything()) @@ -127,11 +77,9 @@ func (s *ingressSearcher) Search(namespace string, conditions *params.Conditions } sort.Slice(result, func(i, j int) bool { if reverse { - tmp := i - i = j - j = tmp + i, j = j, i } - return s.compare(result[i], result[j], orderBy) + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) }) r := make([]interface{}, 0) diff --git a/pkg/models/resources/v1alpha2/interface.go b/pkg/models/resources/v1alpha2/interface.go index acf5f5f3250fbbbbe0cf8d234ac44d852f3d5a7e..11f556e5d88dce448e55694407978249c829b887 100644 --- a/pkg/models/resources/v1alpha2/interface.go +++ b/pkg/models/resources/v1alpha2/interface.go @@ -1,7 +1,10 @@ package v1alpha2 import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/server/params" + "kubesphere.io/kubesphere/pkg/utils/sliceutil" "strings" ) @@ -24,6 +27,7 @@ const ( Keyword = "keyword" UserFacing = "userfacing" Status = "status" + Owner = "owner" StatusRunning = "running" StatusPaused = "paused" @@ -66,7 +70,61 @@ type Interface interface { Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) } -func SearchFuzzy(m map[string]string, key, value string) bool { +func ObjectMetaExactlyMath(key, value string, item metav1.ObjectMeta) bool { + switch key { + case Name: + names := strings.Split(value, "|") + if !sliceutil.HasString(names, item.Name) { + return false + } + case Keyword: + if !strings.Contains(item.Name, value) && !FuzzyMatch(item.Labels, "", value) && !FuzzyMatch(item.Annotations, "", value) { + return false + } + default: + // label not exist or value not equal + if val, ok := item.Labels[key]; !ok || val != value { + return false + } + } + return true +} + +func ObjectMetaFuzzyMath(key, value string, item metav1.ObjectMeta) bool { + switch key { + case Name: + if !strings.Contains(item.Name, value) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], value) { + return false + } + case Label: + if !FuzzyMatch(item.Labels, "", value) { + return false + } + case Annotation: + if !FuzzyMatch(item.Annotations, "", value) { + return false + } + return false + case App: + if !strings.Contains(item.Labels[Chart], value) && !strings.Contains(item.Labels[Release], value) { + return false + } + case Owner: + for _, ownerReference := range item.OwnerReferences { + if strings.Compare(string(ownerReference.UID), value) == 0 { + return true + } + } + return false + default: + if !FuzzyMatch(item.Labels, key, value) { + return false + } + } + return true +} + +func FuzzyMatch(m map[string]string, key, value string) bool { val, exist := m[key] @@ -78,3 +136,17 @@ func SearchFuzzy(m map[string]string, key, value string) bool { return false } + +func ObjectMetaCompare(left, right metav1.ObjectMeta, compareField string) bool { + switch compareField { + case CreateTime: + if left.CreationTimestamp.Equal(&right.CreationTimestamp) { + return strings.Compare(left.Name, right.Name) <= 0 + } + return left.CreationTimestamp.Time.Before(right.CreationTimestamp.Time) + case Name: + fallthrough + default: + return strings.Compare(left.Name, right.Name) <= 0 + } +} diff --git a/pkg/models/resources/v1alpha2/job/jobs.go b/pkg/models/resources/v1alpha2/job/jobs.go index 509d3e2aefb5e7b22ab84371c143f40f5e07b0cd..9530d6c7bb92b2ce354907c5a29e507beabdda7f 100644 --- a/pkg/models/resources/v1alpha2/job/jobs.go +++ b/pkg/models/resources/v1alpha2/job/jobs.go @@ -19,14 +19,11 @@ package job import ( "k8s.io/client-go/informers" - "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" "kubesphere.io/kubesphere/pkg/server/params" "kubesphere.io/kubesphere/pkg/utils/k8sutil" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" "sort" - "strings" "time" batchv1 "k8s.io/api/batch/v1" @@ -64,7 +61,6 @@ func jobStatus(item *batchv1.Job) string { return status } -// Exactly Match func (*jobSearcher) match(match map[string]string, item *batchv1.Job) bool { for k, v := range match { switch k { @@ -80,18 +76,8 @@ func (*jobSearcher) match(match map[string]string, item *batchv1.Job) bool { if v == "false" && k8sutil.IsControlledBy(item.OwnerReferences, s2iRunKind, "") { return false } - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { return false } } @@ -100,33 +86,11 @@ func (*jobSearcher) match(match map[string]string, item *batchv1.Job) bool { } func (*jobSearcher) fuzzy(fuzzy map[string]string, item *batchv1.Job) bool { - for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - case v1alpha2.App: - if !strings.Contains(item.Labels[v1alpha2.Chart], v) && !strings.Contains(item.Labels[v1alpha2.Release], v) { - return false - } - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } - return true } @@ -143,16 +107,12 @@ func jobUpdateTime(item *batchv1.Job) time.Time { return updateTime } -func (*jobSearcher) compare(a, b *batchv1.Job, orderBy string) bool { +func (*jobSearcher) compare(left, right *batchv1.Job, orderBy string) bool { switch orderBy { - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) case v1alpha2.UpdateTime: - return jobUpdateTime(a).Before(jobUpdateTime(b)) - case v1alpha2.Name: - fallthrough + return jobUpdateTime(left).Before(jobUpdateTime(right)) default: - return strings.Compare(a.Name, b.Name) <= 0 + return v1alpha2.ObjectMetaCompare(left.ObjectMeta, right.ObjectMeta, orderBy) } } @@ -176,9 +136,7 @@ func (s *jobSearcher) Search(namespace string, conditions *params.Conditions, or } sort.Slice(result, func(i, j int) bool { if reverse { - tmp := i - i = j - j = tmp + i, j = j, i } return s.compare(result[i], result[j], orderBy) }) diff --git a/pkg/models/resources/v1alpha2/namespace/namespaces.go b/pkg/models/resources/v1alpha2/namespace/namespaces.go index ff206df261070775b708cb99f72c5ba865401a1f..1faf5987de5f429a9b64b9a4f0322f4491901803 100644 --- a/pkg/models/resources/v1alpha2/namespace/namespaces.go +++ b/pkg/models/resources/v1alpha2/namespace/namespaces.go @@ -19,16 +19,12 @@ package namespace import ( "k8s.io/client-go/informers" - "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" ) type namespaceSearcher struct { @@ -43,66 +39,24 @@ func (s *namespaceSearcher) Get(namespace, name string) (interface{}, error) { return s.informers.Core().V1().Namespaces().Lister().Get(name) } -// exactly Match func (*namespaceSearcher) match(match map[string]string, item *v1.Namespace) bool { for k, v := range match { - switch k { - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false } } return true } -// Fuzzy searchInNamespace func (*namespaceSearcher) fuzzy(fuzzy map[string]string, item *v1.Namespace) bool { for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } return true } -func (*namespaceSearcher) compare(a, b *v1.Namespace, orderBy string) bool { - switch orderBy { - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case v1alpha2.Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - func (s *namespaceSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { namespaces, err := s.informers.Core().V1().Namespaces().Lister().List(labels.Everything()) @@ -123,11 +77,9 @@ func (s *namespaceSearcher) Search(namespace string, conditions *params.Conditio } sort.Slice(result, func(i, j int) bool { if reverse { - tmp := i - i = j - j = tmp + i, j = j, i } - return s.compare(result[i], result[j], orderBy) + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) }) r := make([]interface{}, 0) diff --git a/pkg/models/resources/v1alpha2/node/nodes.go b/pkg/models/resources/v1alpha2/node/nodes.go index fff798e77e4d6028d7c3473267557bfec799ab1b..62865a311eead676336e20c0b07116b504505219 100644 --- a/pkg/models/resources/v1alpha2/node/nodes.go +++ b/pkg/models/resources/v1alpha2/node/nodes.go @@ -20,16 +20,12 @@ package node import ( "fmt" "k8s.io/client-go/informers" - "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" ) const ( @@ -80,15 +76,9 @@ func isUnhealthyStatus(condition v1.NodeCondition) bool { return false } -// exactly Match func (*nodeSearcher) match(match map[string]string, item *v1.Node) bool { for k, v := range match { switch k { - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } case v1alpha2.Role: labelKey := fmt.Sprintf("node-role.kubernetes.io/%s", v) if _, ok := item.Labels[labelKey]; !ok { @@ -98,13 +88,8 @@ func (*nodeSearcher) match(match map[string]string, item *v1.Node) bool { if getNodeStatus(item) != v { return false } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { return false } } @@ -112,43 +97,15 @@ func (*nodeSearcher) match(match map[string]string, item *v1.Node) bool { return true } -// Fuzzy searchInNamespace func (*nodeSearcher) fuzzy(fuzzy map[string]string, item *v1.Node) bool { for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } return true } -func (*nodeSearcher) compare(a, b *v1.Node, orderBy string) bool { - switch orderBy { - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case v1alpha2.Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - func (s *nodeSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { nodes, err := s.informers.Core().V1().Nodes().Lister().List(labels.Everything()) @@ -169,11 +126,9 @@ func (s *nodeSearcher) Search(namespace string, conditions *params.Conditions, o } sort.Slice(result, func(i, j int) bool { if reverse { - tmp := i - i = j - j = tmp + i, j = j, i } - return s.compare(result[i], result[j], orderBy) + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) }) r := make([]interface{}, 0) diff --git a/pkg/models/resources/v1alpha2/persistentvolumeclaim/persistentvolumeclaims.go b/pkg/models/resources/v1alpha2/persistentvolumeclaim/persistentvolumeclaims.go index df67040927f6172803993c3a271a7d746606b9ef..5162178617c3743e92235ee55ad5974bbfecaaa8 100644 --- a/pkg/models/resources/v1alpha2/persistentvolumeclaim/persistentvolumeclaims.go +++ b/pkg/models/resources/v1alpha2/persistentvolumeclaim/persistentvolumeclaims.go @@ -19,7 +19,6 @@ package persistentvolumeclaim import ( "k8s.io/client-go/informers" - "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" "strconv" @@ -60,15 +59,9 @@ func pvcStatus(item *v1.PersistentVolumeClaim) string { return status } -// exactly Match func (*persistentVolumeClaimSearcher) match(match map[string]string, item *v1.PersistentVolumeClaim) bool { for k, v := range match { switch k { - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } case v1alpha2.Status: statuses := strings.Split(v, "|") if !sliceutil.HasString(statuses, pvcStatus(item)) { @@ -78,13 +71,8 @@ func (*persistentVolumeClaimSearcher) match(match map[string]string, item *v1.Pe if item.Spec.StorageClassName == nil || *item.Spec.StorageClassName != v { return false } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { return false } } @@ -92,47 +80,15 @@ func (*persistentVolumeClaimSearcher) match(match map[string]string, item *v1.Pe return true } -// Fuzzy searchInNamespace func (*persistentVolumeClaimSearcher) fuzzy(fuzzy map[string]string, item *v1.PersistentVolumeClaim) bool { for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - case v1alpha2.App: - if !strings.Contains(item.Labels[v1alpha2.Chart], v) && !strings.Contains(item.Labels[v1alpha2.Release], v) { - return false - } - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } return true } -func (*persistentVolumeClaimSearcher) compare(a, b *v1.PersistentVolumeClaim, orderBy string) bool { - switch orderBy { - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case v1alpha2.Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - func (s *persistentVolumeClaimSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { persistentVolumeClaims, err := s.informers.Core().V1().PersistentVolumeClaims().Lister().PersistentVolumeClaims(namespace).List(labels.Everything()) @@ -153,11 +109,9 @@ func (s *persistentVolumeClaimSearcher) Search(namespace string, conditions *par } sort.Slice(result, func(i, j int) bool { if reverse { - tmp := i - i = j - j = tmp + i, j = j, i } - return s.compare(result[i], result[j], orderBy) + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) }) r := make([]interface{}, 0) diff --git a/pkg/models/resources/v1alpha2/pod/pods.go b/pkg/models/resources/v1alpha2/pod/pods.go index 3ee226595cf41d68dd9c5b400b55c4ce99e3014b..fa980b46cc4723972bb0e4f1e895675705dc9ceb 100644 --- a/pkg/models/resources/v1alpha2/pod/pods.go +++ b/pkg/models/resources/v1alpha2/pod/pods.go @@ -20,16 +20,12 @@ package pod import ( appsv1 "k8s.io/api/apps/v1" "k8s.io/client-go/informers" - "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" ) type podSearcher struct { @@ -153,7 +149,6 @@ func (s *podSearcher) podBelongToService(item *v1.Pod, serviceName string) bool return true } -// exactly Match func (s *podSearcher) match(match map[string]string, item *v1.Pod) bool { for k, v := range match { switch k { @@ -177,18 +172,8 @@ func (s *podSearcher) match(match map[string]string, item *v1.Pod) bool { if !s.podBelongToService(item, v) { return false } - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { return false } } @@ -196,52 +181,27 @@ func (s *podSearcher) match(match map[string]string, item *v1.Pod) bool { return true } -// Fuzzy searchInNamespace func (*podSearcher) fuzzy(fuzzy map[string]string, item *v1.Pod) bool { for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - case v1alpha2.App: - if !strings.Contains(item.Labels[v1alpha2.Chart], v) && !strings.Contains(item.Labels[v1alpha2.Release], v) { - return false - } - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } return true } -func (*podSearcher) compare(a, b *v1.Pod, orderBy string) bool { +func (*podSearcher) compare(left, right *v1.Pod, orderBy string) bool { switch orderBy { case v1alpha2.StartTime: - if a.Status.StartTime == nil { + if left.Status.StartTime == nil { return false } - if b.Status.StartTime == nil { + if right.Status.StartTime == nil { return true } - return a.Status.StartTime.Before(b.Status.StartTime) - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case v1alpha2.Name: - fallthrough + return left.Status.StartTime.Before(right.Status.StartTime) default: - return strings.Compare(a.Name, b.Name) <= 0 + return v1alpha2.ObjectMetaCompare(left.ObjectMeta, right.ObjectMeta, orderBy) } } @@ -266,9 +226,7 @@ func (s *podSearcher) Search(namespace string, conditions *params.Conditions, or } sort.Slice(result, func(i, j int) bool { if reverse { - tmp := i - i = j - j = tmp + i, j = j, i } return s.compare(result[i], result[j], orderBy) }) diff --git a/pkg/models/resources/v1alpha2/resource/resource_test.go b/pkg/models/resources/v1alpha2/resource/resource_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c55ed5555f0d66af3174ec135cd030dc144cb060 --- /dev/null +++ b/pkg/models/resources/v1alpha2/resource/resource_test.go @@ -0,0 +1,236 @@ +/* + * + * 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 resource + +import ( + "github.com/google/go-cmp/cmp" + fakeapp "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake" + fakeistio "istio.io/client-go/pkg/clientset/versioned/fake" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + fakek8s "k8s.io/client-go/kubernetes/fake" + fakeks "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/models" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "kubesphere.io/kubesphere/pkg/server/params" + "testing" +) + +func TestConditions(t *testing.T) { + factory, err := prepare() + if err != nil { + t.Fatal(err) + } + resource := NewResourceGetter(factory) + + tests := []struct { + Name string + Namespace string + Resource string + Conditions *params.Conditions + OrderBy string + Reverse bool + Limit int + Offset int + ExpectResponse *models.PageableResponse + ExpectError error + }{{ + Name: "list namespace order by name asc", + Namespace: "", + Resource: "namespaces", + Conditions: ¶ms.Conditions{}, + OrderBy: "name", + Reverse: false, + Limit: 10, + Offset: 0, + ExpectResponse: &models.PageableResponse{ + Items: []interface{}{defaultNamespace, kubesphereNamespace}, + TotalCount: 2, + }, + ExpectError: nil, + }, { + Name: "list namespace order by name desc", + Namespace: "", + Resource: "namespaces", + Conditions: ¶ms.Conditions{}, + OrderBy: "name", + Reverse: true, + Limit: 10, + Offset: 0, + ExpectResponse: &models.PageableResponse{ + Items: []interface{}{kubesphereNamespace, defaultNamespace}, + TotalCount: 2, + }, + ExpectError: nil, + }, + { + Name: "list deployment", + Namespace: "default", + Resource: "deployments", + Conditions: ¶ms.Conditions{}, + OrderBy: "name", + Reverse: false, + Limit: 10, + Offset: 0, + ExpectResponse: &models.PageableResponse{ + Items: []interface{}{nginxDeployment, redisDeployment}, + TotalCount: 2, + }, + ExpectError: nil, + }, + { + Name: "filter deployment by keyword", + Namespace: "default", + Resource: "deployments", + Conditions: ¶ms.Conditions{ + Match: map[string]string{v1alpha2.Keyword: "ngin"}, + Fuzzy: nil, + }, + OrderBy: "name", + Reverse: true, + Limit: 10, + Offset: 0, + ExpectResponse: &models.PageableResponse{ + Items: []interface{}{nginxDeployment}, + TotalCount: 1, + }, + ExpectError: nil, + }, + { + Name: "filter deployment by label", + Namespace: "default", + Resource: "deployments", + Conditions: ¶ms.Conditions{ + Match: map[string]string{"kubesphere.io/creator": "admin"}, + Fuzzy: nil, + }, + OrderBy: "", + Reverse: true, + Limit: 10, + Offset: 0, + ExpectResponse: &models.PageableResponse{ + Items: []interface{}{redisDeployment}, + TotalCount: 1, + }, + ExpectError: nil, + }, { + Name: "filter deployment by status", + Namespace: "default", + Resource: "deployments", + Conditions: ¶ms.Conditions{ + Match: map[string]string{v1alpha2.Status: v1alpha2.StatusRunning}, + Fuzzy: nil, + }, + OrderBy: "", + Reverse: true, + Limit: 10, + Offset: 0, + ExpectResponse: &models.PageableResponse{ + Items: []interface{}{nginxDeployment}, + TotalCount: 1, + }, + ExpectError: nil, + }, + } + + for _, test := range tests { + response, err := resource.ListResources(test.Namespace, test.Resource, test.Conditions, test.OrderBy, test.Reverse, test.Limit, test.Offset) + if err != test.ExpectError { + t.Fatalf("expected error: %s, got: %s", test.ExpectError, err) + } + if diff := cmp.Diff(test.ExpectResponse, response); diff != "" { + t.Errorf(diff) + } + } + +} + +var ( + defaultNamespace = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + Labels: map[string]string{"kubesphere.io/workspace": "system-workspace"}, + }, + } + kubesphereNamespace = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "kubesphere-system", + Labels: map[string]string{"kubesphere.io/workspace": "system-workspace"}, + }, + } + + replicas = int32(1) + + nginxDeployment = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "default", + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + }, + Status: appsv1.DeploymentStatus{ + ReadyReplicas: 1, + }, + } + redisDeployment = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "redis", + Namespace: "default", + Labels: map[string]string{"kubesphere.io/creator": "admin"}, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + }, + Status: appsv1.DeploymentStatus{ + ReadyReplicas: 0, + }, + } +) + +func prepare() (informers.InformerFactory, error) { + + namespaces := []interface{}{defaultNamespace, kubesphereNamespace} + deployments := []interface{}{nginxDeployment, redisDeployment} + + ksClient := fakeks.NewSimpleClientset() + k8sClient := fakek8s.NewSimpleClientset() + istioClient := fakeistio.NewSimpleClientset() + appClient := fakeapp.NewSimpleClientset() + fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, istioClient, appClient) + + k8sInformerFactory := fakeInformerFactory.KubernetesSharedInformerFactory() + + for _, namespace := range namespaces { + err := k8sInformerFactory.Core().V1().Namespaces().Informer().GetIndexer().Add(namespace) + if err != nil { + return nil, err + } + } + for _, deployment := range deployments { + err := k8sInformerFactory.Apps().V1().Deployments().Informer().GetIndexer().Add(deployment) + if err != nil { + return nil, err + } + } + + return fakeInformerFactory, nil +} diff --git a/pkg/models/resources/v1alpha2/resource/resources.go b/pkg/models/resources/v1alpha2/resource/resources.go index bdd9c6e8ac1fdbfe0fca156703c1786ff0f6dd2f..ffdae390054f25c64274af70ef47d4a414cb65e1 100644 --- a/pkg/models/resources/v1alpha2/resource/resources.go +++ b/pkg/models/resources/v1alpha2/resource/resources.go @@ -28,6 +28,7 @@ import ( "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/configmap" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/cronjob" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/daemonset" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/deployment" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/hpa" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/ingress" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/job" @@ -62,11 +63,10 @@ func (r ResourceGetter) Add(resource string, getter v1alpha2.Interface) { func NewResourceGetter(factory informers.InformerFactory) *ResourceGetter { resourceGetters := make(map[string]v1alpha2.Interface) - //resourceGetters[v1alpha2.Deployments] = deployments resourceGetters[v1alpha2.ConfigMaps] = configmap.NewConfigmapSearcher(factory.KubernetesSharedInformerFactory()) resourceGetters[v1alpha2.CronJobs] = cronjob.NewCronJobSearcher(factory.KubernetesSharedInformerFactory()) resourceGetters[v1alpha2.DaemonSets] = daemonset.NewDaemonSetSearcher(factory.KubernetesSharedInformerFactory()) - // resourceGetters[Deployments] = + resourceGetters[v1alpha2.Deployments] = deployment.NewDeploymentSetSearcher(factory.KubernetesSharedInformerFactory()) resourceGetters[v1alpha2.Ingresses] = ingress.NewIngressSearcher(factory.KubernetesSharedInformerFactory()) resourceGetters[v1alpha2.Jobs] = job.NewJobSearcher(factory.KubernetesSharedInformerFactory()) resourceGetters[v1alpha2.PersistentVolumeClaims] = persistentvolumeclaim.NewPersistentVolumeClaimSearcher(factory.KubernetesSharedInformerFactory()) @@ -75,18 +75,16 @@ func NewResourceGetter(factory informers.InformerFactory) *ResourceGetter { resourceGetters[v1alpha2.StatefulSets] = statefulset.NewStatefulSetSearcher(factory.KubernetesSharedInformerFactory()) resourceGetters[v1alpha2.Pods] = pod.NewPodSearcher(factory.KubernetesSharedInformerFactory()) resourceGetters[v1alpha2.Roles] = role.NewRoleSearcher(factory.KubernetesSharedInformerFactory()) + resourceGetters[v1alpha2.Nodes] = node.NewNodeSearcher(factory.KubernetesSharedInformerFactory()) resourceGetters[v1alpha2.Namespaces] = namespace.NewNamespaceSearcher(factory.KubernetesSharedInformerFactory()) resourceGetters[v1alpha2.ClusterRoles] = clusterrole.NewClusterRoleSearcher(factory.KubernetesSharedInformerFactory()) resourceGetters[v1alpha2.StorageClasses] = storageclass.NewStorageClassesSearcher(factory.KubernetesSharedInformerFactory()) resourceGetters[v1alpha2.HorizontalPodAutoscalers] = hpa.NewHpaSearcher(factory.KubernetesSharedInformerFactory()) - resourceGetters[v1alpha2.S2iBuilders] = s2ibuilder.NewS2iBuilderSearcher(factory.KubeSphereSharedInformerFactory()) resourceGetters[v1alpha2.S2iRuns] = s2irun.NewS2iRunSearcher(factory.KubeSphereSharedInformerFactory()) resourceGetters[v1alpha2.S2iBuilderTemplates] = s2buildertemplate.NewS2iBuidlerTemplateSearcher(factory.KubeSphereSharedInformerFactory()) - resourceGetters[v1alpha2.Workspaces] = workspace.NewWorkspaceSearcher(factory.KubeSphereSharedInformerFactory()) - resourceGetters[v1alpha2.Applications] = application.NewApplicationSearcher(factory.ApplicationSharedInformerFactory()) return &ResourceGetter{resourcesGetters: resourceGetters} diff --git a/pkg/models/resources/v1alpha2/role/roles.go b/pkg/models/resources/v1alpha2/role/roles.go index 3f651a8edd1271c8e27dac877008a441b0cd8321..b045a28ff0ac4211bc39b7c11c0f254956d90a22 100644 --- a/pkg/models/resources/v1alpha2/role/roles.go +++ b/pkg/models/resources/v1alpha2/role/roles.go @@ -22,13 +22,10 @@ import ( "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - rbac "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" ) type roleSearcher struct { @@ -43,19 +40,9 @@ func (s *roleSearcher) Get(namespace, name string) (interface{}, error) { return s.informers.Rbac().V1().Roles().Lister().Roles(namespace).Get(name) } -// exactly Match func (*roleSearcher) match(match map[string]string, item *rbac.Role) bool { for k, v := range match { switch k { - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } case v1alpha2.UserFacing: if v == "true" { if !isUserFacingRole(item) { @@ -63,8 +50,7 @@ func (*roleSearcher) match(match map[string]string, item *rbac.Role) bool { } } default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { return false } } @@ -72,43 +58,15 @@ func (*roleSearcher) match(match map[string]string, item *rbac.Role) bool { return true } -// Fuzzy searchInNamespace func (*roleSearcher) fuzzy(fuzzy map[string]string, item *rbac.Role) bool { for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } return true } -func (*roleSearcher) compare(a, b *rbac.Role, orderBy string) bool { - switch orderBy { - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case v1alpha2.Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - func (s *roleSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { roles, err := s.informers.Rbac().V1().Roles().Lister().Roles(namespace).List(labels.Everything()) @@ -129,11 +87,9 @@ func (s *roleSearcher) Search(namespace string, conditions *params.Conditions, o } sort.Slice(result, func(i, j int) bool { if reverse { - tmp := i - i = j - j = tmp + i, j = j, i } - return s.compare(result[i], result[j], orderBy) + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) }) r := make([]interface{}, 0) diff --git a/pkg/models/resources/v1alpha2/s2buildertemplate/s2ibuildertemplate.go b/pkg/models/resources/v1alpha2/s2buildertemplate/s2ibuildertemplate.go index fd644204413eccce6319b2f343c33260c509072d..b2d81c93de7bd7e7047b00de9e01b319960b96ce 100644 --- a/pkg/models/resources/v1alpha2/s2buildertemplate/s2ibuildertemplate.go +++ b/pkg/models/resources/v1alpha2/s2buildertemplate/s2ibuildertemplate.go @@ -20,15 +20,11 @@ package s2buildertemplate import ( "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" "kubesphere.io/kubesphere/pkg/client/informers/externalversions" - "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "k8s.io/apimachinery/pkg/labels" "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" "sort" - "strings" - - "k8s.io/apimachinery/pkg/labels" ) type s2iBuilderTemplateSearcher struct { @@ -43,66 +39,24 @@ func (s *s2iBuilderTemplateSearcher) Get(namespace, name string) (interface{}, e return s.informers.Devops().V1alpha1().S2iBuilderTemplates().Lister().Get(name) } -// exactly Match func (*s2iBuilderTemplateSearcher) match(match map[string]string, item *v1alpha1.S2iBuilderTemplate) bool { for k, v := range match { - switch k { - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false } } return true } -// Fuzzy searchInNamespace func (*s2iBuilderTemplateSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1.S2iBuilderTemplate) bool { for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } return true } -func (*s2iBuilderTemplateSearcher) compare(a, b *v1alpha1.S2iBuilderTemplate, orderBy string) bool { - switch orderBy { - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case v1alpha2.Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - func (s *s2iBuilderTemplateSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { builderTemplates, err := s.informers.Devops().V1alpha1().S2iBuilderTemplates().Lister().List(labels.Everything()) @@ -123,11 +77,9 @@ func (s *s2iBuilderTemplateSearcher) Search(namespace string, conditions *params } sort.Slice(result, func(i, j int) bool { if reverse { - tmp := i - i = j - j = tmp + i, j = j, i } - return s.compare(result[i], result[j], orderBy) + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) }) r := make([]interface{}, 0) diff --git a/pkg/models/resources/v1alpha2/s2ibuilder/s2ibuilder.go b/pkg/models/resources/v1alpha2/s2ibuilder/s2ibuilder.go index 11d6c1be745f31d39314791698e73839d4e9bc4f..2f99f721e1bff4aec1f5c8a5ad11460b78d1fc79 100644 --- a/pkg/models/resources/v1alpha2/s2ibuilder/s2ibuilder.go +++ b/pkg/models/resources/v1alpha2/s2ibuilder/s2ibuilder.go @@ -22,13 +22,10 @@ import ( "k8s.io/apimachinery/pkg/labels" "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" "kubesphere.io/kubesphere/pkg/client/informers/externalversions" - "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" "sort" - "strings" ) type s2iBuilderSearcher struct { @@ -43,66 +40,24 @@ func (s *s2iBuilderSearcher) Get(namespace, name string) (interface{}, error) { return s.informers.Devops().V1alpha1().S2iBuilders().Lister().S2iBuilders(namespace).Get(name) } -// exactly Match func (*s2iBuilderSearcher) match(match map[string]string, item *v1alpha1.S2iBuilder) bool { for k, v := range match { - switch k { - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false } } return true } -// Fuzzy searchInNamespace func (*s2iBuilderSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1.S2iBuilder) bool { for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } return true } -func (*s2iBuilderSearcher) compare(a, b *v1alpha1.S2iBuilder, orderBy string) bool { - switch orderBy { - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case v1alpha2.Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - func (s *s2iBuilderSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { s2iBuilders, err := s.informers.Devops().V1alpha1().S2iBuilders().Lister().S2iBuilders(namespace).List(labels.Everything()) @@ -123,11 +78,9 @@ func (s *s2iBuilderSearcher) Search(namespace string, conditions *params.Conditi } sort.Slice(result, func(i, j int) bool { if reverse { - tmp := i - i = j - j = tmp + i, j = j, i } - return s.compare(result[i], result[j], orderBy) + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) }) r := make([]interface{}, 0) diff --git a/pkg/models/resources/v1alpha2/s2irun/s2irun.go b/pkg/models/resources/v1alpha2/s2irun/s2irun.go index a20823cc0fadf15307b64d6cde7ddc698d8cb9e3..47997fa72e9e4bf3bf41184f55760b30825edece 100644 --- a/pkg/models/resources/v1alpha2/s2irun/s2irun.go +++ b/pkg/models/resources/v1alpha2/s2irun/s2irun.go @@ -22,12 +22,9 @@ import ( "k8s.io/apimachinery/pkg/labels" "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" "kubesphere.io/kubesphere/pkg/client/informers/externalversions" - "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" "sort" - "strings" ) type s2iRunSearcher struct { @@ -42,26 +39,15 @@ func (s *s2iRunSearcher) Get(namespace, name string) (interface{}, error) { return s.informers.Devops().V1alpha1().S2iRuns().Lister().S2iRuns(namespace).Get(name) } -// exactly Match func (*s2iRunSearcher) match(match map[string]string, item *v1alpha1.S2iRun) bool { for k, v := range match { switch k { - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } case v1alpha2.Status: if string(item.Status.RunState) != v { return false } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { return false } } @@ -69,43 +55,15 @@ func (*s2iRunSearcher) match(match map[string]string, item *v1alpha1.S2iRun) boo return true } -// Fuzzy searchInNamespace func (*s2iRunSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1.S2iRun) bool { for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } return true } -func (*s2iRunSearcher) compare(a, b *v1alpha1.S2iRun, orderBy string) bool { - switch orderBy { - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case v1alpha2.Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - func (s *s2iRunSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { s2iRuns, err := s.informers.Devops().V1alpha1().S2iRuns().Lister().S2iRuns(namespace).List(labels.Everything()) @@ -126,11 +84,9 @@ func (s *s2iRunSearcher) Search(namespace string, conditions *params.Conditions, } sort.Slice(result, func(i, j int) bool { if reverse { - tmp := i - i = j - j = tmp + i, j = j, i } - return s.compare(result[i], result[j], orderBy) + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) }) r := make([]interface{}, 0) diff --git a/pkg/models/resources/v1alpha2/secret/secrets.go b/pkg/models/resources/v1alpha2/secret/secrets.go index a2d57bc4f6ffac35d2c9e578032ab7af9a5b0830..f7b1155495fbffb6e8268f1301ebcd1877a688f9 100644 --- a/pkg/models/resources/v1alpha2/secret/secrets.go +++ b/pkg/models/resources/v1alpha2/secret/secrets.go @@ -19,16 +19,12 @@ package secret import ( "k8s.io/client-go/informers" - "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" ) type secretSearcher struct { @@ -43,26 +39,15 @@ func (s *secretSearcher) Get(namespace, name string) (interface{}, error) { return s.informers.Core().V1().Secrets().Lister().Secrets(namespace).Get(name) } -// exactly Match func (*secretSearcher) match(match map[string]string, item *v1.Secret) bool { for k, v := range match { switch k { - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } case "type": if string(item.Type) != v { return false } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { return false } } @@ -70,47 +55,15 @@ func (*secretSearcher) match(match map[string]string, item *v1.Secret) bool { return true } -// Fuzzy searchInNamespace func (*secretSearcher) fuzzy(fuzzy map[string]string, item *v1.Secret) bool { for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - case v1alpha2.App: - if !strings.Contains(item.Labels[v1alpha2.Chart], v) && !strings.Contains(item.Labels[v1alpha2.Release], v) { - return false - } - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } return true } -func (*secretSearcher) compare(a, b *v1.Secret, orderBy string) bool { - switch orderBy { - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case v1alpha2.Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - func (s *secretSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { secrets, err := s.informers.Core().V1().Secrets().Lister().Secrets(namespace).List(labels.Everything()) @@ -131,11 +84,9 @@ func (s *secretSearcher) Search(namespace string, conditions *params.Conditions, } sort.Slice(result, func(i, j int) bool { if reverse { - tmp := i - i = j - j = tmp + i, j = j, i } - return s.compare(result[i], result[j], orderBy) + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) }) r := make([]interface{}, 0) diff --git a/pkg/models/resources/v1alpha2/service/services.go b/pkg/models/resources/v1alpha2/service/services.go index 7e406c9d54b8668571471218c43ce08913e9d9ef..cf0f0930b219908eb3bf48bf7eb0599cd99ce0e8 100644 --- a/pkg/models/resources/v1alpha2/service/services.go +++ b/pkg/models/resources/v1alpha2/service/services.go @@ -19,16 +19,12 @@ package service import ( "k8s.io/client-go/informers" - "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" ) type serviceSearcher struct { @@ -43,70 +39,24 @@ func (s *serviceSearcher) Get(namespace, name string) (interface{}, error) { return s.informers.Core().V1().Services().Lister().Services(namespace).Get(name) } -// exactly Match func (*serviceSearcher) match(match map[string]string, item *v1.Service) bool { for k, v := range match { - switch k { - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false } } return true } -// Fuzzy searchInNamespace func (*serviceSearcher) fuzzy(fuzzy map[string]string, item *v1.Service) bool { for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - case v1alpha2.App: - if !strings.Contains(item.Labels[v1alpha2.Chart], v) && !strings.Contains(item.Labels[v1alpha2.Release], v) { - return false - } - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } return true } -func (*serviceSearcher) compare(a, b *v1.Service, orderBy string) bool { - switch orderBy { - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case v1alpha2.Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - func (s *serviceSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { services, err := s.informers.Core().V1().Services().Lister().Services(namespace).List(labels.Everything()) @@ -127,11 +77,9 @@ func (s *serviceSearcher) Search(namespace string, conditions *params.Conditions } sort.Slice(result, func(i, j int) bool { if reverse { - tmp := i - i = j - j = tmp + i, j = j, i } - return s.compare(result[i], result[j], orderBy) + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) }) r := make([]interface{}, 0) diff --git a/pkg/models/resources/v1alpha2/statefulset/statefulsets.go b/pkg/models/resources/v1alpha2/statefulset/statefulsets.go index ecb7af0069d28ecccb18cd0891d653d0bcc8c0ee..c2e8ffee247c40387b9dfd317ad9a3871e188cd4 100644 --- a/pkg/models/resources/v1alpha2/statefulset/statefulsets.go +++ b/pkg/models/resources/v1alpha2/statefulset/statefulsets.go @@ -19,16 +19,12 @@ package statefulset import ( "k8s.io/client-go/informers" - "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" ) type statefulSetSearcher struct { @@ -56,26 +52,15 @@ func statefulSetStatus(item *v1.StatefulSet) string { return v1alpha2.StatusStopped } -// Exactly Match func (*statefulSetSearcher) match(match map[string]string, item *v1.StatefulSet) bool { for k, v := range match { switch k { - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } case v1alpha2.Status: if statefulSetStatus(item) != v { return false } default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { return false } } @@ -84,47 +69,14 @@ func (*statefulSetSearcher) match(match map[string]string, item *v1.StatefulSet) } func (*statefulSetSearcher) fuzzy(fuzzy map[string]string, item *v1.StatefulSet) bool { - for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - case v1alpha2.App: - if !strings.Contains(item.Labels[v1alpha2.Chart], v) && !strings.Contains(item.Labels[v1alpha2.Release], v) { - return false - } - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } - return true } -func (*statefulSetSearcher) compare(a, b *v1.StatefulSet, orderBy string) bool { - switch orderBy { - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case v1alpha2.Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - func (s *statefulSetSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { statefulSets, err := s.informers.Apps().V1().StatefulSets().Lister().StatefulSets(namespace).List(labels.Everything()) @@ -145,11 +97,9 @@ func (s *statefulSetSearcher) Search(namespace string, conditions *params.Condit } sort.Slice(result, func(i, j int) bool { if reverse { - tmp := i - i = j - j = tmp + i, j = j, i } - return s.compare(result[i], result[j], orderBy) + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) }) r := make([]interface{}, 0) diff --git a/pkg/models/resources/v1alpha2/storageclass/storageclasses.go b/pkg/models/resources/v1alpha2/storageclass/storageclasses.go index e90c07e59af978d7b2aa97af976fa373d1293eda..a92d7606c3c9fe813559e675b8b4949ba145906c 100644 --- a/pkg/models/resources/v1alpha2/storageclass/storageclasses.go +++ b/pkg/models/resources/v1alpha2/storageclass/storageclasses.go @@ -19,16 +19,12 @@ package storageclass import ( corev1 "k8s.io/api/core/v1" + "k8s.io/api/storage/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/informers" - "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" "sort" - "strings" - - "k8s.io/api/storage/v1" - "k8s.io/apimachinery/pkg/labels" ) type storageClassesSearcher struct { @@ -43,66 +39,24 @@ func (s *storageClassesSearcher) Get(namespace, name string) (interface{}, error return s.informers.Storage().V1().StorageClasses().Lister().Get(name) } -// exactly Match func (*storageClassesSearcher) match(match map[string]string, item *v1.StorageClass) bool { for k, v := range match { - switch k { - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false } } return true } -// Fuzzy searchInNamespace func (*storageClassesSearcher) fuzzy(fuzzy map[string]string, item *v1.StorageClass) bool { for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } return true } -func (*storageClassesSearcher) compare(a, b *v1.StorageClass, orderBy string) bool { - switch orderBy { - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case v1alpha2.Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - func (s *storageClassesSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { storageClasses, err := s.informers.Storage().V1().StorageClasses().Lister().List(labels.Everything()) @@ -125,7 +79,7 @@ func (s *storageClassesSearcher) Search(namespace string, conditions *params.Con if reverse { i, j = j, i } - return s.compare(result[i], result[j], orderBy) + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) }) r := make([]interface{}, 0) diff --git a/pkg/models/resources/v1alpha2/workspace/workspaces.go b/pkg/models/resources/v1alpha2/workspace/workspaces.go index 22dda69d62249930dc84e93e615e464fb6117410..77a15af4f01bbfe5bec258b248d172081477c061 100644 --- a/pkg/models/resources/v1alpha2/workspace/workspaces.go +++ b/pkg/models/resources/v1alpha2/workspace/workspaces.go @@ -20,15 +20,11 @@ package workspace import ( tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" "kubesphere.io/kubesphere/pkg/client/informers/externalversions" - "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "k8s.io/apimachinery/pkg/labels" "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" "sort" - "strings" - - "k8s.io/apimachinery/pkg/labels" ) type workspaceSearcher struct { @@ -43,66 +39,24 @@ func (s *workspaceSearcher) Get(namespace, name string) (interface{}, error) { return s.informers.Tenant().V1alpha1().Workspaces().Lister().Get(name) } -// exactly Match func (*workspaceSearcher) match(match map[string]string, item *tenantv1alpha1.Workspace) bool { for k, v := range match { - switch k { - case v1alpha2.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case v1alpha2.Keyword: - if !strings.Contains(item.Name, v) && !v1alpha2.SearchFuzzy(item.Labels, "", v) && !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false } } return true } -// Fuzzy searchInNamespace func (*workspaceSearcher) fuzzy(fuzzy map[string]string, item *tenantv1alpha1.Workspace) bool { for k, v := range fuzzy { - switch k { - case v1alpha2.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case v1alpha2.Label: - if !v1alpha2.SearchFuzzy(item.Labels, "", v) { - return false - } - case v1alpha2.Annotation: - if !v1alpha2.SearchFuzzy(item.Annotations, "", v) { - return false - } + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { return false - default: - if !v1alpha2.SearchFuzzy(item.Labels, k, v) { - return false - } } } return true } -func (*workspaceSearcher) compare(a, b *tenantv1alpha1.Workspace, orderBy string) bool { - switch orderBy { - case v1alpha2.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case v1alpha2.Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - func (s *workspaceSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { workspaces, err := s.informers.Tenant().V1alpha1().Workspaces().Lister().List(labels.Everything()) @@ -124,11 +78,9 @@ func (s *workspaceSearcher) Search(namespace string, conditions *params.Conditio } sort.Slice(result, func(i, j int) bool { if reverse { - tmp := i - i = j - j = tmp + i, j = j, i } - return s.compare(result[i], result[j], orderBy) + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) }) r := make([]interface{}, 0) diff --git a/pkg/models/resources/v1alpha3/application/applications.go b/pkg/models/resources/v1alpha3/application/applications.go new file mode 100644 index 0000000000000000000000000000000000000000..433fdd9d5e1b1cf2b767e925a6c9f3f4cf0a2aac --- /dev/null +++ b/pkg/models/resources/v1alpha3/application/applications.go @@ -0,0 +1,78 @@ +/* + + 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 application + +import ( + appv1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1" + "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type applicationsGetter struct { + informer externalversions.SharedInformerFactory +} + +func New(sharedInformers externalversions.SharedInformerFactory) v1alpha3.Interface { + return &applicationsGetter{informer: sharedInformers} +} + +func (d *applicationsGetter) Get(namespace, name string) (runtime.Object, error) { + return d.informer.App().V1beta1().Applications().Lister().Applications(namespace).Get(name) +} + +func (d *applicationsGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { + all, err := d.informer.App().V1beta1().Applications().Lister().Applications(namespace).List(labels.Everything()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, app := range all { + result = append(result, app) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *applicationsGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftApplication, ok := left.(*appv1beta1.Application) + if !ok { + return false + } + + rightApplication, ok := right.(*appv1beta1.Application) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftApplication.ObjectMeta, rightApplication.ObjectMeta, field) +} + +func (d *applicationsGetter) filter(object runtime.Object, filter query.Filter) bool { + application, ok := object.(*appv1beta1.Application) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(application.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/application/applications_test.go b/pkg/models/resources/v1alpha3/application/applications_test.go new file mode 100644 index 0000000000000000000000000000000000000000..acb6c468ea23e4abefaaa4517972a14e7839e703 --- /dev/null +++ b/pkg/models/resources/v1alpha3/application/applications_test.go @@ -0,0 +1,109 @@ +package application + +import ( + "github.com/google/go-cmp/cmp" + appv1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1" + "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake" + "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "testing" +) + +func applicationsToRuntimeObjects(applications ...*appv1beta1.Application) []runtime.Object { + var objs []runtime.Object + for _, app := range applications { + objs = append(objs, app) + } + return objs +} + +func TestListApplications(t *testing.T) { + tests := []struct { + description string + namespace string + deployments []*appv1beta1.Application + query *query.Query + expected api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar2", + []*appv1beta1.Application{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "foo-1", + Namespace: "bar", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "foo-2", + Namespace: "bar", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "bar-2", + Namespace: "bar2", + }, + }, + }, + &query.Query{ + Pagination: &query.Pagination{ + Limit: 10, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: []query.Filter{ + { + Field: query.FieldNamespace, + Value: query.Value("bar2"), + }, + }, + }, + api.ListResult{ + Items: []interface{}{ + &appv1beta1.Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar-2", + Namespace: "bar2", + }, + }, + }, + TotalItems: 2, + }, + nil, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + objs := applicationsToRuntimeObjects(test.deployments...) + client := fake.NewSimpleClientset(objs...) + + informer := externalversions.NewSharedInformerFactory(client, 0) + + for _, deployment := range test.deployments { + informer.App().V1beta1().Applications().Informer().GetIndexer().Add(deployment) + } + + getter := New(informer) + + 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.Items, test.expected.Items); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} diff --git a/pkg/models/resources/v1alpha3/configmap/configmaps.go b/pkg/models/resources/v1alpha3/configmap/configmaps.go new file mode 100644 index 0000000000000000000000000000000000000000..9b5ee74faa7dd29caccedb9ab184eac323338df5 --- /dev/null +++ b/pkg/models/resources/v1alpha3/configmap/configmaps.go @@ -0,0 +1,78 @@ +/* + + 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 configmap + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "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 configmapsGetter struct { + informer informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &configmapsGetter{informer: sharedInformers} +} + +func (d *configmapsGetter) Get(namespace, name string) (runtime.Object, error) { + return d.informer.Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).Get(name) +} + +func (d *configmapsGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { + all, err := d.informer.Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).List(labels.Everything()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, app := range all { + result = append(result, app) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *configmapsGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftConfigMap, ok := left.(*corev1.ConfigMap) + if !ok { + return false + } + + rightConfigMap, ok := right.(*corev1.ConfigMap) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftConfigMap.ObjectMeta, rightConfigMap.ObjectMeta, field) +} + +func (d *configmapsGetter) filter(object runtime.Object, filter query.Filter) bool { + configMap, ok := object.(*corev1.ConfigMap) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(configMap.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/configmap/configmaps_test.go b/pkg/models/resources/v1alpha3/configmap/configmaps_test.go new file mode 100644 index 0000000000000000000000000000000000000000..f0c27ac6e6d777278358384fcab932105386bd6f --- /dev/null +++ b/pkg/models/resources/v1alpha3/configmap/configmaps_test.go @@ -0,0 +1,96 @@ +package configmap + +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 TestListConfigMaps(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "default", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 10, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: []query.Filter{ + { + Field: query.FieldNamespace, + Value: query.Value("default"), + }, + }, + }, + &api.ListResult{ + Items: []interface{}{foo3, foo2, foo1}, + TotalItems: len(configmaps), + }, + 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("%T differ (-got, +want): %s", test.expected, diff) + } + } +} + +var ( + foo1 = &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + Namespace: "default", + }, + } + foo2 = &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + Namespace: "default", + }, + } + foo3 = &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo3", + Namespace: "default", + }, + } + configmaps = []interface{}{foo1, foo2, foo3} +) + +func prepare() v1alpha3.Interface { + + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, configmap := range configmaps { + informer.Core().V1().ConfigMaps().Informer().GetIndexer().Add(configmap) + } + + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/deployment/deployments_test.go b/pkg/models/resources/v1alpha3/deployment/deployments_test.go index db99b3776347be78489fc36a8c1d67eacfe8a209..043c64e676b0de164329cca6feb90b45c76bb12d 100644 --- a/pkg/models/resources/v1alpha3/deployment/deployments_test.go +++ b/pkg/models/resources/v1alpha3/deployment/deployments_test.go @@ -1,149 +1,103 @@ package deployment import ( - "fmt" "github.com/google/go-cmp/cmp" - v1 "k8s.io/api/apps/v1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" + 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" - "time" ) -func newDeployments(total int, name, namespace, application string) []*v1.Deployment { - var deployments []*v1.Deployment - - for i := 0; i < total; i++ { - deploy := &v1.Deployment{ - TypeMeta: metaV1.TypeMeta{ - Kind: "Deployment", - APIVersion: "v1", - }, - ObjectMeta: metaV1.ObjectMeta{ - Name: fmt.Sprintf("%s-%d", name, i), - Namespace: namespace, - Labels: map[string]string{ - "seq": fmt.Sprintf("seq-%d", i), - }, - Annotations: map[string]string{}, - CreationTimestamp: metaV1.Time{Time: time.Now().Add(time.Duration(i*5) * time.Second)}, - }, - Status: v1.DeploymentStatus{ - ReadyReplicas: int32(i + 1), - Replicas: int32(i + 1), - AvailableReplicas: int32(i + 1), - Conditions: []v1.DeploymentCondition{ - { - Type: v1.DeploymentAvailable, - LastUpdateTime: metaV1.Time{Time: time.Now().Add(time.Duration(i*5) * time.Second)}, - }, - }, - }, - } - - deployments = append(deployments, deploy) - } - - return deployments -} - -func deploymentsToRuntimeObjects(deployments ...*v1.Deployment) []runtime.Object { - var objs []runtime.Object - for _, deploy := range deployments { - objs = append(objs, deploy) - } - - return objs -} - func TestListDeployments(t *testing.T) { tests := []struct { description string namespace string - deployments []*v1.Deployment query *query.Query - expected api.ListResult + expected *api.ListResult expectedErr error }{ { "test name filter", "bar", - []*v1.Deployment{ - { - ObjectMeta: metaV1.ObjectMeta{ - Name: "foo-1", - Namespace: "bar", - }, - }, - { - ObjectMeta: metaV1.ObjectMeta{ - Name: "foo-2", - Namespace: "bar", - }, - }, - { - ObjectMeta: metaV1.ObjectMeta{ - Name: "bar-1", - Namespace: "bar", - }, - }, - }, &query.Query{ Pagination: &query.Pagination{ Limit: 1, - Offset: 1, + Offset: 0, }, SortBy: query.FieldName, Ascending: false, Filters: []query.Filter{ { Field: query.FieldName, - Value: query.Value("foo"), + Value: query.Value("foo2"), }, }, }, - api.ListResult{ + &api.ListResult{ Items: []interface{}{ - &v1.Deployment{ - ObjectMeta: metaV1.ObjectMeta{ - Name: "foo-2", - Namespace: "bar", - }, - }, + foo2, }, - TotalItems: 2, + TotalItems: 1, }, nil, }, } + getter := prepare() + for _, test := range tests { t.Run(test.description, func(t *testing.T) { - objs := deploymentsToRuntimeObjects(test.deployments...) - client := fake.NewSimpleClientset(objs...) - - informer := informers.NewSharedInformerFactory(client, 0) - - for _, deployment := range test.deployments { - informer.Apps().V1().Deployments().Informer().GetIndexer().Add(deployment) - } - - getter := New(informer) 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.Items, test.expected.Items); diff != "" { + if diff := cmp.Diff(got, test.expected); diff != "" { t.Errorf("%T differ (-got, +want): %s", test.expected, diff) } }) } } + +var ( + foo1 = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + Namespace: "bar", + }, + } + + foo2 = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + Namespace: "bar", + }, + } + bar1 = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + Namespace: "bar", + }, + } + + deployments = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, deployment := range deployments { + informer.Apps().V1().Deployments().Informer().GetIndexer().Add(deployment) + } + + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/interface.go b/pkg/models/resources/v1alpha3/interface.go index d639411beb8b856e282e9ce569af997920573445..120fe0eb190a9790d17b1e60ef4b46583da24419 100644 --- a/pkg/models/resources/v1alpha3/interface.go +++ b/pkg/models/resources/v1alpha3/interface.go @@ -17,6 +17,7 @@ type Interface interface { List(namespace string, query *query.Query) (*api.ListResult, error) } +// CompareFunc return true is left great than right type CompareFunc func(runtime.Object, runtime.Object, query.Field) bool type FilterFunc func(runtime.Object, query.Filter) bool @@ -41,9 +42,9 @@ func DefaultList(objects []runtime.Object, query *query.Query, compareFunc Compa // sort by sortBy field sort.Slice(filtered, func(i, j int) bool { if !query.Ascending { - return !compareFunc(filtered[i], filtered[j], query.SortBy) + return compareFunc(filtered[i], filtered[j], query.SortBy) } - return compareFunc(filtered[i], filtered[j], query.SortBy) + return !compareFunc(filtered[i], filtered[j], query.SortBy) }) total := len(filtered) @@ -55,6 +56,7 @@ func DefaultList(objects []runtime.Object, query *query.Query, compareFunc Compa } } +// DefaultObjectMetaCompare return true is left great than right func DefaultObjectMetaCompare(left, right metav1.ObjectMeta, sortBy query.Field) bool { switch sortBy { // ?sortBy=name @@ -62,6 +64,10 @@ func DefaultObjectMetaCompare(left, right metav1.ObjectMeta, sortBy query.Field) return strings.Compare(left.Name, right.Name) > 0 // ?sortBy=creationTimestamp case query.FieldCreationTimeStamp: + // compare by name if creation timestamp is equal + if left.CreationTimestamp.Equal(&right.CreationTimestamp) { + return strings.Compare(left.Name, right.Name) > 0 + } return left.CreationTimestamp.After(right.CreationTimestamp.Time) default: return false diff --git a/pkg/models/resources/v1alpha3/resource/resource.go b/pkg/models/resources/v1alpha3/resource/resource.go index d846cb8e47331c3df3315e5b00e256bc318fc5df..057a1228109b42ecbec6a4d02acbcd490a0c6bcb 100644 --- a/pkg/models/resources/v1alpha3/resource/resource.go +++ b/pkg/models/resources/v1alpha3/resource/resource.go @@ -7,6 +7,7 @@ import ( "kubesphere.io/kubesphere/pkg/apiserver/query" "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/application" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/deployment" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/namespace" ) @@ -17,11 +18,12 @@ type ResourceGetter struct { getters map[schema.GroupVersionResource]v1alpha3.Interface } -func New(factory informers.InformerFactory) *ResourceGetter { +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: "", Version: "v1", Resource: "namespaces"}] = namespace.New(factory.KubernetesSharedInformerFactory()) + getters[schema.GroupVersionResource{Group: "app.k8s.io", Version: "v1beta1", Resource: "applications"}] = application.New(factory.ApplicationSharedInformerFactory()) return &ResourceGetter{ getters: getters, diff --git a/pkg/models/resources/v1alpha3/resource/resource_test.go b/pkg/models/resources/v1alpha3/resource/resource_test.go index 7526e4e0f57aeeb7ec59a1254ea52d261ae0bd51..4ea035c141200f2dbf422ac533c9e7a8e78c46e7 100644 --- a/pkg/models/resources/v1alpha3/resource/resource_test.go +++ b/pkg/models/resources/v1alpha3/resource/resource_test.go @@ -22,8 +22,8 @@ import ( "github.com/google/go-cmp/cmp" fakeapp "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake" fakeistio "istio.io/client-go/pkg/clientset/versioned/fake" - v1 "k8s.io/api/core/v1" - corev1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" fakek8s "k8s.io/client-go/kubernetes/fake" "kubesphere.io/kubesphere/pkg/api" "kubesphere.io/kubesphere/pkg/apiserver/query" @@ -34,37 +34,7 @@ import ( func TestResourceGetter(t *testing.T) { - namespaces := make([]interface{}, 0) - defaultNamespace := &v1.Namespace{ - ObjectMeta: corev1.ObjectMeta{ - Name: "default", - Labels: map[string]string{"kubesphere.io/workspace": "system-workspace"}, - }, - } - kubesphereNamespace := &v1.Namespace{ - ObjectMeta: corev1.ObjectMeta{ - Name: "kubesphere-system", - Labels: map[string]string{"kubesphere.io/workspace": "system-workspace"}, - }, - } - - namespaces = append(namespaces, defaultNamespace, kubesphereNamespace) - - ksClient := fakeks.NewSimpleClientset() - k8sClient := fakek8s.NewSimpleClientset(defaultNamespace, kubesphereNamespace) - istioClient := fakeistio.NewSimpleClientset() - appClient := fakeapp.NewSimpleClientset() - fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, istioClient, appClient) - - k8sInformerFactory := fakeInformerFactory.KubernetesSharedInformerFactory() - for _, namespace := range namespaces { - err := k8sInformerFactory.Core().V1().Namespaces().Informer().GetIndexer().Add(namespace) - if err != nil { - t.Fatal(err) - } - } - - resource := New(fakeInformerFactory) + resource := prepare() tests := []struct { Name string @@ -89,8 +59,8 @@ func TestResourceGetter(t *testing.T) { }, ExpectError: nil, ExpectResponse: &api.ListResult{ - Items: namespaces, - TotalItems: 2, + Items: []interface{}{foo2, foo1, bar1}, + TotalItems: 3, }, }, } @@ -99,7 +69,6 @@ func TestResourceGetter(t *testing.T) { result, err := resource.List(test.Resource, test.Namespace, test.Query) - t.Logf("%+v", result) if err != test.ExpectError { t.Errorf("expected error: %s, got: %s", test.ExpectError, err) } @@ -107,5 +76,44 @@ func TestResourceGetter(t *testing.T) { t.Errorf(diff) } } +} + +var ( + foo1 = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + Namespace: "bar", + }, + } + + foo2 = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + Namespace: "bar", + }, + } + bar1 = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + Namespace: "bar", + }, + } + + namespaces = []interface{}{foo1, foo2, bar1} +) + +func prepare() *ResourceGetter { + + ksClient := fakeks.NewSimpleClientset() + k8sClient := fakek8s.NewSimpleClientset() + istioClient := fakeistio.NewSimpleClientset() + appClient := fakeapp.NewSimpleClientset() + fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, istioClient, appClient) + + for _, namespace := range namespaces { + fakeInformerFactory.KubernetesSharedInformerFactory().Core().V1(). + Namespaces().Informer().GetIndexer().Add(namespace) + } + return NewResourceGetter(fakeInformerFactory) } diff --git a/pkg/models/tenant/tenant.go b/pkg/models/tenant/tenant.go index 038967d843e46042da67da1b443097282d02d1e5..5f94d3f08a323f687a048fc13ade1610623a0f74 100644 --- a/pkg/models/tenant/tenant.go +++ b/pkg/models/tenant/tenant.go @@ -21,63 +21,91 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apiserver/pkg/authentication/user" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/api" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizerfactory" "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models/iam/am" "kubesphere.io/kubesphere/pkg/simple/client/k8s" ) type Interface interface { - ListWorkspaces(username string) (*api.ListResult, error) - ListNamespaces(username, workspace string) (*api.ListResult, error) + ListWorkspaces(user user.Info) (*api.ListResult, error) + ListNamespaces(user user.Info, workspace string) (*api.ListResult, error) } type tenantOperator struct { - informers informers.InformerFactory - am am.AccessManagementInterface + informers informers.InformerFactory + am am.AccessManagementInterface + authorizer authorizer.Authorizer } func New(k8sClient k8s.Client, informers informers.InformerFactory) Interface { + amOperator := am.NewAMOperator(k8sClient.KubeSphere(), informers.KubeSphereSharedInformerFactory()) + opaAuthorizer := authorizerfactory.NewOPAAuthorizer(amOperator) return &tenantOperator{ - informers: informers, - am: am.NewAMOperator(k8sClient.KubeSphere(), informers.KubeSphereSharedInformerFactory()), + informers: informers, + am: amOperator, + authorizer: opaAuthorizer, } } -func (t *tenantOperator) ListWorkspaces(username string) (*api.ListResult, error) { +func (t *tenantOperator) ListWorkspaces(user user.Info) (*api.ListResult, error) { + + workspaces := make([]*tenantv1alpha1.Workspace, 0) + + listWS := authorizer.AttributesRecord{ + User: user, + Verb: "list", + APIGroup: "tenant.kubesphere.io", + APIVersion: "v1alpha2", + Resource: "workspaces", + } + + decision, _, err := t.authorizer.Authorize(listWS) - workspaceRoles, err := t.am.ListRolesOfUser(iamv1alpha2.WorkspaceScope, username) if err != nil { klog.Error(err) return nil, err } - workspaces := make([]*tenantv1alpha1.Workspace, 0) - - for _, role := range workspaceRoles { - - // all workspaces are allowed - if role.Target.Name == iamv1alpha2.TargetAll { - workspaces, err = t.informers.KubeSphereSharedInformerFactory(). - Tenant().V1alpha1().Workspaces().Lister().List(labels.Everything()) - break - } - workspace, err := t.informers.KubeSphereSharedInformerFactory(). - Tenant().V1alpha1().Workspaces().Lister().Get(role.Target.Name) + if decision == authorizer.DecisionAllow { + workspaces, err = t.informers.KubeSphereSharedInformerFactory(). + Tenant().V1alpha1().Workspaces().Lister().List(labels.Everything()) - if errors.IsNotFound(err) { - klog.Warningf("workspace role: %s found but workspace not exist", role.Target) - continue + if err != nil { + klog.Error(err) + return nil, err } + } else { + workspaceRoles, err := t.am.ListRolesOfUser(iamv1alpha2.WorkspaceScope, user.GetName()) if err != nil { klog.Error(err) return nil, err } - if !containsWorkspace(workspaces, workspace) { - workspaces = append(workspaces, workspace) + + for _, role := range workspaceRoles { + + workspace, err := t.informers.KubeSphereSharedInformerFactory(). + Tenant().V1alpha1().Workspaces().Lister().Get(role.Target.Name) + + if errors.IsNotFound(err) { + klog.Warningf("workspace role: %s found but workspace not exist", role.Target) + continue + } + + if err != nil { + klog.Error(err) + return nil, err + } + + if !containsWorkspace(workspaces, workspace) { + workspaces = append(workspaces, workspace) + } } } @@ -87,39 +115,59 @@ func (t *tenantOperator) ListWorkspaces(username string) (*api.ListResult, error }, nil } -func (t *tenantOperator) ListNamespaces(username, workspace string) (*api.ListResult, error) { +func (t *tenantOperator) ListNamespaces(user user.Info, workspace string) (*api.ListResult, error) { + namespaces := make([]*corev1.Namespace, 0) + + listNSInWS := authorizer.AttributesRecord{ + User: user, + Verb: "list", + APIGroup: "", + APIVersion: "v1", + Workspace: workspace, + Resource: "namespaces", + } - namespaceRoles, err := t.am.ListRolesOfUser(iamv1alpha2.NamespaceScope, username) + decision, _, err := t.authorizer.Authorize(listNSInWS) if err != nil { klog.Error(err) return nil, err } - namespaces := make([]*corev1.Namespace, 0) - - for _, role := range namespaceRoles { + if decision == authorizer.DecisionAllow { + namespaces, err = t.informers.KubernetesSharedInformerFactory(). + Core().V1().Namespaces().Lister().List(labels.Everything()) - // all workspaces are allowed - if role.Target.Name == iamv1alpha2.TargetAll { - namespaces, err = t.informers.KubernetesSharedInformerFactory(). - Core().V1().Namespaces().Lister().List(labels.Everything()) - break + if err != nil { + klog.Error(err) + return nil, err } + } else { + namespaceRoles, err := t.am.ListRolesOfUser(iamv1alpha2.NamespaceScope, workspace) - namespace, err := t.informers.KubernetesSharedInformerFactory(). - Core().V1().Namespaces().Lister().Get(role.Target.Name) - - if errors.IsNotFound(err) { - klog.Warningf("workspace role: %s found but workspace not exist", role.Target) - continue - } if err != nil { klog.Error(err) return nil, err } - if !containsNamespace(namespaces, namespace) { - namespaces = append(namespaces, namespace) + + for _, role := range namespaceRoles { + + namespace, err := t.informers.KubernetesSharedInformerFactory(). + Core().V1().Namespaces().Lister().Get(role.Target.Name) + + if errors.IsNotFound(err) { + klog.Warningf("workspace role: %s found but workspace not exist", role.Target) + continue + } + + if err != nil { + klog.Error(err) + return nil, err + } + + if !containsNamespace(namespaces, namespace) { + namespaces = append(namespaces, namespace) + } } }