diff --git a/pkg/apiserver/tenant/tenant.go b/pkg/apiserver/tenant/tenant.go index a3144f921..08e12de94 100644 --- a/pkg/apiserver/tenant/tenant.go +++ b/pkg/apiserver/tenant/tenant.go @@ -90,7 +90,12 @@ func DescribeWorkspace(req *restful.Request, resp *restful.Response) { result, err := tenant.DescribeWorkspace(username, workspaceName) if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + glog.Errorf("describe workspace failed: %+v", err) + if k8serr.IsNotFound(err) { + resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + } else { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + } return } diff --git a/pkg/controller/namespace/namespace_controller.go b/pkg/controller/namespace/namespace_controller.go index 59cd09878..af2d560f3 100644 --- a/pkg/controller/namespace/namespace_controller.go +++ b/pkg/controller/namespace/namespace_controller.go @@ -189,10 +189,13 @@ func (r *ReconcileNamespace) checkAndCreateRoles(namespace *corev1.Namespace) er log.Info("Creating default role", "namespace", namespace.Name, "role", role.Name) err = r.Create(context.TODO(), role) if err != nil { + log.Info("Creating default role failed", "namespace", namespace.Name, "role", role.Name) return err } + } else { + log.Info("Get default role failed", "namespace", namespace.Name, "role", role.Name) + return err } - return err } } return nil diff --git a/pkg/models/iam/im.go b/pkg/models/iam/im.go index 2dc2f2aab..250a048d4 100644 --- a/pkg/models/iam/im.go +++ b/pkg/models/iam/im.go @@ -352,11 +352,11 @@ func ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limi } switch orderBy { case "username": - fallthrough - case "createTime": - return users[i].CreateTime.Before(users[j].CreateTime) - default: return strings.Compare(users[i].Username, users[j].Username) <= 0 + case "createTime": + fallthrough + default: + return users[i].CreateTime.Before(users[j].CreateTime) } }) diff --git a/pkg/models/metrics/metrics.go b/pkg/models/metrics/metrics.go index 82c4b5ed1..ae7bd3e63 100644 --- a/pkg/models/metrics/metrics.go +++ b/pkg/models/metrics/metrics.go @@ -21,6 +21,7 @@ package metrics import ( "github.com/golang/glog" "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/simple/client/kubesphere" "net/url" "regexp" "runtime/debug" @@ -1009,11 +1010,11 @@ func GetAllWorkspacesStatistics() *FormatedLevelMetric { }() go func() { - actNums, errAct := workspaces.GetAllAccountNums() + result, errAct := kubesphere.Client().ListUsers() if errAct != nil { glog.Errorln(errAct.Error()) } - accountResultItem = getSpecificMetricItem(timestamp, MetricNameWorkspaceAllAccountCount, WorkspaceResourceKindAccount, actNums, errAct) + accountResultItem = getSpecificMetricItem(timestamp, MetricNameWorkspaceAllAccountCount, WorkspaceResourceKindAccount, result.TotalCount, errAct) wg.Done() }() diff --git a/pkg/models/resources/cronjobs.go b/pkg/models/resources/cronjobs.go index df75876b5..16971cc04 100644 --- a/pkg/models/resources/cronjobs.go +++ b/pkg/models/resources/cronjobs.go @@ -39,9 +39,9 @@ func (*cronJobSearcher) get(namespace, name string) (interface{}, error) { func cronJobStatus(item *v1beta1.CronJob) string { if item.Spec.Suspend != nil && *item.Spec.Suspend { - return paused + return StatusPaused } - return running + return StatusRunning } // Exactly Match @@ -53,7 +53,7 @@ func (*cronJobSearcher) match(match map[string]string, item *v1beta1.CronJob) bo if !sliceutil.HasString(names, item.Name) { return false } - case status: + case Status: if cronJobStatus(item) != v { return false } diff --git a/pkg/models/resources/daemonsets.go b/pkg/models/resources/daemonsets.go index 782bb7500..cb2bac63a 100644 --- a/pkg/models/resources/daemonsets.go +++ b/pkg/models/resources/daemonsets.go @@ -38,11 +38,11 @@ func (*daemonSetSearcher) get(namespace, name string) (interface{}, error) { func daemonSetStatus(item *v1.DaemonSet) string { if item.Status.NumberAvailable == 0 { - return stopped + return StatusStopped } else if item.Status.DesiredNumberScheduled == item.Status.NumberAvailable { - return running + return StatusRunning } else { - return updating + return StatusUpdating } } @@ -50,7 +50,7 @@ func daemonSetStatus(item *v1.DaemonSet) string { func (*daemonSetSearcher) match(match map[string]string, item *v1.DaemonSet) bool { for k, v := range match { switch k { - case status: + case Status: if daemonSetStatus(item) != v { return false } diff --git a/pkg/models/resources/deployments.go b/pkg/models/resources/deployments.go index 3492fb8f8..5657748d7 100644 --- a/pkg/models/resources/deployments.go +++ b/pkg/models/resources/deployments.go @@ -40,21 +40,21 @@ func (*deploymentSearcher) get(namespace, name string) (interface{}, error) { func deploymentStatus(item *v1.Deployment) string { if item.Spec.Replicas != nil { if item.Status.ReadyReplicas == 0 && *item.Spec.Replicas == 0 { - return stopped + return StatusStopped } else if item.Status.ReadyReplicas == *item.Spec.Replicas { - return running + return StatusRunning } else { - return updating + return StatusUpdating } } - return stopped + return StatusStopped } // Exactly Match func (*deploymentSearcher) match(match map[string]string, item *v1.Deployment) bool { for k, v := range match { switch k { - case status: + case Status: if deploymentStatus(item) != v { return false } diff --git a/pkg/models/resources/extraannotations.go b/pkg/models/resources/extraannotations.go new file mode 100644 index 000000000..ee75a43f2 --- /dev/null +++ b/pkg/models/resources/extraannotations.go @@ -0,0 +1,74 @@ +/* + + 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 resources + +import ( + "github.com/golang/glog" + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/informers" +) + +type extraAnnotationInjector struct { +} + +func (i extraAnnotationInjector) addExtraAnnotations(item interface{}) interface{} { + + switch item.(type) { + case *v1.PersistentVolumeClaim: + return i.injectPersistentVolumeClaim(item.(*v1.PersistentVolumeClaim)) + } + + return item +} + +func (i extraAnnotationInjector) injectPersistentVolumeClaim(item *v1.PersistentVolumeClaim) *v1.PersistentVolumeClaim { + podLister := informers.SharedInformerFactory().Core().V1().Pods().Lister() + pods, err := podLister.Pods(item.Namespace).List(labels.Everything()) + if err != nil { + glog.Errorf("inject annotation failed %+v", err) + return item + } + + item = item.DeepCopy() + + if item.Annotations == nil { + item.Annotations = make(map[string]string, 0) + } + + if isPvcInUse(pods, item.Name) { + item.Annotations["kubesphere.io/in-use"] = "true" + } else { + item.Annotations["kubesphere.io/in-use"] = "false" + } + + return item +} + +func isPvcInUse(pods []*v1.Pod, pvcName string) bool { + for _, pod := range pods { + volumes := pod.Spec.Volumes + for _, volume := range volumes { + pvc := volume.PersistentVolumeClaim + if pvc != nil && pvc.ClaimName == pvcName { + return true + } + } + } + return false +} diff --git a/pkg/models/resources/jobs.go b/pkg/models/resources/jobs.go index d4578fe6e..506e4fa10 100644 --- a/pkg/models/resources/jobs.go +++ b/pkg/models/resources/jobs.go @@ -28,7 +28,6 @@ import ( "time" batchv1 "k8s.io/api/batch/v1" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" ) @@ -40,15 +39,13 @@ func (*jobSearcher) get(namespace, name string) (interface{}, error) { } func jobStatus(item *batchv1.Job) string { - status := "" - - for _, condition := range item.Status.Conditions { - if condition.Type == batchv1.JobFailed && condition.Status == corev1.ConditionTrue { - status = failed - } - if condition.Type == batchv1.JobComplete && condition.Status == corev1.ConditionTrue { - status = complete - } + status := StatusFailed + if item.Status.Active > 0 { + status = StatusRunning + } else if item.Status.Failed > 0 { + status = StatusFailed + } else if item.Status.Succeeded > 0 { + status = StatusComplete } return status } @@ -57,7 +54,7 @@ func jobStatus(item *batchv1.Job) string { func (*jobSearcher) match(match map[string]string, item *batchv1.Job) bool { for k, v := range match { switch k { - case status: + case Status: if jobStatus(item) != v { return false } diff --git a/pkg/models/resources/persistentvolumeclaims.go b/pkg/models/resources/persistentvolumeclaims.go index 307dfc7bc..c65ad314e 100644 --- a/pkg/models/resources/persistentvolumeclaims.go +++ b/pkg/models/resources/persistentvolumeclaims.go @@ -36,6 +36,18 @@ func (*persistentVolumeClaimSearcher) get(namespace, name string) (interface{}, return informers.SharedInformerFactory().Core().V1().PersistentVolumeClaims().Lister().PersistentVolumeClaims(namespace).Get(name) } +func pvcStatus(item *v1.PersistentVolumeClaim) string { + status := StatusPending + if item.Status.Phase == v1.ClaimPending { + status = StatusPending + } else if item.Status.Phase == v1.ClaimBound { + status = StatusBound + } else if item.Status.Phase == v1.ClaimLost { + status = StatusLost + } + return status +} + // exactly Match func (*persistentVolumeClaimSearcher) match(match map[string]string, item *v1.PersistentVolumeClaim) bool { for k, v := range match { @@ -45,6 +57,11 @@ func (*persistentVolumeClaimSearcher) match(match map[string]string, item *v1.Pe if !sliceutil.HasString(names, item.Name) { return false } + case Status: + statuses := strings.Split(v, "|") + if !sliceutil.HasString(statuses, pvcStatus(item)) { + return false + } case Keyword: if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { return false diff --git a/pkg/models/resources/resources.go b/pkg/models/resources/resources.go index 528462f16..c78e50fa1 100644 --- a/pkg/models/resources/resources.go +++ b/pkg/models/resources/resources.go @@ -51,6 +51,7 @@ func init() { } var ( + injector = extraAnnotationInjector{} resources = make(map[string]resourceSearchInterface) clusterResources = []string{Nodes, Workspaces, Namespaces, ClusterRoles, StorageClasses, S2iBuilderTemplates} ) @@ -67,17 +68,20 @@ const ( release = "release" annotation = "annotation" Keyword = "keyword" - status = "status" + Status = "status" includeCronJob = "includeCronJob" cronJobKind = "CronJob" s2iRunKind = "S2iRun" includeS2iRun = "includeS2iRun" - running = "running" - paused = "paused" - updating = "updating" - stopped = "stopped" - failed = "failed" - complete = "complete" + StatusRunning = "running" + StatusPaused = "paused" + StatusPending = "pending" + StatusUpdating = "updating" + StatusStopped = "stopped" + StatusFailed = "failed" + StatusBound = "bound" + StatusLost = "lost" + StatusComplete = "complete" app = "app" Deployments = "deployments" DaemonSets = "daemonsets" @@ -142,9 +146,9 @@ func ListResources(namespace, resource string, conditions *params.Conditions, or return nil, err } - for i, d := range result { + for i, item := range result { if i >= offset && (limit == -1 || len(items) < limit) { - items = append(items, d) + items = append(items, injector.addExtraAnnotations(item)) } } diff --git a/pkg/models/resources/s2irun.go b/pkg/models/resources/s2irun.go index 7c8b52e39..eb7245cd9 100644 --- a/pkg/models/resources/s2irun.go +++ b/pkg/models/resources/s2irun.go @@ -48,7 +48,7 @@ func (*s2iRunSearcher) match(match map[string]string, item *v1alpha1.S2iRun) boo if !sliceutil.HasString(names, item.Name) { return false } - case status: + case Status: if string(item.Status.RunState) != v { return false } diff --git a/pkg/models/resources/statefulsets.go b/pkg/models/resources/statefulsets.go index 09a244b85..69ff8c33f 100644 --- a/pkg/models/resources/statefulsets.go +++ b/pkg/models/resources/statefulsets.go @@ -39,14 +39,14 @@ func (*statefulSetSearcher) get(namespace, name string) (interface{}, error) { func statefulSetStatus(item *v1.StatefulSet) string { if item.Spec.Replicas != nil { if item.Status.ReadyReplicas == 0 && *item.Spec.Replicas == 0 { - return stopped + return StatusStopped } else if item.Status.ReadyReplicas == *item.Spec.Replicas { - return running + return StatusRunning } else { - return updating + return StatusUpdating } } - return stopped + return StatusStopped } // Exactly Match @@ -62,7 +62,7 @@ func (*statefulSetSearcher) match(match map[string]string, item *v1.StatefulSet) if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { return false } - case status: + case Status: if statefulSetStatus(item) != v { return false } diff --git a/pkg/models/status/status.go b/pkg/models/status/status.go index c726a408c..9e90b68de 100644 --- a/pkg/models/status/status.go +++ b/pkg/models/status/status.go @@ -18,8 +18,10 @@ package status import ( + "github.com/golang/glog" "kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/params" + "strings" "kubesphere.io/kubesphere/pkg/models/resources" ) @@ -34,15 +36,22 @@ func GetNamespacesResourceStatus(namespace string) (*workLoadStatus, error) { res := workLoadStatus{Count: make(map[string]int), Namespace: namespace, Items: make(map[string]interface{})} var notReadyList *models.PageableResponse var err error - for _, resource := range []string{resources.Deployments, resources.StatefulSets, resources.DaemonSets, resources.PersistentVolumeClaims} { - notReadyStatus := "updating" - if resource == resources.PersistentVolumeClaims { - notReadyStatus = "pending" + for _, resource := range []string{resources.Deployments, resources.StatefulSets, resources.DaemonSets, resources.PersistentVolumeClaims, resources.Jobs} { + var notReadyStatus string + + switch resource { + case resources.PersistentVolumeClaims: + notReadyStatus = strings.Join([]string{resources.StatusPending, resources.StatusLost}, "|") + case resources.Jobs: + notReadyStatus = resources.StatusFailed + default: + notReadyStatus = resources.StatusUpdating } - notReadyList, err = resources.ListResources(namespace, resource, ¶ms.Conditions{Match: map[string]string{"status": notReadyStatus}}, "", false, -1, 0) + notReadyList, err = resources.ListResources(namespace, resource, ¶ms.Conditions{Match: map[string]string{resources.Status: notReadyStatus}}, "", false, -1, 0) if err != nil { + glog.Errorf("list resources failed: %+v", err) return nil, err } diff --git a/pkg/models/workspaces/workspaces.go b/pkg/models/workspaces/workspaces.go index 18716e9de..58795e2b6 100644 --- a/pkg/models/workspaces/workspaces.go +++ b/pkg/models/workspaces/workspaces.go @@ -249,11 +249,3 @@ func GetAllDevOpsProjectsNums() (int, error) { } return len(devOpsProjects), nil } - -func GetAllAccountNums() (int, error) { - users, err := iam.ListUsers(¶ms.Conditions{}, "", false, 1, 0) - if err != nil { - return 0, err - } - return users.TotalCount, nil -} diff --git a/pkg/simple/client/kubesphere/kubesphereclient.go b/pkg/simple/client/kubesphere/kubesphereclient.go index d287bf096..f7040e2da 100644 --- a/pkg/simple/client/kubesphere/kubesphereclient.go +++ b/pkg/simple/client/kubesphere/kubesphereclient.go @@ -42,6 +42,7 @@ type Interface interface { UpdateGroup(group *models.Group) (*models.Group, error) DescribeGroup(name string) (*models.Group, error) DeleteGroup(name string) error + ListUsers() (*models.PageableResponse, error) } type client struct {