Initial commit

This commit is contained in:
jeff
2019-03-07 17:08:54 +08:00
commit 47bf8820f4
2817 changed files with 960937 additions and 0 deletions

30
pkg/apis/apis.go Normal file
View File

@@ -0,0 +1,30 @@
/*
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 apis contains KubeSphere API groups.
package apis
import (
"k8s.io/apimachinery/pkg/runtime"
)
// AddToSchemes may be used to add all resources defined in the project to a Scheme
var AddToSchemes runtime.SchemeBuilder
// AddToScheme adds all Resources to the Scheme
func AddToScheme(s *runtime.Scheme) error {
return AddToSchemes.AddToScheme(s)
}

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

@@ -0,0 +1,18 @@
/*
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 metrics

View File

@@ -0,0 +1,33 @@
/*
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 install
import (
"github.com/emicklei/go-restful"
urlruntime "k8s.io/apimachinery/pkg/util/runtime"
metrcisv1alpha2 "kubesphere.io/kubesphere/pkg/apis/metrics/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
)
func init() {
Install(runtime.Container)
}
func Install(container *restful.Container) {
urlruntime.Must(metrcisv1alpha2.AddToContainer(container))
}

View File

@@ -0,0 +1,45 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha2
import (
"github.com/emicklei/go-restful"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/apiserver/metrics"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
)
const GroupName = "metrics.kubesphere.io"
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
var (
WebServiceBuilder = runtime.NewContainerBuilder(addWebService)
AddToContainer = WebServiceBuilder.AddToContainer
)
func addWebService(c *restful.Container) error {
webservice := runtime.NewWebService(GroupVersion)
webservice.Route(webservice.GET("/storageclasses/{storageclass}").To(metrics.GetScMetrics))
webservice.Route(webservice.GET("/metrics/storageclass").To(metrics.GetScMetricsList))
c.Add(webservice)
return nil
}

View File

@@ -0,0 +1,18 @@
/*
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 operations

View File

@@ -0,0 +1,33 @@
/*
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 install
import (
"github.com/emicklei/go-restful"
urlruntime "k8s.io/apimachinery/pkg/util/runtime"
operationsv1alpha2 "kubesphere.io/kubesphere/pkg/apis/operations/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
)
func init() {
Install(runtime.Container)
}
func Install(container *restful.Container) {
urlruntime.Must(operationsv1alpha2.AddToContainer(container))
}

View File

@@ -0,0 +1,58 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha2
import (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/apiserver/operations"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
)
const GroupName = "operations.kubesphere.io"
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
var (
WebServiceBuilder = runtime.NewContainerBuilder(addWebService)
AddToContainer = WebServiceBuilder.AddToContainer
)
func addWebService(c *restful.Container) error {
webservice := runtime.NewWebService(GroupVersion)
webservice.Route(webservice.POST("/nodes/{node}/drainage").To(operations.DrainNode))
webservice.Route(webservice.POST("/namespaces/{namespace}/jobs/{job}").
To(operations.RerunJob).
Metadata(restfulspec.KeyOpenAPITags, []string{"jobs"}).
Doc("Handle job operation").
Param(webservice.PathParameter("job", "job name").
DataType("string")).
Param(webservice.PathParameter("namespace", "job's namespace").
DataType("string")).
Param(webservice.QueryParameter("a", "action").
DataType("string")).
Writes(""))
c.Add(webservice)
return nil
}

View File

@@ -0,0 +1,18 @@
/*
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 metrics

View File

@@ -0,0 +1,33 @@
/*
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 install
import (
"github.com/emicklei/go-restful"
urlruntime "k8s.io/apimachinery/pkg/util/runtime"
resourcev1alpha2 "kubesphere.io/kubesphere/pkg/apis/resources/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
)
func init() {
Install(runtime.Container)
}
func Install(c *restful.Container) {
urlruntime.Must(resourcev1alpha2.AddToContainer(c))
}

View File

@@ -0,0 +1,57 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha2
import (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/apiserver/resources"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
)
const GroupName = "resources.kubesphere.io"
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
var (
WebServiceBuilder = runtime.NewContainerBuilder(addWebService)
AddToContainer = WebServiceBuilder.AddToContainer
)
func addWebService(c *restful.Container) error {
webservice := runtime.NewWebService(GroupVersion)
webservice.Route(webservice.GET("/namespaces/{namespace}/{resources}").To(resources.NamespaceResourceHandler))
webservice.Route(webservice.GET("/{resources}").To(resources.ClusterResourceHandler))
webservice.Route(webservice.GET("/storageclasses/{storageclass}/persistentvolumeclaims").To(resources.GetPvcListBySc))
webservice.Route(webservice.GET("/namespaces/{namespace}/persistentvolumeclaims/{pvc}/pods").To(resources.GetPodListByPvc))
tags := []string{"users"}
webservice.Route(webservice.GET("/users/{username}/kubectl").Doc("get user's kubectl pod").Param(webservice.PathParameter("username",
"username").DataType("string")).Metadata(restfulspec.KeyOpenAPITags, tags).To(resources.GetKubectl))
webservice.Route(webservice.GET("/users/{username}/kubeconfig").Doc("get users' kubeconfig").Param(webservice.PathParameter("username",
"username").DataType("string")).Metadata(restfulspec.KeyOpenAPITags, tags).To(resources.GetKubeconfig))
c.Add(webservice)
return nil
}

View File

@@ -0,0 +1,66 @@
/*
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 components
import (
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/components"
)
func V1Alpha2(ws *restful.WebService) {
ws.Route(ws.GET("/components").To(getComponents))
ws.Route(ws.GET("/components/{component}").To(getComponentStatus))
ws.Route(ws.GET("/health").To(getSystemHealthStatus))
}
func getSystemHealthStatus(request *restful.Request, response *restful.Response) {
result, err := components.GetAllComponentsStatus()
if errors.HandlerError(err, response) {
return
}
response.WriteAsJson(result)
}
// get a specific component status
func getComponentStatus(request *restful.Request, response *restful.Response) {
component := request.PathParameter("component")
result, err := components.GetComponentStatus(component)
if errors.HandlerError(err, response) {
return
}
response.WriteAsJson(result)
}
// get all componentsHandler
func getComponents(request *restful.Request, response *restful.Response) {
result, err := components.GetAllComponentsStatus()
if errors.HandlerError(err, response) {
return
}
response.WriteAsJson(result)
}

52
pkg/apiserver/hpa/hpa.go Normal file
View File

@@ -0,0 +1,52 @@
/*
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 hpa
import (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"k8s.io/api/autoscaling/v1"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/hpa"
)
func V1Alpha2(ws *restful.WebService) {
ws.Route(ws.GET("/namespaces/{namespace}/horizontalpodautoscalers/{horizontalpodautoscaler}").
To(getHpa).
Metadata(restfulspec.KeyOpenAPITags, []string{"hpa"}).
Doc("get horizontalpodautoscalers").
Param(ws.PathParameter("namespace", "horizontalpodautoscalers's namespace").
DataType("string")).
Param(ws.PathParameter("horizontalpodautoscaler", "horizontalpodautoscaler's name")).
Writes(v1.HorizontalPodAutoscaler{}))
}
func getHpa(req *restful.Request, resp *restful.Response) {
name := req.PathParameter("horizontalpodautoscaler")
namespace := req.PathParameter("namespace")
result, err := hpa.GetHPA(namespace, name)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(result)
}

View File

@@ -0,0 +1,76 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package metrics
import (
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/storage"
)
type scMetricsItem struct {
Name string `json:"name"`
Metrics *storage.ScMetrics `json:"metrics"`
}
// Get StorageClass item
// Extended API URL: "GET /api/v1alpha1/storage/storageclasses/{storageclass}/metrics"
func GetScMetrics(request *restful.Request, response *restful.Response) {
scName := request.PathParameter("storageclass")
metrics, err := storage.GetScMetrics(scName)
if errors.HandlerError(err, response) {
return
}
result := scMetricsItem{
Name: scName, Metrics: metrics,
}
response.WriteAsJson(result)
}
// Get StorageClass item list
// Extended API URL: "GET /api/v1alpha1/storage/storageclasses/metrics"
func GetScMetricsList(request *restful.Request, response *restful.Response) {
scList, err := storage.GetScList()
if errors.HandlerError(err, response) {
return
}
// Set return value
items := make([]scMetricsItem, 0)
for _, v := range scList {
metrics, err := storage.GetScMetrics(v.GetName())
if errors.HandlerError(err, response) {
return
}
item := scMetricsItem{
Name: v.GetName(), Metrics: metrics,
}
items = append(items, item)
}
response.WriteAsJson(items)
}

View File

@@ -0,0 +1,51 @@
/*
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 operations
import (
"net/http"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/workloads"
"github.com/emicklei/go-restful"
"fmt"
)
func RerunJob(req *restful.Request, resp *restful.Response) {
var err error
job := req.PathParameter("job")
namespace := req.PathParameter("namespace")
action := req.QueryParameter("a")
switch action {
case "rerun":
err = workloads.JobReRun(namespace, job)
default:
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, fmt.Sprintf("invalid operation %s", action)))
return
}
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(errors.None)
}

View File

@@ -0,0 +1,39 @@
/*
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 operations
import (
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/nodes"
)
func DrainNode(request *restful.Request, response *restful.Response) {
nodeName := request.PathParameter("node")
err := nodes.DrainNode(nodeName)
if errors.HandlerError(err, response) {
return
}
response.WriteAsJson(errors.None)
}

View File

@@ -0,0 +1,69 @@
/*
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 quotas
import (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/quotas"
)
func V1Alpha2(ws *restful.WebService) {
tags := []string{"quotas"}
ws.Route(ws.GET("/quotas").
To(getClusterQuotas).
Doc("get whole cluster's resource usage").
Writes(quotas.ResourceQuota{}).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}/quotas").
Doc("get specified namespace's resource quota and usage").
Param(ws.PathParameter("namespace", "namespace's name").
DataType("string")).
Writes(quotas.ResourceQuota{}).
Metadata(restfulspec.KeyOpenAPITags, tags).
To(getNamespaceQuotas))
}
func getNamespaceQuotas(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
quota, err := quotas.GetNamespaceQuotas(namespace)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(quota)
}
func getClusterQuotas(req *restful.Request, resp *restful.Response) {
quota, err := quotas.GetClusterQuotas()
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(quota)
}

View File

@@ -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 registries
import (
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/registries"
)
func V1Alpha2(ws *restful.WebService) {
ws.Route(ws.POST("registries/verify").To(registryVerify))
}
func registryVerify(request *restful.Request, response *restful.Response) {
authInfo := registries.AuthInfo{}
err := request.ReadEntity(&authInfo)
if errors.HandlerError(err, response) {
return
}
err = registries.RegistryVerify(authInfo)
if errors.HandlerError(err, response) {
return
}
response.WriteAsJson(errors.None)
}
//func (c *registriesHandler) handlerImageSearch(request *restful.Request, response *restful.Response) {
//
// registry := request.PathParameter("name")
// searchWord := request.PathParameter("searchWord")
// namespace := request.PathParameter("namespace")
//
// res := c.registries.ImageSearch(namespace, registry, searchWord)
//
// response.WriteAsJson(res)
//
//}
//
//func (c *registriesHandler) handlerGetImageTags(request *restful.Request, response *restful.Response) {
//
// registry := request.PathParameter("name")
// image := request.QueryParameter("image")
// namespace := request.PathParameter("namespace")
//
// res := c.registries.GetImageTags(namespace, registry, image)
//
// response.WriteAsJson(res)
//}

View File

@@ -0,0 +1,42 @@
/*
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/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/resources"
"kubesphere.io/kubesphere/pkg/params"
)
func ClusterResourceHandler(req *restful.Request, resp *restful.Response) {
resourceName := req.PathParameter("resources")
conditions := req.QueryParameter(params.Conditions)
orderBy := req.QueryParameter(params.OrderBy)
limit, offset := params.ParsePaging(req.QueryParameter(params.Paging))
reverse := params.ParseReserve(req.QueryParameter(params.Reserve))
result, err := resources.ListClusterResource(resourceName, conditions, orderBy, reverse, limit, offset)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(result)
}

View File

@@ -0,0 +1,43 @@
/*
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/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/resources"
"kubesphere.io/kubesphere/pkg/params"
)
func NamespaceResourceHandler(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
resourceName := req.PathParameter("resources")
conditions := req.QueryParameter(params.Conditions)
orderBy := req.QueryParameter(params.OrderBy)
limit, offset := params.ParsePaging(req.QueryParameter(params.Paging))
reverse := params.ParseReserve(req.QueryParameter(params.Reserve))
result, err := resources.ListNamespaceResource(namespace, resourceName, conditions, orderBy, reverse, limit, offset)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(result)
}

View File

@@ -0,0 +1,68 @@
/*
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/emicklei/go-restful"
"k8s.io/api/core/v1"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/storage"
)
type pvcList struct {
Name string `json:"name"`
Items []*v1.PersistentVolumeClaim `json:"items"`
}
type podListByPvc struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
Pods []*v1.Pod `json:"pods"`
}
// List all pods of a specific PVC
// Extended API URL: "GET /api/v1alpha2/namespaces/{namespace}/persistentvolumeclaims/{name}/pods"
func GetPodListByPvc(request *restful.Request, response *restful.Response) {
pvcName := request.PathParameter("pvc")
nsName := request.PathParameter("namespace")
pods, err := storage.GetPodListByPvc(pvcName, nsName)
if errors.HandlerError(err, response) {
return
}
result := podListByPvc{Name: pvcName, Namespace: nsName, Pods: pods}
response.WriteAsJson(result)
}
// List all PersistentVolumeClaims of a specific StorageClass
// Extended API URL: "GET /api/v1alpha2/storageclasses/{storageclass}/persistentvolumeclaims"
func GetPvcListBySc(request *restful.Request, response *restful.Response) {
scName := request.PathParameter("storageclass")
claims, err := storage.GetPvcListBySc(scName)
if errors.HandlerError(err, response) {
return
}
result := pvcList{
Name: scName, Items: claims,
}
response.WriteAsJson(result)
}

View File

@@ -0,0 +1,52 @@
/*
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/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/kubeconfig"
"kubesphere.io/kubesphere/pkg/models/kubectl"
)
func GetKubectl(req *restful.Request, resp *restful.Response) {
user := req.PathParameter("username")
kubectlPod, err := kubectl.GetKubectlPod(user)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(kubectlPod)
}
func GetKubeconfig(req *restful.Request, resp *restful.Response) {
user := req.PathParameter("username")
kubectlConfig, err := kubeconfig.GetKubeConfig(user)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(kubectlConfig)
}

View File

@@ -0,0 +1,116 @@
/*
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 revisions
import (
"net/http"
"strconv"
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"k8s.io/api/apps/v1"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/revisions"
)
func V1Alpha2(ws *restful.WebService) {
ws.Route(ws.GET("/namespaces/{namespace}/daemonsets/{daemonset}/revisions/{revision}").
To(getDaemonSetRevision).
Metadata(restfulspec.KeyOpenAPITags, []string{"daemonsets", "revision"}).
Doc("Handle daemonset operation").
Param(ws.PathParameter("daemonset", "daemonset's name").
DataType("string")).
Param(ws.PathParameter("namespace", "daemonset's namespace").
DataType("string")).
Param(ws.PathParameter("revision", "daemonset's revision")).
Writes(v1.DaemonSet{}))
ws.Route(ws.GET("/namespaces/{namespace}/deployments/{deployment}/revisions/{revision}").
To(getDeployRevision).
Metadata(restfulspec.KeyOpenAPITags, []string{"deployments", "revision"}).
Doc("Handle deployment operation").
Param(ws.PathParameter("deployment", "deployment's name").
DataType("string")).
Param(ws.PathParameter("namespace",
"deployment's namespace").
DataType("string")).
Param(ws.PathParameter("deployment", "deployment's name")).
Writes(v1.ReplicaSet{}))
ws.Route(ws.GET("/namespaces/{namespace}/statefulsets/{statefulset}/revisions/{revision}").
To(getStatefulSetRevision).
Metadata(restfulspec.KeyOpenAPITags, []string{"statefulsets", "revisions"}).
Doc("Handle statefulset operation").
Param(ws.PathParameter("statefulset", "statefulset's name").
DataType("string")).
Param(ws.PathParameter("namespace", "statefulset's namespace").
DataType("string")).
Param(ws.PathParameter("revision", "statefulset's revision")).
Writes(v1.StatefulSet{}))
}
func getDaemonSetRevision(req *restful.Request, resp *restful.Response) {
daemonset := req.PathParameter("daemonset")
namespace := req.PathParameter("namespace")
revision, err := strconv.Atoi(req.PathParameter("revision"))
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, err.Error()))
return
}
result, err := revisions.GetDaemonSetRevision(namespace, daemonset, revision)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(result)
}
func getDeployRevision(req *restful.Request, resp *restful.Response) {
deploy := req.PathParameter("deployment")
namespace := req.PathParameter("namespace")
revision := req.PathParameter("revision")
result, err := revisions.GetDeployRevision(namespace, deploy, revision)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(result)
}
func getStatefulSetRevision(req *restful.Request, resp *restful.Response) {
statefulset := req.PathParameter("statefulset")
namespace := req.PathParameter("namespace")
revision, err := strconv.Atoi(req.PathParameter("revision"))
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, err.Error()))
return
}
result, err := revisions.GetStatefulSetRevision(namespace, statefulset, revision)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(result)
}

View File

@@ -0,0 +1,187 @@
/*
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 routers
import (
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/errors"
"net/http"
"strings"
"github.com/golang/glog"
"k8s.io/api/core/v1"
"kubesphere.io/kubesphere/pkg/models/routers"
)
func V1Alpha2(ws *restful.WebService) {
ws.Route(ws.GET("/routers").To(getAllRouters).
Doc("Get all routers"))
ws.Route(ws.GET("/users/{username}/routers").To(getAllRoutersOfUser).
Doc("Get routers for user"))
ws.Route(ws.GET("/namespaces/{namespace}/router").To(getRouter).
Doc("Get router of a specified project").
Param(ws.PathParameter("namespace", "name of the project").
DataType("string")))
ws.Route(ws.DELETE("/namespaces/{namespace}/router").To(deleteRouter).
Doc("Get router of a specified project").
Param(ws.PathParameter("namespace", "name of the project").
DataType("string")))
ws.Route(ws.POST("/namespaces/{namespace}/router").To(createRouter).
Doc("Create a router for a specified project").
Param(ws.PathParameter("namespace", "name of the project").
DataType("string")))
ws.Route(ws.PUT("/namespaces/{namespace}/router").To(updateRouter).
Doc("Update a router for a specified project").
Param(ws.PathParameter("namespace", "name of the project").
DataType("string")))
}
type Router struct {
RouterType string `json:"type"`
Annotations map[string]string `json:"annotations"`
}
// Get all namespace ingress controller services
func getAllRouters(request *restful.Request, response *restful.Response) {
routers, err := routers.GetAllRouters()
if errors.HandlerError(err, response) {
return
}
response.WriteAsJson(routers)
}
// Get all namespace ingress controller services for user
func getAllRoutersOfUser(request *restful.Request, response *restful.Response) {
username := request.PathParameter("username")
routers, err := routers.GetAllRoutersOfUser(username)
if errors.HandlerError(err, response) {
return
}
response.WriteAsJson(routers)
}
// Get ingress controller service for specified namespace
func getRouter(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace")
router, err := routers.GetRouter(namespace)
if errors.HandlerError(err, response) {
return
}
response.WriteAsJson(router)
}
// Create ingress controller and related services
func createRouter(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace")
newRouter := Router{}
err := request.ReadEntity(&newRouter)
if err != nil {
response.WriteAsJson(err)
return
}
var router *v1.Service
serviceType, annotationMap, err := ParseParameter(newRouter)
if err != nil {
glog.Error("Wrong annotations, missing key or value")
response.WriteHeaderAndEntity(http.StatusBadRequest,
errors.New(errors.InvalidArgument, "Wrong annotations, missing key or value"))
return
}
router, err = routers.CreateRouter(namespace, serviceType, annotationMap)
if errors.HandlerError(err, response) {
return
}
response.WriteAsJson(router)
}
// Delete ingress controller and services
func deleteRouter(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace")
router, err := routers.DeleteRouter(namespace)
if errors.HandlerError(err, response) {
return
}
response.WriteAsJson(router)
}
func updateRouter(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace")
newRouter := Router{}
err := request.ReadEntity(&newRouter)
if err != nil {
glog.Error(err)
response.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, err.Error()))
return
}
serviceType, annotationMap, err := ParseParameter(newRouter)
router, err := routers.UpdateRouter(namespace, serviceType, annotationMap)
if errors.HandlerError(err, response) {
return
}
response.WriteAsJson(router)
}
func ParseParameter(router Router) (routerType v1.ServiceType, annotationMap map[string]string, err error) {
routerType = v1.ServiceTypeNodePort
if strings.Compare(strings.ToLower(router.RouterType), "loadbalancer") == 0 {
return v1.ServiceTypeLoadBalancer, router.Annotations, nil
} else {
return v1.ServiceTypeNodePort, make(map[string]string, 0), nil
}
}

View File

@@ -0,0 +1,64 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package runtime
import (
"github.com/emicklei/go-restful"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const (
ApiRootPath = "/apis"
)
// container holds all webservice of apiserver
var Container = restful.NewContainer()
type ContainerBuilder []func(c *restful.Container) error
//
func NewWebService(gv schema.GroupVersion) *restful.WebService {
webservice := restful.WebService{}
webservice.Path(ApiRootPath + "/" + gv.String())
return &webservice
}
func (cb *ContainerBuilder) AddToContainer(c *restful.Container) error {
for _, f := range *cb {
if err := f(c); err != nil {
return err
}
}
return nil
}
func (cb *ContainerBuilder) Register(funcs ...func(*restful.Container) error) {
for _, f := range funcs {
*cb = append(*cb, f)
}
}
//
func NewContainerBuilder(funcs ...func(*restful.Container) error) ContainerBuilder {
var cb ContainerBuilder
cb.Register(funcs...)
return cb
}

View File

@@ -0,0 +1,59 @@
/*
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 workloadstatuses
import (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/status"
)
func V1Alpha2(ws *restful.WebService) {
tags := []string{"workloadStatus"}
ws.Route(ws.GET("/workloadstatuses").
Doc("get abnormal workloads' count of whole cluster").
Metadata(restfulspec.KeyOpenAPITags, tags).
To(getClusterResourceStatus))
ws.Route(ws.GET("/namespaces/{namespace}/workloadstatuses").
Doc("get abnormal workloads' count of specified namespace").
Param(ws.PathParameter("namespace", "the name of namespace").
DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
To(getNamespacesResourceStatus))
}
func getClusterResourceStatus(req *restful.Request, resp *restful.Response) {
res, err := status.GetClusterResourceStatus()
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(res)
}
func getNamespacesResourceStatus(req *restful.Request, resp *restful.Response) {
res, err := status.GetNamespacesResourceStatus(req.PathParameter("namespace"))
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(res)
}

View File

@@ -0,0 +1,506 @@
/*
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 workspaces
import (
"net/http"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/metrics"
"github.com/emicklei/go-restful"
"k8s.io/api/core/v1"
"fmt"
"strings"
"strconv"
"regexp"
"sort"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/workspaces"
)
const UserNameHeader = "X-Token-Username"
func V1Alpha2(ws *restful.WebService) {
ws.Route(ws.GET("/workspaces").To(UserWorkspaceListHandler))
ws.Route(ws.POST("/workspaces").To(WorkspaceCreateHandler))
ws.Route(ws.DELETE("/workspaces/{name}").To(DeleteWorkspaceHandler))
ws.Route(ws.GET("/workspaces/{name}").To(WorkspaceDetailHandler))
ws.Route(ws.PUT("/workspaces/{name}").To(WorkspaceEditHandler))
ws.Route(ws.GET("/workspaces/{workspace}/namespaces").To(UserNamespaceListHandler))
ws.Route(ws.GET("/workspaces/{workspace}/members/{username}/namespaces").To(UserNamespaceListHandler))
ws.Route(ws.POST("/workspaces/{name}/namespaces").To(NamespaceCreateHandler))
ws.Route(ws.DELETE("/workspaces/{name}/namespaces/{namespace}").To(NamespaceDeleteHandler))
ws.Route(ws.GET("/workspaces/{name}/namespaces/{namespace}").To(NamespaceCheckHandler))
ws.Route(ws.GET("/namespaces/{namespace}").To(NamespaceCheckHandler))
ws.Route(ws.GET("/workspaces/{name}/devops").To(DevOpsProjectHandler))
ws.Route(ws.GET("/workspaces/{name}/members/{username}/devops").To(DevOpsProjectHandler))
ws.Route(ws.POST("/workspaces/{name}/devops").To(DevOpsProjectCreateHandler))
ws.Route(ws.DELETE("/workspaces/{name}/devops/{id}").To(DevOpsProjectDeleteHandler))
ws.Route(ws.GET("/workspaces/{name}/members").To(MembersHandler))
ws.Route(ws.GET("/workspaces/{name}/members/{member}").To(MemberHandler))
ws.Route(ws.GET("/workspaces/{name}/roles").To(RolesHandler))
// TODO /workspaces/{name}/roles/{role}
ws.Route(ws.POST("/workspaces/{name}/members").To(MembersInviteHandler))
ws.Route(ws.DELETE("/workspaces/{name}/members").To(MembersRemoveHandler))
}
func RolesHandler(req *restful.Request, resp *restful.Response) {
name := req.PathParameter("name")
workspace, err := workspaces.Detail(name)
if errors.HandlerError(err, resp) {
return
}
roles, err := workspaces.Roles(workspace)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(roles)
}
func MembersHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("name")
keyword := req.QueryParameter("keyword")
users, err := workspaces.GetWorkspaceMembers(workspace, keyword)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(users)
}
func MemberHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("name")
username := req.PathParameter("member")
user, err := iam.GetUser(username)
if errors.HandlerError(err, resp) {
return
}
namespaces, err := workspaces.Namespaces(workspace)
if errors.HandlerError(err, resp) {
return
}
user.WorkspaceRole = user.WorkspaceRoles[workspace]
roles := make(map[string]string)
for _, namespace := range namespaces {
if role := user.Roles[namespace.Name]; role != "" {
roles[namespace.Name] = role
}
}
user.Roles = roles
user.Rules = nil
user.WorkspaceRules = nil
user.WorkspaceRoles = nil
user.ClusterRules = nil
resp.WriteAsJson(user)
}
func MembersInviteHandler(req *restful.Request, resp *restful.Response) {
var users []workspaces.UserInvite
workspace := req.PathParameter("name")
err := req.ReadEntity(&users)
if errors.HandlerError(err, resp) {
return
}
err = workspaces.Invite(workspace, users)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(errors.None)
}
func MembersRemoveHandler(req *restful.Request, resp *restful.Response) {
query := req.QueryParameter("name")
workspace := req.PathParameter("name")
names := strings.Split(query, ",")
err := workspaces.RemoveMembers(workspace, names)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(errors.None)
}
func NamespaceCheckHandler(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
exist, err := workspaces.NamespaceExistCheck(namespace)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(map[string]bool{"exist": exist})
}
func NamespaceDeleteHandler(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
workspace := req.PathParameter("name")
err := workspaces.DeleteNamespace(workspace, namespace)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(errors.None)
}
func DevOpsProjectDeleteHandler(req *restful.Request, resp *restful.Response) {
devops := req.PathParameter("id")
workspace := req.PathParameter("name")
force := req.QueryParameter("force")
username := req.HeaderParameter(UserNameHeader)
err := workspaces.UnBindDevopsProject(workspace, devops)
if err != nil && force != "true" {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.New(errors.Internal, err.Error()))
return
}
err = workspaces.DeleteDevopsProject(username, devops)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(errors.None)
}
func DevOpsProjectCreateHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("name")
username := req.HeaderParameter(UserNameHeader)
var devops workspaces.DevopsProject
err := req.ReadEntity(&devops)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, err.Error()))
return
}
project, err := workspaces.CreateDevopsProject(username, workspace, devops)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(project)
}
func NamespaceCreateHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("name")
username := req.HeaderParameter(UserNameHeader)
namespace := &v1.Namespace{}
err := req.ReadEntity(namespace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, err.Error()))
return
}
if namespace.Annotations == nil {
namespace.Annotations = make(map[string]string, 0)
}
namespace.Annotations["creator"] = username
namespace.Annotations["workspace"] = workspace
if namespace.Labels == nil {
namespace.Labels = make(map[string]string, 0)
}
namespace.Labels["kubesphere.io/workspace"] = workspace
namespace, err = workspaces.CreateNamespace(namespace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, err.Error()))
return
}
resp.WriteAsJson(namespace)
}
func DevOpsProjectHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("name")
username := req.PathParameter("username")
keyword := req.QueryParameter("keyword")
if username == "" {
username = req.HeaderParameter(UserNameHeader)
}
limit := 65535
offset := 0
orderBy := "createTime"
reverse := true
if groups := regexp.MustCompile(`^limit=(\d+),page=(\d+)$`).FindStringSubmatch(req.QueryParameter("paging")); len(groups) == 3 {
limit, _ = strconv.Atoi(groups[1])
page, _ := strconv.Atoi(groups[2])
offset = (page - 1) * limit
}
if groups := regexp.MustCompile(`^(createTime|name)$`).FindStringSubmatch(req.QueryParameter("order")); len(groups) == 2 {
orderBy = groups[1]
reverse = false
}
if q := req.QueryParameter("reverse"); q != "" {
b, err := strconv.ParseBool(q)
if err == nil {
reverse = b
}
}
total, devOpsProjects, err := workspaces.ListDevopsProjectsByUser(username, workspace, keyword, orderBy, reverse, limit, offset)
if errors.HandlerError(err, resp) {
return
}
result := models.PageableResponse{}
result.TotalCount = total
result.Items = make([]interface{}, 0)
for _, n := range devOpsProjects {
result.Items = append(result.Items, n)
}
resp.WriteAsJson(result)
}
func WorkspaceCreateHandler(req *restful.Request, resp *restful.Response) {
var workspace workspaces.Workspace
username := req.HeaderParameter(UserNameHeader)
err := req.ReadEntity(&workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, err.Error()))
return
}
if workspace.Name == "" || strings.Contains(workspace.Name, ":") {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, "invalid workspace name"))
return
}
workspace.Path = workspace.Name
workspace.Members = nil
if workspace.Admin != "" {
workspace.Creator = workspace.Admin
} else {
workspace.Creator = username
}
created, err := workspaces.Create(&workspace)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(created)
}
func DeleteWorkspaceHandler(req *restful.Request, resp *restful.Response) {
name := req.PathParameter("name")
if name == "" || strings.Contains(name, ":") {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, "invalid workspace name"))
return
}
workspace, err := workspaces.Detail(name)
if errors.HandlerError(err, resp) {
return
}
err = workspaces.Delete(workspace)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(errors.None)
}
func WorkspaceEditHandler(req *restful.Request, resp *restful.Response) {
var workspace workspaces.Workspace
name := req.PathParameter("name")
err := req.ReadEntity(&workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, err.Error()))
return
}
if name != workspace.Name {
resp.WriteError(http.StatusBadRequest, fmt.Errorf("the name of workspace (%s) does not match the name on the URL (%s)", workspace.Name, name))
return
}
if workspace.Name == "" || strings.Contains(workspace.Name, ":") {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, "invalid workspace name"))
return
}
workspace.Path = workspace.Name
workspace.Members = nil
edited, err := workspaces.Edit(&workspace)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(edited)
}
func WorkspaceDetailHandler(req *restful.Request, resp *restful.Response) {
name := req.PathParameter("name")
workspace, err := workspaces.Detail(name)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(workspace)
}
// List all workspaces for the current user
func UserWorkspaceListHandler(req *restful.Request, resp *restful.Response) {
keyword := req.QueryParameter("keyword")
username := req.HeaderParameter(UserNameHeader)
ws, err := workspaces.ListWorkspaceByUser(username, keyword)
if errors.HandlerError(err, resp) {
return
}
sort.Slice(ws, func(i, j int) bool {
t1, err := ws[i].GetCreateTime()
if err != nil {
return false
}
t2, err := ws[j].GetCreateTime()
if err != nil {
return true
}
return t1.After(t2)
})
resp.WriteAsJson(ws)
}
func UserNamespaceListHandler(req *restful.Request, resp *restful.Response) {
withMetrics, err := strconv.ParseBool(req.QueryParameter("metrics"))
if err != nil {
withMetrics = false
}
username := req.PathParameter("username")
keyword := req.QueryParameter("keyword")
if username == "" {
username = req.HeaderParameter(UserNameHeader)
}
limit := 65535
offset := 0
orderBy := "createTime"
reverse := true
if groups := regexp.MustCompile(`^limit=(\d+),page=(\d+)$`).FindStringSubmatch(req.QueryParameter("paging")); len(groups) == 3 {
limit, _ = strconv.Atoi(groups[1])
page, _ := strconv.Atoi(groups[2])
if page < 0 {
page = 1
}
offset = (page - 1) * limit
}
if groups := regexp.MustCompile(`^(createTime|name)$`).FindStringSubmatch(req.QueryParameter("order")); len(groups) == 2 {
orderBy = groups[1]
reverse = false
}
if q := req.QueryParameter("reverse"); q != "" {
b, err := strconv.ParseBool(q)
if err == nil {
reverse = b
}
}
workspaceName := req.PathParameter("workspace")
total, namespaces, err := workspaces.ListNamespaceByUser(workspaceName, username, keyword, orderBy, reverse, limit, offset)
if withMetrics {
namespaces = metrics.GetNamespacesWithMetrics(namespaces)
}
if errors.HandlerError(err, resp) {
return
}
result := models.PageableResponse{}
result.TotalCount = total
result.Items = make([]interface{}, 0)
for _, n := range namespaces {
result.Items = append(result.Items, n)
}
resp.WriteAsJson(result)
}

51
pkg/client/dbclient.go Normal file
View File

@@ -0,0 +1,51 @@
/*
Copyright 2018 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 client
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/golang/glog"
"github.com/jinzhu/gorm"
)
var dbClient *gorm.DB
func NewDBClient() *gorm.DB {
conn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", "", "", "", "")
db, err := gorm.Open("mysql", conn)
if err != nil {
glog.Error(err)
panic(err)
}
return db
}
func NewSharedDBClient() *gorm.DB {
if dbClient != nil {
err := dbClient.DB().Ping()
if err == nil {
return dbClient
} else {
glog.Error(err)
panic(err)
}
}
return NewDBClient()
}

97
pkg/client/k8sclient.go Normal file
View File

@@ -0,0 +1,97 @@
/*
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 client
import (
"fmt"
"os"
"sync"
"github.com/mitchellh/go-homedir"
"github.com/golang/glog"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)
var (
KubeConfigFile string
k8sClient *kubernetes.Clientset
k8sClientOnce sync.Once
KubeConfig *rest.Config
)
func K8sClient() *kubernetes.Clientset {
k8sClientOnce.Do(func() {
config, err := getKubeConfig()
if err != nil {
glog.Fatalf("cannot load kubeconfig: %v", err)
}
k8sClient, err = kubernetes.NewForConfig(config)
if err != nil {
glog.Fatalf("cannot create k8s client: %v", err)
}
KubeConfig = config
})
return k8sClient
}
func getKubeConfig() (kubeConfig *rest.Config, err error) {
if KubeConfigFile == "" {
if env := os.Getenv("KUBECONFIG"); env != "" {
KubeConfigFile = env
} else {
if home, err := homedir.Dir(); err == nil {
KubeConfigFile = fmt.Sprintf("%s/.kube/config", home)
}
}
}
if KubeConfigFile != "" {
kubeConfig, err = clientcmd.BuildConfigFromFlags("", KubeConfigFile)
if err != nil {
return nil, err
}
} else {
kubeConfig, err = rest.InClusterConfig()
if err != nil {
return nil, err
}
}
kubeConfig.QPS = 1e6
kubeConfig.Burst = 1e6
return kubeConfig, nil
}

View File

@@ -0,0 +1,192 @@
/*
Copyright 2018 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 client
import (
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"os"
"github.com/emicklei/go-restful"
"github.com/golang/glog"
)
const (
DefaultScheme = "http"
DefaultPrometheusPort = "9090"
PrometheusApiPath = "/api/v1/"
DefaultQueryStep = "10m"
DefaultQueryTimeout = "10s"
RangeQueryType = "query_range?"
DefaultQueryType = "query?"
PrometheusAPIServerEnv = "PROMETHEUS_API_SERVER"
)
var PrometheusAPIServer = "prometheus-k8s.kubesphere-monitoring-system.svc"
var PrometheusEndpointUrl string
func init() {
if env := os.Getenv(PrometheusAPIServerEnv); env != "" {
PrometheusAPIServer = env
}
PrometheusEndpointUrl = DefaultScheme + "://" + PrometheusAPIServer + ":" + DefaultPrometheusPort + PrometheusApiPath
}
type MonitoringRequestParams struct {
Params url.Values
QueryType string
SortMetricName string
SortType string
PageNum string
LimitNum string
Tp string
MetricsFilter string
NodesFilter string
WsFilter string
NsFilter string
PodsFilter string
ContainersFilter string
MetricsName string
WorkloadName string
WlFilter string
NodeId string
WsName string
NsName string
PodName string
ContainerName string
WorkloadKind string
}
var client = &http.Client{}
func SendMonitoringRequest(queryType string, params string) string {
epurl := PrometheusEndpointUrl + queryType + params
response, err := client.Get(epurl)
if err != nil {
glog.Error(err)
} else {
defer response.Body.Close()
contents, err := ioutil.ReadAll(response.Body)
if err != nil {
glog.Error(err)
}
return string(contents)
}
return ""
}
func ParseMonitoringRequestParams(request *restful.Request) *MonitoringRequestParams {
instantTime := strings.Trim(request.QueryParameter("time"), " ")
start := strings.Trim(request.QueryParameter("start"), " ")
end := strings.Trim(request.QueryParameter("end"), " ")
step := strings.Trim(request.QueryParameter("step"), " ")
timeout := strings.Trim(request.QueryParameter("timeout"), " ")
sortMetricName := strings.Trim(request.QueryParameter("sort_metric"), " ")
sortType := strings.Trim(request.QueryParameter("sort_type"), " ")
pageNum := strings.Trim(request.QueryParameter("page"), " ")
limitNum := strings.Trim(request.QueryParameter("limit"), " ")
tp := strings.Trim(request.QueryParameter("type"), " ")
metricsFilter := strings.Trim(request.QueryParameter("metrics_filter"), " ")
nodesFilter := strings.Trim(request.QueryParameter("nodes_filter"), " ")
wsFilter := strings.Trim(request.QueryParameter("workspaces_filter"), " ")
nsFilter := strings.Trim(request.QueryParameter("namespaces_filter"), " ")
wlFilter := strings.Trim(request.QueryParameter("workloads_filter"), " ")
podsFilter := strings.Trim(request.QueryParameter("pods_filter"), " ")
containersFilter := strings.Trim(request.QueryParameter("containers_filter"), " ")
metricsName := strings.Trim(request.QueryParameter("metrics_name"), " ")
workloadName := strings.Trim(request.QueryParameter("workload_name"), " ")
nodeId := strings.Trim(request.PathParameter("node_id"), " ")
wsName := strings.Trim(request.PathParameter("workspace_name"), " ")
nsName := strings.Trim(request.PathParameter("ns_name"), " ")
podName := strings.Trim(request.PathParameter("pod_name"), " ")
containerName := strings.Trim(request.PathParameter("container_name"), " ")
workloadKind := strings.Trim(request.PathParameter("workload_kind"), " ")
var requestParams = MonitoringRequestParams{
SortMetricName: sortMetricName,
SortType: sortType,
PageNum: pageNum,
LimitNum: limitNum,
Tp: tp,
MetricsFilter: metricsFilter,
NodesFilter: nodesFilter,
WsFilter: wsFilter,
NsFilter: nsFilter,
PodsFilter: podsFilter,
ContainersFilter: containersFilter,
MetricsName: metricsName,
WorkloadName: workloadName,
WlFilter: wlFilter,
NodeId: nodeId,
WsName: wsName,
NsName: nsName,
PodName: podName,
ContainerName: containerName,
WorkloadKind: workloadKind,
}
if timeout == "" {
timeout = DefaultQueryTimeout
}
if step == "" {
step = DefaultQueryStep
}
// Whether query or query_range request
u := url.Values{}
if start != "" && end != "" {
u.Set("start", convertTimeGranularity(start))
u.Set("end", convertTimeGranularity(end))
u.Set("step", step)
u.Set("timeout", timeout)
requestParams.QueryType = RangeQueryType
requestParams.Params = u
return &requestParams
}
if instantTime != "" {
u.Set("time", instantTime)
u.Set("timeout", timeout)
requestParams.QueryType = DefaultQueryType
requestParams.Params = u
return &requestParams
} else {
//u.Set("time", strconv.FormatInt(int64(time.Now().Unix()), 10))
u.Set("timeout", timeout)
requestParams.QueryType = DefaultQueryType
requestParams.Params = u
return &requestParams
}
}
func convertTimeGranularity(ts string) string {
timeFloat, err := strconv.ParseFloat(ts, 64)
if err != nil {
glog.Errorf("convert second timestamp %s to minute timestamp failed", ts)
return strconv.FormatInt(int64(time.Now().Unix()), 10)
}
timeInt := int64(timeFloat)
// convert second timestamp to minute timestamp
secondTime := time.Unix(timeInt, 0).Truncate(time.Minute).Unix()
return strconv.FormatInt(secondTime, 10)
}

View File

@@ -0,0 +1,55 @@
package constants
import "os"
const (
APIVersion = "v1alpha1"
KubeSystemNamespace = "kube-system"
OpenPitrixNamespace = "openpitrix-system"
IstioNamespace = "istio-system"
KubeSphereNamespace = "kubesphere-system"
KubeSphereControlNamespace = "kubesphere-controls-system"
IngressControllerNamespace = KubeSphereControlNamespace
AdminUserName = "admin"
DataHome = "/etc/kubesphere"
IngressControllerFolder = DataHome + "/ingress-controller"
IngressControllerPrefix = "kubesphere-router-"
WorkspaceLabelKey = "kubesphere.io/workspace"
WorkspaceAdmin = "workspace-admin"
ClusterAdmin = "cluster-admin"
WorkspaceRegular = "workspace-regular"
WorkspaceViewer = "workspace-viewer"
DevopsOwner = "owner"
DevopsReporter = "reporter"
DevopsAPIServerEnv = "DEVOPS_API_SERVER"
AccountAPIServerEnv = "ACCOUNT_API_SERVER"
DevopsProxyTokenEnv = "DEVOPS_PROXY_TOKEN"
OpenPitrixProxyTokenEnv = "OPENPITRIX_PROXY_TOKEN"
)
var (
WorkSpaceRoles = []string{WorkspaceAdmin, WorkspaceRegular, WorkspaceViewer}
DevopsAPIServer = "ks-devops-apiserver.kubesphere-system.svc"
AccountAPIServer = "ks-account.kubesphere-system.svc"
DevopsProxyToken = ""
OpenPitrixProxyToken = ""
SystemNamespaces = []string{KubeSystemNamespace, OpenPitrixNamespace, KubeSystemNamespace}
)
func init() {
if env := os.Getenv(DevopsAPIServerEnv); env != "" {
DevopsAPIServer = env
}
if env := os.Getenv(AccountAPIServerEnv); env != "" {
AccountAPIServer = env
}
if env := os.Getenv(DevopsProxyTokenEnv); env != "" {
DevopsProxyToken = env
}
if env := os.Getenv(OpenPitrixProxyTokenEnv); env != "" {
OpenPitrixProxyToken = env
}
}

View File

@@ -0,0 +1,34 @@
/*
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 controller
import (
"sigs.k8s.io/controller-runtime/pkg/manager"
)
// AddToManagerFuncs is a list of functions to add all Controllers to the Manager
var AddToManagerFuncs []func(manager.Manager) error
// AddToManager adds all Controllers to the Manager
func AddToManager(m manager.Manager) error {
for _, f := range AddToManagerFuncs {
if err := f(m); err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,29 @@
package controller
import (
"kubesphere.io/kubesphere/pkg/controller/namespace"
"log"
"sync"
"time"
"k8s.io/client-go/informers"
"kubesphere.io/kubesphere/pkg/client"
)
const defaultResync = 600 * time.Second
var once sync.Once
func Run(stopCh <-chan struct{}) {
once.Do(func() {
kubeclientset := client.K8sClient()
informerFactory := informers.NewSharedInformerFactory(kubeclientset, defaultResync)
namespaceController := namespace.NewNamespaceController(kubeclientset, informerFactory.Core().V1().Namespaces(), informerFactory.Rbac().V1().Roles())
// data sync
informerFactory.Start(stopCh)
// start workers
namespaceController.Start(stopCh)
log.Println("all controller is running")
})
}

View File

@@ -0,0 +1,10 @@
package controller
import (
"testing"
)
// controller test
func TestController(t *testing.T) {
}

View File

@@ -0,0 +1,191 @@
/*
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 namespace
import (
"fmt"
"time"
"github.com/golang/glog"
corev1 "k8s.io/api/core/v1"
rbac "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/errors"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
coreinformers "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/informers/rbac/v1"
rbacinformers "k8s.io/client-go/informers/rbac/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
)
const threadiness = 2
var (
defaultRoles = []rbac.Role{
{ObjectMeta: metaV1.ObjectMeta{Name: "admin", Annotations: map[string]string{"creator": "system"}}, Rules: []rbac.PolicyRule{{Verbs: []string{"*"}, APIGroups: []string{"*"}, Resources: []string{"*"}}}},
{ObjectMeta: metaV1.ObjectMeta{Name: "operator", Annotations: map[string]string{"creator": "system"}}, Rules: []rbac.PolicyRule{{Verbs: []string{"get", "list", "watch"}, APIGroups: []string{"*"}, Resources: []string{"*"}}, {Verbs: []string{"*"}, APIGroups: []string{"", "apps", "extensions", "batch", "kubesphere.io", "account.kubesphere.io", "autoscaling"}, Resources: []string{"*"}}}},
{ObjectMeta: metaV1.ObjectMeta{Name: "viewer", Annotations: map[string]string{"creator": "system"}}, Rules: []rbac.PolicyRule{{Verbs: []string{"get", "list", "watch"}, APIGroups: []string{"*"}, Resources: []string{"*"}}}},
}
)
type NamespaceController struct {
clientset kubernetes.Interface
namespaceInformer coreinformers.NamespaceInformer
roleInformer v1.RoleInformer
workqueue workqueue.RateLimitingInterface
}
func NewNamespaceController(
kubeclientset kubernetes.Interface,
namespaceInformer coreinformers.NamespaceInformer,
roleInformer rbacinformers.RoleInformer) *NamespaceController {
controller := &NamespaceController{
clientset: kubeclientset,
namespaceInformer: namespaceInformer,
roleInformer: roleInformer,
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "namespaces"),
}
glog.Info("setting up event handlers")
namespaceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: controller.handleObject,
UpdateFunc: func(old, new interface{}) {
newNamespace := new.(*corev1.Namespace)
oldNamespace := old.(*corev1.Namespace)
if newNamespace.ResourceVersion == oldNamespace.ResourceVersion {
return
}
controller.handleObject(new)
},
DeleteFunc: controller.handleObject,
})
roleInformer.Lister()
return controller
}
func (c *NamespaceController) Start(stopCh <-chan struct{}) {
go func() {
defer utilruntime.HandleCrash()
defer c.workqueue.ShutDown()
// Start the informer factories to begin populating the informer caches
glog.Info("starting namespace controller")
// Wait for the caches to be synced before starting workers
glog.Info("waiting for informer caches to sync")
if ok := cache.WaitForCacheSync(stopCh, c.namespaceInformer.Informer().HasSynced, c.roleInformer.Informer().HasSynced); !ok {
glog.Fatalf("controller exit with error: failed to wait for caches to sync")
}
glog.Info("starting workers")
for i := 0; i < threadiness; i++ {
go wait.Until(c.runWorker, time.Second, stopCh)
}
glog.Info("started workers")
<-stopCh
glog.Info("shutting down workers")
}()
}
func (c *NamespaceController) runWorker() {
for c.processNextWorkItem() {
}
}
func (c *NamespaceController) processNextWorkItem() bool {
obj, shutdown := c.workqueue.Get()
if shutdown {
return false
}
err := func(obj interface{}) error {
defer c.workqueue.Done(obj)
var namespace string
var ok bool
if namespace, ok = obj.(string); !ok {
c.workqueue.Forget(obj)
utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj))
return nil
}
if err := c.syncHandler(namespace); err != nil {
c.workqueue.AddRateLimited(namespace)
return fmt.Errorf("error syncing '%s': %s, requeuing", namespace, err.Error())
}
c.workqueue.Forget(obj)
glog.Infof("successfully namespace synced '%s'", namespace)
return nil
}(obj)
if err != nil {
utilruntime.HandleError(err)
return true
}
return true
}
func (c *NamespaceController) syncHandler(name string) error {
_, err := c.namespaceInformer.Lister().Get(name)
// Handler delete event
if errors.IsNotFound(err) {
return nil
}
// Handler update or create event
if err := c.checkRoles(name); err != nil {
return err
}
return nil
}
func (c *NamespaceController) handleObject(obj interface{}) {
if namespace, ok := obj.(*corev1.Namespace); ok {
c.workqueue.AddRateLimited(namespace.Name)
}
}
func (c *NamespaceController) checkRoles(namespace string) error {
for _, role := range defaultRoles {
_, err := c.roleInformer.Lister().Roles(namespace).Get(role.Name)
if errors.IsNotFound(err) {
r := role.DeepCopy()
r.Namespace = namespace
_, err := c.clientset.RbacV1().Roles(namespace).Create(r)
if err != nil && !errors.IsAlreadyExists(err) {
return err
}
}
}
return nil
}

17
pkg/errors/code.go Normal file
View File

@@ -0,0 +1,17 @@
package errors
type Code int
const (
OK Code = iota
Canceled
Unknown
InvalidArgument
Internal // 5
Unavailable
AlreadyExists
NotFound
NotImplement
VerifyFailed
Conflict
)

16
pkg/errors/code_string.go Normal file
View File

@@ -0,0 +1,16 @@
// Code generated by "stringer -type=Code"; DO NOT EDIT.
package errors
import "strconv"
const _Code_name = "OKCanceledUnknownInvalidArgumentInternalUnavailableAlreadyExistsWTFNotFoundNotImplementVerifyFailed"
var _Code_index = [...]uint8{0, 2, 10, 17, 32, 40, 51, 64, 67, 75, 87, 99}
func (i Code) String() string {
if i < 0 || i >= Code(len(_Code_index)-1) {
return "Code(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Code_name[_Code_index[i]:_Code_index[i+1]]
}

7
pkg/errors/code_test.go Normal file
View File

@@ -0,0 +1,7 @@
package errors
import "testing"
func TestCode_String(t *testing.T) {
t.Log(Code(1).String())
}

114
pkg/errors/errors.go Normal file
View File

@@ -0,0 +1,114 @@
package errors
import (
"encoding/json"
"errors"
"fmt"
"net"
"net/http"
"reflect"
k8sError "k8s.io/apimachinery/pkg/api/errors"
"github.com/emicklei/go-restful"
"github.com/go-sql-driver/mysql"
"github.com/golang/glog"
)
type Error struct {
Code Code `json:"code"`
Message string `json:"message"`
}
var None = New(OK, "success")
func (e *Error) Error() string {
return fmt.Sprintf("error: %v,message: %v", e.Code.String(), e.Message)
}
func (e *Error) HttpStatusCode() int {
switch e.Code {
case OK:
return http.StatusOK
case InvalidArgument:
return http.StatusBadRequest
case AlreadyExists:
return http.StatusConflict
case Unavailable:
return http.StatusServiceUnavailable
case NotImplement:
return http.StatusNotImplemented
case VerifyFailed:
return http.StatusBadRequest
case Conflict:
return http.StatusConflict
case Internal:
fallthrough
case Unknown:
fallthrough
default:
return http.StatusInternalServerError
}
}
func New(code Code, message string) error {
if message == "" {
message = code.String()
}
return &Error{Code: code, Message: message}
}
func HandlerError(err error, resp *restful.Response) bool {
if err == nil {
return false
}
glog.Errorln(reflect.TypeOf(err), err)
resp.WriteHeaderAndEntity(wrapper(err))
return true
}
func wrapper(err error) (int, interface{}) {
switch err.(type) {
case *Error:
case *json.UnmarshalTypeError:
err = New(InvalidArgument, err.Error())
case *mysql.MySQLError:
err = wrapperMysqlError(err.(*mysql.MySQLError))
case *net.OpError:
err = New(Internal, err.Error())
default:
if k8sError.IsNotFound(err) {
err = New(NotFound, err.Error())
} else {
err = New(Unknown, err.Error())
}
}
return err.(*Error).HttpStatusCode(), err
}
func wrapperMysqlError(sqlError *mysql.MySQLError) error {
switch sqlError.Number {
case 1062:
return New(AlreadyExists, sqlError.Message)
default:
return New(Unknown, sqlError.Message)
}
}
func Wrap(data []byte) error {
var j map[string]string
err := json.Unmarshal(data, &j)
if err != nil {
return errors.New(string(data))
} else if message := j["message"]; message != "" {
return errors.New(message)
} else if message := j["Error"]; message != "" {
return errors.New(message)
} else {
return errors.New(string(data))
}
}

41
pkg/filter/logging.go Normal file
View File

@@ -0,0 +1,41 @@
/*
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 filter
import (
"strings"
"time"
"github.com/emicklei/go-restful"
"github.com/golang/glog"
)
func Logging(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
start := time.Now()
chain.ProcessFilter(req, resp)
glog.Infof("%s - \"%s %s %s\" %d %d %dms",
strings.Split(req.Request.RemoteAddr, ":")[0],
req.Request.Method,
req.Request.RequestURI,
req.Request.Proto,
resp.StatusCode(),
resp.ContentLength(),
time.Since(start)/time.Millisecond,
)
}

View File

@@ -0,0 +1,42 @@
/*
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 informers
import (
"sync"
"time"
"k8s.io/client-go/informers"
"kubesphere.io/kubesphere/pkg/client"
)
const defaultResync = 600 * time.Second
var (
once sync.Once
informerFactory informers.SharedInformerFactory
)
func SharedInformerFactory() informers.SharedInformerFactory {
once.Do(func() {
k8sClient := client.K8sClient()
informerFactory = informers.NewSharedInformerFactory(k8sClient, defaultResync)
})
return informerFactory
}

View File

@@ -0,0 +1,204 @@
/*
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 components
import (
"time"
lister "k8s.io/client-go/listers/core/v1"
"kubesphere.io/kubesphere/pkg/informers"
"github.com/golang/glog"
coreV1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
"kubesphere.io/kubesphere/pkg/constants"
)
type Component struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
SelfLink string `json:"selfLink"`
Label interface{} `json:"label"`
StartedAt time.Time `json:"startedAt"`
TotalBackends int `json:"totalBackends"`
HealthyBackends int `json:"healthyBackends"`
}
var (
componentStatusLister lister.ComponentStatusLister
serviceLister lister.ServiceLister
podLister lister.PodLister
nodeLister lister.NodeLister
)
func init() {
componentStatusLister = informers.SharedInformerFactory().Core().V1().ComponentStatuses().Lister()
serviceLister = informers.SharedInformerFactory().Core().V1().Services().Lister()
podLister = informers.SharedInformerFactory().Core().V1().Pods().Lister()
nodeLister = informers.SharedInformerFactory().Core().V1().Nodes().Lister()
}
func GetComponentStatus(name string) (interface{}, error) {
var service *coreV1.Service
var err error
for _, ns := range constants.SystemNamespaces {
service, err = serviceLister.Services(ns).Get(name)
if err == nil {
break
}
}
if err != nil {
return nil, err
}
pods, err := podLister.Pods(service.Namespace).List(labels.SelectorFromValidatedSet(service.Spec.Selector))
if err != nil {
return nil, err
}
component := Component{
Name: service.Name,
Namespace: service.Namespace,
SelfLink: service.SelfLink,
Label: service.Spec.Selector,
StartedAt: service.CreationTimestamp.Time,
HealthyBackends: 0,
TotalBackends: 0,
}
for _, v := range pods {
component.TotalBackends++
component.HealthyBackends++
for _, c := range v.Status.ContainerStatuses {
if !c.Ready {
component.HealthyBackends--
break
}
}
}
return component, nil
}
func GetSystemHealthStatus() (map[string]interface{}, error) {
status := make(map[string]interface{})
componentStatuses, err := componentStatusLister.List(labels.Everything())
if err != nil {
return nil, err
}
for _, cs := range componentStatuses {
status[cs.Name] = cs.Conditions[0]
}
// get kubesphere-system components
systemComponentStatus, err := GetAllComponentsStatus()
if err != nil {
glog.Errorln(err)
}
for k, v := range systemComponentStatus {
status[k] = v
}
// get node status
nodes, err := nodeLister.List(labels.Everything())
if err != nil {
glog.Errorln(err)
return status, nil
}
nodeStatus := make(map[string]int)
totalNodes := 0
healthyNodes := 0
for _, nodes := range nodes {
totalNodes++
for _, condition := range nodes.Status.Conditions {
if condition.Type == coreV1.NodeReady && condition.Status == coreV1.ConditionTrue {
healthyNodes++
}
}
}
nodeStatus["totalNodes"] = totalNodes
nodeStatus["healthyNodes"] = healthyNodes
status["nodes"] = nodeStatus
return status, nil
}
func GetAllComponentsStatus() (map[string]interface{}, error) {
status := make(map[string]interface{})
var err error
for _, ns := range constants.SystemNamespaces {
nsStatus := make(map[string]interface{})
services, err := serviceLister.Services(ns).List(labels.Everything())
if err != nil {
glog.Error(err)
continue
}
for _, service := range services {
component := Component{
Name: service.Name,
Namespace: service.Namespace,
SelfLink: service.SelfLink,
Label: service.Spec.Selector,
StartedAt: service.CreationTimestamp.Time,
HealthyBackends: 0,
TotalBackends: 0,
}
pods, err := podLister.Pods(ns).List(labels.SelectorFromValidatedSet(service.Spec.Selector))
if err != nil {
glog.Errorln(err)
continue
}
for _, pod := range pods {
component.TotalBackends++
component.HealthyBackends++
for _, c := range pod.Status.ContainerStatuses {
if !c.Ready {
component.HealthyBackends--
break
}
}
}
nsStatus[service.Name] = component
}
if len(nsStatus) > 0 {
status[ns] = nsStatus
}
}
return status, err
}

29
pkg/models/hpa/hpa.go Normal file
View File

@@ -0,0 +1,29 @@
/*
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 hpa
import (
"k8s.io/api/autoscaling/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"kubesphere.io/kubesphere/pkg/client"
)
func GetHPA(namespace, hpa string) (*v1.HorizontalPodAutoscaler, error) {
return client.K8sClient().AutoscalingV1().HorizontalPodAutoscalers(namespace).Get(hpa, metaV1.GetOptions{})
}

461
pkg/models/iam/iam.go Normal file
View File

@@ -0,0 +1,461 @@
package iam
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
v12 "k8s.io/client-go/listers/rbac/v1"
"kubesphere.io/kubesphere/pkg/informers"
"github.com/golang/glog"
"k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/kubernetes/pkg/util/slice"
"kubesphere.io/kubesphere/pkg/constants"
ksErr "kubesphere.io/kubesphere/pkg/errors"
)
const ClusterRoleKind = "ClusterRole"
var (
clusterRoleBindingLister v12.ClusterRoleBindingLister
clusterRoleLister v12.ClusterRoleLister
roleBindingLister v12.RoleBindingLister
roleLister v12.RoleLister
)
func init() {
clusterRoleBindingLister = informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
clusterRoleLister = informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
roleBindingLister = informers.SharedInformerFactory().Rbac().V1().RoleBindings().Lister()
roleLister = informers.SharedInformerFactory().Rbac().V1().Roles().Lister()
}
// Get user list based on workspace role
func WorkspaceRoleUsers(workspace string, roleName string) ([]User, error) {
workspaceRoleBinding, err := clusterRoleBindingLister.Get(fmt.Sprintf("system:%s:%s", workspace, roleName))
if err != nil {
return nil, err
}
names := make([]string, 0)
for _, subject := range workspaceRoleBinding.Subjects {
if subject.Kind == v1.UserKind {
names = append(names, subject.Name)
}
}
users, err := GetUsers(names)
if err != nil {
return nil, err
}
for i := 0; i < len(users); i++ {
users[i].WorkspaceRole = roleName
}
return users, nil
}
func GetUsers(names []string) ([]User, error) {
var users []User
if names == nil || len(names) == 0 {
return make([]User, 0), nil
}
result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/users?name=%s", constants.AccountAPIServer, strings.Join(names, ",")))
if err != nil {
return nil, err
}
defer result.Body.Close()
data, err := ioutil.ReadAll(result.Body)
if err != nil {
return nil, err
}
if result.StatusCode > 200 {
return nil, ksErr.Wrap(data)
}
err = json.Unmarshal(data, &users)
if err != nil {
return nil, err
}
return users, nil
}
func GetUser(name string) (*User, error) {
result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/users/%s", constants.AccountAPIServer, name))
if err != nil {
return nil, err
}
defer result.Body.Close()
data, err := ioutil.ReadAll(result.Body)
if err != nil {
return nil, err
}
if result.StatusCode > 200 {
return nil, ksErr.Wrap(data)
}
var user User
err = json.Unmarshal(data, &user)
if err != nil {
return nil, err
}
return &user, nil
}
func GetUserNamespaces(username string, requiredRule v1.PolicyRule) (allNamespace bool, namespaces []string, err error) {
clusterRoles, err := GetClusterRoles(username)
if err != nil {
return false, nil, err
}
clusterRules := make([]v1.PolicyRule, 0)
for _, role := range clusterRoles {
clusterRules = append(clusterRules, role.Rules...)
}
if requiredRule.Size() == 0 {
if RulesMatchesRequired(clusterRules, v1.PolicyRule{
Verbs: []string{"get"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/namespaces"},
}) {
return true, nil, nil
}
} else {
if RulesMatchesRequired(clusterRules, requiredRule) {
return true, nil, nil
}
}
roles, err := GetRoles("", username)
if err != nil {
return false, nil, err
}
rulesMapping := make(map[string][]v1.PolicyRule, 0)
for _, role := range roles {
rules := rulesMapping[role.Namespace]
if rules == nil {
rules = make([]v1.PolicyRule, 0)
}
rules = append(rules, role.Rules...)
rulesMapping[role.Namespace] = rules
}
namespaces = make([]string, 0)
for namespace, rules := range rulesMapping {
if requiredRule.Size() == 0 || RulesMatchesRequired(rules, requiredRule) {
namespaces = append(namespaces, namespace)
}
}
return false, namespaces, nil
}
func GetRole(namespace string, name string) (*v1.Role, error) {
role, err := roleLister.Roles(namespace).Get(name)
if err != nil {
return nil, err
}
return role.DeepCopy(), nil
}
func GetWorkspaceUsers(workspace string, workspaceRole string) ([]string, error) {
clusterRoleBinding, err := clusterRoleBindingLister.Get(fmt.Sprintf("system:%s:%s", workspace, workspaceRole))
if err != nil {
return nil, err
}
users := make([]string, 0)
for _, s := range clusterRoleBinding.Subjects {
if s.Kind == v1.UserKind && !slice.ContainsString(users, s.Name, nil) {
users = append(users, s.Name)
}
}
return users, nil
}
func GetClusterRole(name string) (*v1.ClusterRole, error) {
role, err := clusterRoleLister.Get(name)
if err != nil {
return nil, err
}
return role.DeepCopy(), nil
}
func GetRoles(namespace string, username string) ([]v1.Role, error) {
roleBindings, err := roleBindingLister.RoleBindings(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
roles := make([]v1.Role, 0)
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && subject.Name == username {
if roleBinding.RoleRef.Kind == ClusterRoleKind {
clusterRole, err := clusterRoleLister.Get(roleBinding.RoleRef.Name)
if err == nil {
var role = v1.Role{TypeMeta: (*clusterRole).TypeMeta, ObjectMeta: (*clusterRole).ObjectMeta, Rules: (*clusterRole).Rules}
role.Namespace = roleBinding.Namespace
roles = append(roles, role)
break
} else if apierrors.IsNotFound(err) {
glog.Infoln(err.Error())
break
} else {
return nil, err
}
} else {
if subject.Kind == v1.UserKind && subject.Name == username {
role, err := roleLister.Roles(roleBinding.Namespace).Get(roleBinding.RoleRef.Name)
if err == nil {
roles = append(roles, *role)
break
} else if apierrors.IsNotFound(err) {
glog.Infoln(err.Error())
break
} else {
return nil, err
}
}
}
}
}
}
return roles, nil
}
// Get cluster roles by username
func GetClusterRoles(username string) ([]v1.ClusterRole, error) {
clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything())
if err != nil {
return nil, err
}
roles := make([]v1.ClusterRole, 0)
for _, roleBinding := range clusterRoleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && subject.Name == username {
if roleBinding.RoleRef.Kind == ClusterRoleKind {
role, err := clusterRoleLister.Get(roleBinding.RoleRef.Name)
if err == nil {
role = role.DeepCopy()
if role.Annotations == nil {
role.Annotations = make(map[string]string, 0)
}
role.Annotations["rbac.authorization.k8s.io/clusterrolebinding"] = roleBinding.Name
if roleBinding.Annotations != nil &&
roleBinding.Annotations["rbac.authorization.k8s.io/clusterrole"] == roleBinding.RoleRef.Name {
role.Annotations["rbac.authorization.k8s.io/clusterrole"] = "true"
}
roles = append(roles, *role)
break
} else if apierrors.IsNotFound(err) {
glog.Warning(err)
break
} else {
return nil, err
}
}
}
}
}
return roles, nil
}
func RulesMatchesRequired(rules []v1.PolicyRule, required v1.PolicyRule) bool {
for _, rule := range rules {
if ruleMatchesRequired(rule, required) {
return true
}
}
return false
}
func ruleMatchesRequired(rule v1.PolicyRule, required v1.PolicyRule) bool {
if len(required.NonResourceURLs) == 0 {
for _, apiGroup := range required.APIGroups {
for _, resource := range required.Resources {
resources := strings.Split(resource, "/")
resource = resources[0]
var subsource string
if len(resources) > 1 {
subsource = resources[1]
}
if len(required.ResourceNames) == 0 {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, "", verb) {
return false
}
}
} else {
for _, resourceName := range required.ResourceNames {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, resourceName, verb) {
return false
}
}
}
}
}
}
} else {
for _, apiGroup := range required.APIGroups {
for _, nonResourceURL := range required.NonResourceURLs {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, nonResourceURL, "", "", "", verb) {
return false
}
}
}
}
}
return true
}
func ruleMatchesResources(rule v1.PolicyRule, apiGroup string, resource string, subresource string, resourceName string) bool {
if resource == "" {
return false
}
if !hasString(rule.APIGroups, apiGroup) && !hasString(rule.APIGroups, v1.ResourceAll) {
return false
}
if len(rule.ResourceNames) > 0 && !hasString(rule.ResourceNames, resourceName) {
return false
}
combinedResource := resource
if subresource != "" {
combinedResource = combinedResource + "/" + subresource
}
for _, res := range rule.Resources {
// match "*"
if res == v1.ResourceAll || res == combinedResource {
return true
}
// match "*/subresource"
if len(subresource) > 0 && strings.HasPrefix(res, "*/") && subresource == strings.TrimLeft(res, "*/") {
return true
}
// match "resource/*"
if strings.HasSuffix(res, "/*") && resource == strings.TrimRight(res, "/*") {
return true
}
}
return false
}
func ruleMatchesRequest(rule v1.PolicyRule, apiGroup string, nonResourceURL string, resource string, subresource string, resourceName string, verb string) bool {
if !hasString(rule.Verbs, verb) && !hasString(rule.Verbs, v1.VerbAll) {
return false
}
if nonResourceURL == "" {
return ruleMatchesResources(rule, apiGroup, resource, subresource, resourceName)
} else {
return ruleMatchesNonResource(rule, nonResourceURL)
}
}
func ruleMatchesNonResource(rule v1.PolicyRule, nonResourceURL string) bool {
if nonResourceURL == "" {
return false
}
for _, spec := range rule.NonResourceURLs {
if pathMatches(nonResourceURL, spec) {
return true
}
}
return false
}
func pathMatches(path, spec string) bool {
// Allow wildcard match
if spec == "*" {
return true
}
// Allow exact match
if spec == path {
return true
}
// Allow a trailing * subpath match
if strings.HasSuffix(spec, "*") && strings.HasPrefix(path, strings.TrimRight(spec, "*")) {
return true
}
return false
}
func hasString(slice []string, value string) bool {
for _, s := range slice {
if s == value {
return true
}
}
return false
}

39
pkg/models/iam/types.go Normal file
View File

@@ -0,0 +1,39 @@
package iam
import (
"k8s.io/api/rbac/v1"
)
type Action struct {
Name string `json:"name"`
Rules []v1.PolicyRule `json:"rules"`
}
type Rule struct {
Name string `json:"name"`
Actions []Action `json:"actions"`
}
type SimpleRule struct {
Name string `json:"name"`
Actions []string `json:"actions"`
}
type User struct {
Username string `json:"username"`
Groups []string `json:"groups"`
Password string `json:"password,omitempty"`
AvatarUrl string `json:"avatar_url"`
Description string `json:"description"`
Email string `json:"email"`
LastLoginTime string `json:"last_login_time"`
Status int `json:"status"`
ClusterRole string `json:"cluster_role"`
ClusterRules []SimpleRule `json:"cluster_rules,omitempty"`
Roles map[string]string `json:"roles,omitempty"`
Rules map[string][]SimpleRule `json:"rules,omitempty"`
Role string `json:"role,omitempty"`
WorkspaceRoles map[string]string `json:"workspace_roles,omitempty"`
WorkspaceRole string `json:"workspace_role,omitempty"`
WorkspaceRules map[string][]SimpleRule `json:"workspace_rules,omitempty"`
}

View File

@@ -0,0 +1,294 @@
/*
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 kubeconfig
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/pem"
"io/ioutil"
"math/big"
rd "math/rand"
"time"
"github.com/golang/glog"
"gopkg.in/yaml.v2"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/api/core/v1"
"kubesphere.io/kubesphere/pkg/client"
"kubesphere.io/kubesphere/pkg/constants"
)
const (
caPath = "/etc/kubernetes/pki/ca.crt"
keyPath = "/etc/kubernetes/pki/ca.key"
clusterName = "kubernetes"
kubectlConfigKey = "config"
defaultNamespace = "default"
)
type clusterInfo struct {
CertificateAuthorityData string `yaml:"certificate-authority-data"`
Server string `yaml:"server"`
}
type cluster struct {
Cluster clusterInfo `yaml:"cluster"`
Name string `yaml:"name"`
}
type contextInfo struct {
Cluster string `yaml:"cluster"`
User string `yaml:"user"`
NameSpace string `yaml:"namespace"`
}
type contextObject struct {
Context contextInfo `yaml:"context"`
Name string `yaml:"name"`
}
type userInfo struct {
CaData string `yaml:"client-certificate-data"`
KeyData string `yaml:"client-key-data"`
}
type user struct {
Name string `yaml:"name"`
User userInfo `yaml:"user"`
}
type kubeConfig struct {
ApiVersion string `yaml:"apiVersion"`
Clusters []cluster `yaml:"clusters"`
Contexts []contextObject `yaml:"contexts"`
CurrentContext string `yaml:"current-context"`
Kind string `yaml:"kind"`
Preferences map[string]string `yaml:"preferences"`
Users []user `yaml:"users"`
}
type CertInformation struct {
Country []string
Organization []string
OrganizationalUnit []string
EmailAddress []string
Province []string
Locality []string
CommonName string
CrtName, KeyName string
IsCA bool
Names []pkix.AttributeTypeAndValue
}
func createCRT(RootCa *x509.Certificate, RootKey *rsa.PrivateKey, info CertInformation) ([]byte, []byte, error) {
var cert, key bytes.Buffer
Crt := newCertificate(info)
Key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
glog.Error(err)
return nil, nil, err
}
var buf []byte
buf, err = x509.CreateCertificate(rand.Reader, Crt, RootCa, &Key.PublicKey, RootKey)
if err != nil {
glog.Error(err)
return nil, nil, err
}
pem.Encode(&cert, &pem.Block{Type: "CERTIFICATE", Bytes: buf})
if err != nil {
glog.Error(err)
return nil, nil, err
}
buf = x509.MarshalPKCS1PrivateKey(Key)
pem.Encode(&key, &pem.Block{Type: "PRIVATE KEY", Bytes: buf})
return cert.Bytes(), key.Bytes(), nil
}
func Parse(crtPath, keyPath string) (rootcertificate *x509.Certificate, rootPrivateKey *rsa.PrivateKey, err error) {
rootcertificate, err = parseCrt(crtPath)
if err != nil {
glog.Error(err)
return nil, nil, err
}
rootPrivateKey, err = parseKey(keyPath)
return rootcertificate, rootPrivateKey, nil
}
func parseCrt(path string) (*x509.Certificate, error) {
buf, err := ioutil.ReadFile(path)
if err != nil {
glog.Error(err)
return nil, err
}
p := &pem.Block{}
p, buf = pem.Decode(buf)
return x509.ParseCertificate(p.Bytes)
}
func parseKey(path string) (*rsa.PrivateKey, error) {
buf, err := ioutil.ReadFile(path)
if err != nil {
glog.Error(err)
return nil, err
}
p, buf := pem.Decode(buf)
return x509.ParsePKCS1PrivateKey(p.Bytes)
}
func newCertificate(info CertInformation) *x509.Certificate {
rd.Seed(time.Now().UnixNano())
return &x509.Certificate{
SerialNumber: big.NewInt(rd.Int63()),
Subject: pkix.Name{
Country: info.Country,
Organization: info.Organization,
OrganizationalUnit: info.OrganizationalUnit,
Province: info.Province,
CommonName: info.CommonName,
Locality: info.Locality,
ExtraNames: info.Names,
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(20, 0, 0),
BasicConstraintsValid: true,
IsCA: info.IsCA,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
EmailAddresses: info.EmailAddress,
}
}
func generateCaAndKey(user, caPath, keyPath string) (string, string, error) {
crtInfo := CertInformation{CommonName: user, IsCA: false}
crt, pri, err := Parse(caPath, keyPath)
if err != nil {
glog.Error(err)
return "", "", err
}
cert, key, err := createCRT(crt, pri, crtInfo)
if err != nil {
glog.Error(err)
return "", "", err
}
base64Cert := base64.StdEncoding.EncodeToString(cert)
base64Key := base64.StdEncoding.EncodeToString(key)
return base64Cert, base64Key, nil
}
func createKubeConfig(userName string) (string, error) {
tmpKubeConfig := kubeConfig{ApiVersion: "v1", Kind: "Config"}
serverCa, err := ioutil.ReadFile(caPath)
if err != nil {
glog.Errorln(err)
return "", err
}
base64ServerCa := base64.StdEncoding.EncodeToString(serverCa)
tmpClusterInfo := clusterInfo{CertificateAuthorityData: base64ServerCa, Server: client.KubeConfig.Host}
tmpCluster := cluster{Cluster: tmpClusterInfo, Name: clusterName}
tmpKubeConfig.Clusters = append(tmpKubeConfig.Clusters, tmpCluster)
contextName := userName + "@" + clusterName
tmpContext := contextObject{Context: contextInfo{User: userName, Cluster: clusterName, NameSpace: defaultNamespace}, Name: contextName}
tmpKubeConfig.Contexts = append(tmpKubeConfig.Contexts, tmpContext)
cert, key, err := generateCaAndKey(userName, caPath, keyPath)
if err != nil {
return "", err
}
tmpUser := user{User: userInfo{CaData: cert, KeyData: key}, Name: userName}
tmpKubeConfig.Users = append(tmpKubeConfig.Users, tmpUser)
tmpKubeConfig.CurrentContext = contextName
config, err := yaml.Marshal(tmpKubeConfig)
if err != nil {
return "", err
}
return string(config), nil
}
func CreateKubeConfig(user string) error {
k8sClient := client.K8sClient()
_, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(user, metaV1.GetOptions{})
if errors.IsNotFound(err) {
config, err := createKubeConfig(user)
if err != nil {
glog.Errorln(err)
return err
}
data := map[string]string{"config": string(config)}
configMap := v1.ConfigMap{TypeMeta: metaV1.TypeMeta{Kind: "Configmap", APIVersion: "v1"}, ObjectMeta: metaV1.ObjectMeta{Name: user}, Data: data}
_, err = k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Create(&configMap)
if err != nil && !errors.IsAlreadyExists(err) {
glog.Errorf("create user %s's kubeConfig failed, reason: %s", user, err)
return err
}
}
return nil
}
func GetKubeConfig(user string) (string, error) {
k8sClient := client.K8sClient()
configMap, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(user, metaV1.GetOptions{})
if err != nil {
glog.Errorf("cannot get user %s's kubeConfig, reason: %s", user, err)
return "", err
}
return configMap.Data[kubectlConfigKey], nil
}
func DelKubeConfig(user string) error {
k8sClient := client.K8sClient()
_, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(user, metaV1.GetOptions{})
if errors.IsNotFound(err) {
return nil
}
deletePolicy := metaV1.DeletePropagationBackground
err = k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Delete(user, &metaV1.DeleteOptions{PropagationPolicy: &deletePolicy})
if err != nil {
glog.Errorf("delete user %s's kubeConfig failed, reason: %s", user, err)
return err
}
return nil
}

View File

@@ -0,0 +1,156 @@
/*
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 kubectl
import (
"fmt"
"math/rand"
"github.com/golang/glog"
"k8s.io/api/apps/v1beta2"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/api/errors"
"kubesphere.io/kubesphere/pkg/client"
"kubesphere.io/kubesphere/pkg/constants"
)
const (
namespace = constants.KubeSphereControlNamespace
)
type PodInfo struct {
Namespace string `json:"namespace"`
Pod string `json:"pod"`
Container string `json:"container"`
}
func GetKubectlPod(username string) (PodInfo, error) {
k8sClient := client.K8sClient()
deploy, err := k8sClient.AppsV1beta2().Deployments(namespace).Get(username, metav1.GetOptions{})
if err != nil {
glog.Errorln(err)
return PodInfo{}, err
}
selectors := deploy.Spec.Selector.MatchLabels
labelSelector := labels.Set(selectors).AsSelector().String()
podList, err := k8sClient.CoreV1().Pods(namespace).List(metav1.ListOptions{LabelSelector: labelSelector})
if err != nil {
glog.Errorln(err)
return PodInfo{}, err
}
pod, err := selectCorrectPod(namespace, podList.Items)
if err != nil {
glog.Errorln(err)
return PodInfo{}, err
}
info := PodInfo{Namespace: pod.Namespace, Pod: pod.Name, Container: pod.Status.ContainerStatuses[0].Name}
return info, nil
}
func selectCorrectPod(namespace string, pods []v1.Pod) (kubectlPod v1.Pod, err error) {
var kubectlPodList []v1.Pod
for _, pod := range pods {
for _, condition := range pod.Status.Conditions {
if condition.Type == "Ready" && condition.Status == "True" {
kubectlPodList = append(kubectlPodList, pod)
}
}
}
if len(kubectlPodList) < 1 {
err = fmt.Errorf("cannot find valid kubectl pod in namespace:%s", namespace)
return v1.Pod{}, err
}
random := rand.Intn(len(kubectlPodList))
return kubectlPodList[random], nil
}
func CreateKubectlDeploy(user string) error {
k8sClient := client.K8sClient()
_, err := k8sClient.AppsV1().Deployments(namespace).Get(user, metav1.GetOptions{})
if err == nil {
return nil
}
replica := int32(1)
selector := metav1.LabelSelector{MatchLabels: map[string]string{"user": user}}
config := v1.ConfigMapVolumeSource{Items: []v1.KeyToPath{{Key: "config", Path: "config"}}, LocalObjectReference: v1.LocalObjectReference{Name: user}}
deployment := v1beta2.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: user,
},
Spec: v1beta2.DeploymentSpec{
Replicas: &replica,
Selector: &selector,
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"user": user,
},
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{Name: "kubectl",
Image: "",
VolumeMounts: []v1.VolumeMount{{Name: "kubeconfig", MountPath: "/root/.kube"}},
},
},
Volumes: []v1.Volume{{Name: "kubeconfig", VolumeSource: v1.VolumeSource{ConfigMap: &config}}},
},
},
},
}
_, err = k8sClient.AppsV1beta2().Deployments(namespace).Create(&deployment)
return err
}
func DelKubectlDeploy(user string) error {
k8sClient := client.K8sClient()
_, err := k8sClient.AppsV1beta2().Deployments(namespace).Get(user, metav1.GetOptions{})
if errors.IsNotFound(err) {
return nil
}
if err != nil {
err := fmt.Errorf("delete user %s failed, reason:%v", user, err)
return err
}
deletePolicy := metav1.DeletePropagationBackground
err = k8sClient.AppsV1beta2().Deployments(namespace).Delete(user, &metav1.DeleteOptions{PropagationPolicy: &deletePolicy})
if err != nil {
err := fmt.Errorf("delete user %s failed, reason:%v", user, err)
return err
}
return nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,200 @@
/*
Copyright 2018 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 metrics
import (
"strings"
)
func MakeWorkloadPromQL(metricName, nsName, wlFilter string) string {
if wlFilter == "" {
wlFilter = ".*"
}
var promql = RulePromQLTmplMap[metricName]
promql = strings.Replace(promql, "$2", nsName, -1)
promql = strings.Replace(promql, "$3", wlFilter, -1)
return promql
}
func MakeSpecificWorkloadRule(wkKind, wkName, namespace string) string {
var rule = PodInfoRule
if namespace == "" {
namespace = ".*"
}
// alertnatives values: Deployment StatefulSet ReplicaSet DaemonSet
wkKind = strings.ToLower(wkKind)
switch wkKind {
case "deployment":
wkKind = ReplicaSet
if wkName != "" {
wkName = "~\"^" + wkName + `-(\\w)+$"`
} else {
wkName = "~\".*\""
}
rule = strings.Replace(rule, "$1", wkKind, -1)
rule = strings.Replace(rule, "$2", wkName, -1)
rule = strings.Replace(rule, "$3", namespace, -1)
return rule
case "replicaset":
wkKind = ReplicaSet
case "statefulset":
wkKind = StatefulSet
case "daemonset":
wkKind = DaemonSet
}
if wkName == "" {
wkName = "~\".*\""
} else {
wkName = "\"" + wkName + "\""
}
rule = strings.Replace(rule, "$1", wkKind, -1)
rule = strings.Replace(rule, "$2", wkName, -1)
rule = strings.Replace(rule, "$3", namespace, -1)
return rule
}
func MakeAllWorkspacesPromQL(metricsName, nsFilter string) string {
var promql = RulePromQLTmplMap[metricsName]
nsFilter = "!~\"" + nsFilter + "\""
promql = strings.Replace(promql, "$1", nsFilter, -1)
return promql
}
func MakeSpecificWorkspacePromQL(metricsName, nsFilter string) string {
var promql = RulePromQLTmplMap[metricsName]
nsFilter = "=~\"" + nsFilter + "\""
promql = strings.Replace(promql, "$1", nsFilter, -1)
return promql
}
func MakeContainerPromQL(nsName, nodeId, podName, containerName, metricName, containerFilter string) string {
var promql string
if nsName != "" {
// get container metrics from namespace-pod
promql = RulePromQLTmplMap[metricName]
promql = strings.Replace(promql, "$1", nsName, -1)
} else {
// get container metrics from node-pod
promql = RulePromQLTmplMap[metricName+"_node"]
promql = strings.Replace(promql, "$1", nodeId, -1)
}
promql = strings.Replace(promql, "$2", podName, -1)
if containerName == "" {
if containerFilter == "" {
containerFilter = ".*"
}
promql = strings.Replace(promql, "$3", containerFilter, -1)
} else {
promql = strings.Replace(promql, "$3", containerName, -1)
}
return promql
}
func MakePodPromQL(metricName, nsName, nodeID, podName, podFilter string) string {
if podFilter == "" {
podFilter = ".*"
}
var promql = ""
if nsName != "" {
// get pod metrics by namespace
if podName != "" {
// specific pod
promql = RulePromQLTmplMap[metricName]
promql = strings.Replace(promql, "$1", nsName, -1)
promql = strings.Replace(promql, "$2", podName, -1)
} else {
// all pods
metricName += "_all"
promql = RulePromQLTmplMap[metricName]
promql = strings.Replace(promql, "$1", nsName, -1)
promql = strings.Replace(promql, "$2", podFilter, -1)
}
} else if nodeID != "" {
// get pod metrics by nodeid
metricName += "_node"
promql = RulePromQLTmplMap[metricName]
promql = strings.Replace(promql, "$3", nodeID, -1)
if podName != "" {
// specific pod
promql = strings.Replace(promql, "$2", podName, -1)
} else {
promql = strings.Replace(promql, "$2", podFilter, -1)
}
}
return promql
}
func MakeNamespacePromQL(nsName string, nsFilter string, metricsName string) string {
var recordingRule = RulePromQLTmplMap[metricsName]
if nsName != "" {
nsFilter = nsName
} else {
if nsFilter == "" {
nsFilter = ".*"
}
}
recordingRule = strings.Replace(recordingRule, "$1", nsFilter, -1)
return recordingRule
}
// cluster rule
func MakeClusterRule(metricsName string) string {
var rule = RulePromQLTmplMap[metricsName]
return rule
}
// node rule
func MakeNodeRule(nodeID string, nodesFilter string, metricsName string) string {
var rule = RulePromQLTmplMap[metricsName]
if nodesFilter == "" {
nodesFilter = ".*"
}
if strings.Contains(metricsName, "disk_size") || strings.Contains(metricsName, "pod") || strings.Contains(metricsName, "usage") || strings.Contains(metricsName, "inode") || strings.Contains(metricsName, "load") {
// disk size promql
if nodeID != "" {
nodesFilter = "{" + "node" + "=" + "\"" + nodeID + "\"" + "}"
} else {
nodesFilter = "{" + "node" + "=~" + "\"" + nodesFilter + "\"" + "}"
}
rule = strings.Replace(rule, "$1", nodesFilter, -1)
} else {
// cpu, memory, network, disk_iops rules
if nodeID != "" {
// specific node
rule = rule + "{" + "node" + "=" + "\"" + nodeID + "\"" + "}"
} else {
// all nodes or specific nodes filted with re2 syntax
rule = rule + "{" + "node" + "=~" + "\"" + nodesFilter + "\"" + "}"
}
}
return rule
}

View File

@@ -0,0 +1,579 @@
/*
Copyright 2018 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 metrics
const (
ResultTypeVector = "vector"
ResultTypeMatrix = "matrix"
MetricStatus = "status"
MetricStatusError = "error"
MetricStatusSuccess = "success"
ResultItemMetric = "metric"
ResultItemMetricResource = "resource"
ResultItemValue = "value"
ResultItemValues = "values"
ResultSortTypeDesc = "desc"
ResultSortTypeAsc = "asc"
)
const (
MetricNameWorkloadCount = "workload_count"
MetricNameNamespacePodCount = "namespace_pod_count"
MetricNameWorkspaceAllOrganizationCount = "workspace_all_organization_count"
MetricNameWorkspaceAllAccountCount = "workspace_all_account_count"
MetricNameWorkspaceAllProjectCount = "workspace_all_project_count"
MetricNameWorkspaceAllDevopsCount = "workspace_all_devops_project_count"
MetricNameClusterAllProjectCount = "cluster_namespace_count"
MetricNameWorkspaceNamespaceCount = "workspace_namespace_count"
MetricNameWorkspaceDevopsCount = "workspace_devops_project_count"
MetricNameWorkspaceMemberCount = "workspace_member_count"
MetricNameWorkspaceRoleCount = "workspace_role_count"
MetricNameComponentOnLine = "component_online_count"
MetricNameComponentLine = "component_count"
)
const (
WorkspaceResourceKindOrganization = "organization"
WorkspaceResourceKindAccount = "account"
WorkspaceResourceKindNamespace = "namespace"
WorkspaceResourceKindDevops = "devops"
WorkspaceResourceKindMember = "member"
WorkspaceResourceKindRole = "role"
)
const (
MetricLevelCluster = "cluster"
MetricLevelClusterWorkspace = "cluster_workspace"
MetricLevelNode = "node"
MetricLevelWorkspace = "workspace"
MetricLevelNamespace = "namespace"
MetricLevelPod = "pod"
MetricLevelPodName = "pod_name"
MetricLevelContainer = "container"
MetricLevelContainerName = "container_name"
MetricLevelWorkload = "workload"
)
const (
ReplicaSet = "ReplicaSet"
StatefulSet = "StatefulSet"
DaemonSet = "DaemonSet"
Deployment = "Deployment"
)
const (
NodeStatusRule = `kube_node_status_condition{condition="Ready"} > 0`
PodInfoRule = `kube_pod_info{created_by_kind="$1",created_by_name=$2,namespace="$3"}`
NamespaceLabelRule = `kube_namespace_labels`
WorkloadReplicaSetOwnerRule = `kube_pod_owner{namespace="$1", owner_name!="<none>", owner_kind="ReplicaSet"}`
WorkspaceNamespaceLabelRule = `sum(kube_namespace_labels{label_kubesphere_io_workspace != ""}) by (label_kubesphere_io_workspace)`
ExcludedVirtualNetworkInterfaces = `interface!~"^(cali.+|tunl.+|dummy.+|kube.+|flannel.+|cni.+|docker.+|veth.+|lo.*)"`
)
const (
WorkspaceJoinedKey = "label_kubesphere_io_workspace"
)
type MetricMap map[string]string
var ClusterMetricsNames = []string{
"cluster_cpu_utilisation",
"cluster_cpu_usage",
"cluster_cpu_total",
"cluster_memory_utilisation",
"cluster_memory_available",
"cluster_memory_total",
"cluster_memory_usage_wo_cache",
"cluster_net_utilisation",
"cluster_net_bytes_transmitted",
"cluster_net_bytes_received",
"cluster_disk_read_iops",
"cluster_disk_write_iops",
"cluster_disk_read_throughput",
"cluster_disk_write_throughput",
"cluster_disk_size_usage",
"cluster_disk_size_utilisation",
"cluster_disk_size_capacity",
"cluster_disk_size_available",
"cluster_disk_inode_total",
"cluster_disk_inode_usage",
"cluster_disk_inode_utilisation",
"cluster_node_online",
"cluster_node_offline",
"cluster_node_total",
"cluster_pod_count",
"cluster_pod_quota",
"cluster_pod_utilisation",
"cluster_pod_running_count",
"cluster_pod_succeeded_count",
"cluster_pod_abnormal_count",
"cluster_ingresses_extensions_count",
"cluster_cronjob_count",
"cluster_pvc_count",
"cluster_daemonset_count",
"cluster_deployment_count",
"cluster_endpoint_count",
"cluster_hpa_count",
"cluster_job_count",
"cluster_statefulset_count",
"cluster_replicaset_count",
"cluster_service_count",
"cluster_secret_count",
"cluster_namespace_count",
"cluster_load1",
"cluster_load5",
"cluster_load15",
}
var NodeMetricsNames = []string{
"node_cpu_utilisation",
"node_cpu_total",
"node_cpu_usage",
"node_memory_utilisation",
"node_memory_usage_wo_cache",
"node_memory_available",
"node_memory_total",
"node_net_utilisation",
"node_net_bytes_transmitted",
"node_net_bytes_received",
"node_disk_read_iops",
"node_disk_write_iops",
"node_disk_read_throughput",
"node_disk_write_throughput",
"node_disk_size_capacity",
"node_disk_size_available",
"node_disk_size_usage",
"node_disk_size_utilisation",
"node_disk_inode_total",
"node_disk_inode_usage",
"node_disk_inode_utilisation",
"node_pod_count",
"node_pod_quota",
"node_pod_utilisation",
"node_pod_running_count",
"node_pod_succeeded_count",
"node_pod_abnormal_count",
"node_load1",
"node_load5",
"node_load15",
}
var WorkspaceMetricsNames = []string{
"workspace_cpu_usage",
"workspace_memory_usage",
"workspace_memory_usage_wo_cache",
"workspace_net_bytes_transmitted",
"workspace_net_bytes_received",
"workspace_pod_count",
"workspace_pod_running_count",
"workspace_pod_succeeded_count",
"workspace_pod_abnormal_count",
"workspace_ingresses_extensions_count",
"workspace_cronjob_count",
"workspace_pvc_count",
"workspace_daemonset_count",
"workspace_deployment_count",
"workspace_endpoint_count",
"workspace_hpa_count",
"workspace_job_count",
"workspace_statefulset_count",
"workspace_replicaset_count",
"workspace_service_count",
"workspace_secret_count",
"workspace_all_project_count",
}
var NamespaceMetricsNames = []string{
"namespace_cpu_usage",
"namespace_memory_usage",
"namespace_memory_usage_wo_cache",
"namespace_net_bytes_transmitted",
"namespace_net_bytes_received",
"namespace_pod_count",
"namespace_pod_running_count",
"namespace_pod_succeeded_count",
"namespace_pod_abnormal_count",
"namespace_configmap_count_used",
"namespace_jobs_batch_count_used",
"namespace_roles_count_used",
"namespace_memory_limit_used",
"namespace_pvc_used",
"namespace_memory_request_used",
"namespace_pvc_count_used",
"namespace_cronjobs_batch_count_used",
"namespace_ingresses_extensions_count_used",
"namespace_cpu_limit_used",
"namespace_storage_request_used",
"namespace_deployment_count_used",
"namespace_pod_count_used",
"namespace_statefulset_count_used",
"namespace_daemonset_count_used",
"namespace_secret_count_used",
"namespace_service_count_used",
"namespace_cpu_request_used",
"namespace_service_loadbalancer_used",
"namespace_configmap_count_hard",
"namespace_jobs_batch_count_hard",
"namespace_roles_count_hard",
"namespace_memory_limit_hard",
"namespace_pvc_hard",
"namespace_memory_request_hard",
"namespace_pvc_count_hard",
"namespace_cronjobs_batch_count_hard",
"namespace_ingresses_extensions_count_hard",
"namespace_cpu_limit_hard",
"namespace_storage_request_hard",
"namespace_deployment_count_hard",
"namespace_pod_count_hard",
"namespace_statefulset_count_hard",
"namespace_daemonset_count_hard",
"namespace_secret_count_hard",
"namespace_service_count_hard",
"namespace_cpu_request_hard",
"namespace_service_loadbalancer_hard",
"namespace_cronjob_count",
"namespace_pvc_count",
"namespace_daemonset_count",
"namespace_deployment_count",
"namespace_endpoint_count",
"namespace_hpa_count",
"namespace_job_count",
"namespace_statefulset_count",
"namespace_replicaset_count",
"namespace_service_count",
"namespace_secret_count",
"namespace_ingresses_extensions_count",
}
var PodMetricsNames = []string{
"pod_cpu_usage",
"pod_memory_usage",
"pod_memory_usage_wo_cache",
"pod_net_bytes_transmitted",
"pod_net_bytes_received",
}
var WorkloadMetricsNames = []string{
"workload_pod_cpu_usage",
"workload_pod_memory_usage",
"workload_pod_memory_usage_wo_cache",
"workload_pod_net_bytes_transmitted",
"workload_pod_net_bytes_received",
"workload_deployment_replica",
"workload_deployment_replica_available",
"workload_statefulset_replica",
"workload_statefulset_replica_available",
"workload_daemonset_replica",
"workload_daemonset_replica_available",
}
var ContainerMetricsNames = []string{
"container_cpu_usage",
"container_memory_usage",
"container_memory_usage_wo_cache",
//"container_net_bytes_transmitted",
//"container_net_bytes_received",
}
var RulePromQLTmplMap = MetricMap{
//cluster
"cluster_cpu_utilisation": ":node_cpu_utilisation:avg1m",
"cluster_cpu_usage": `:node_cpu_utilisation:avg1m * sum(node:node_num_cpu:sum)`,
"cluster_cpu_total": "sum(node:node_num_cpu:sum)",
"cluster_memory_utilisation": ":node_memory_utilisation:",
"cluster_memory_available": "sum(node:node_memory_bytes_available:sum)",
"cluster_memory_total": "sum(node:node_memory_bytes_total:sum)",
"cluster_memory_usage_wo_cache": "sum(node:node_memory_bytes_total:sum) - sum(node:node_memory_bytes_available:sum)",
"cluster_net_utilisation": ":node_net_utilisation:sum_irate",
"cluster_net_bytes_transmitted": "sum(node:node_net_bytes_transmitted:sum_irate)",
"cluster_net_bytes_received": "sum(node:node_net_bytes_received:sum_irate)",
"cluster_disk_read_iops": "sum(node:data_volume_iops_reads:sum)",
"cluster_disk_write_iops": "sum(node:data_volume_iops_writes:sum)",
"cluster_disk_read_throughput": "sum(node:data_volume_throughput_bytes_read:sum)",
"cluster_disk_write_throughput": "sum(node:data_volume_throughput_bytes_written:sum)",
"cluster_disk_size_usage": `sum(max((node_filesystem_size{device=~"/dev/.+", job="node-exporter"} - node_filesystem_avail{device=~"/dev/.+", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:) by (node))`,
"cluster_disk_size_utilisation": `1 - sum(max(node_filesystem_avail{device=~"/dev/.+", job="node-exporter"} * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:) by (node)) / sum(max(node_filesystem_size{device=~"/dev/.+", job="node-exporter"} * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:) by (node))`,
"cluster_disk_size_capacity": `sum(max(node_filesystem_size{device=~"/dev/.+", job="node-exporter"} * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:) by (node))`,
"cluster_disk_size_available": `sum(max(node_filesystem_avail{device=~"/dev/.+", job="node-exporter"} * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:) by (node))`,
"cluster_disk_inode_total": `sum(node:disk_inodes_total:)`,
"cluster_disk_inode_usage": `sum(node:disk_inodes_total:) - sum(node:disk_inodes_free:)`,
"cluster_disk_inode_utilisation": `1 - sum(node:disk_inodes_free:) / sum(node:disk_inodes_total:)`,
"cluster_namespace_count": `count(kube_namespace_annotations)`,
// cluster_pod_count = cluster_pod_running_count + cluster_pod_succeeded_count + cluster_pod_abnormal_count
"cluster_pod_count": `sum((kube_pod_status_scheduled{condition="true"} > 0) * on (namespace, pod) group_left(node) (sum by (node, namespace, pod) (kube_pod_info)) unless on (node) (kube_node_status_condition{condition="Ready",status=~"unknown|false"} > 0))`,
"cluster_pod_quota": `sum(kube_node_status_capacity_pods unless on (node) (kube_node_status_condition{condition="Ready",status=~"unknown|false"} > 0))`,
"cluster_pod_utilisation": `sum(kube_pod_info unless on (node) (kube_node_status_condition{condition="Ready",status=~"unknown|false"} > 0)) / sum(kube_node_status_capacity_pods unless on (node) (kube_node_status_condition{condition="Ready",status=~"unknown|false"} > 0))`,
"cluster_pod_running_count": `count(kube_pod_info unless on (pod) (kube_pod_status_phase{phase=~"Failed|Pending|Unknown|Succeeded"} > 0) unless on (node) (kube_node_status_condition{condition="Ready",status=~"unknown|false"} > 0))`,
"cluster_pod_succeeded_count": `count(kube_pod_info unless on (pod) (kube_pod_status_phase{phase=~"Failed|Pending|Unknown|Running"} > 0) unless on (node) (kube_node_status_condition{condition="Ready",status=~"unknown|false"} > 0))`,
"cluster_pod_abnormal_count": `count(kube_pod_info unless on (pod) (kube_pod_status_phase{phase=~"Succeeded|Running"} > 0) unless on (node) (kube_node_status_condition{condition="Ready",status=~"unknown|false"} > 0))`,
"cluster_node_online": `sum(kube_node_status_condition{condition="Ready",status="true"})`,
"cluster_node_offline": `sum(kube_node_status_condition{condition="Ready",status=~"unknown|false"})`,
"cluster_node_total": `sum(kube_node_status_condition{condition="Ready"})`,
"cluster_ingresses_extensions_count": `sum(kube_ingress_labels)`,
"cluster_configmap_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", resource="count/configmaps"}) by (resource, type)`,
"cluster_jobs_batch_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", resource="count/jobs.batch"}) by (resource, type)`,
"cluster_roles_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", resource="count/roles.rbac.authorization.k8s.io"}) by (resource, type)`,
"cluster_memory_limit_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", resource="limits.memory"}) by (resource, type)`,
"cluster_pvc_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", resource="persistentvolumeclaims"}) by (resource, type)`,
"cluster_memory_request_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", resource="requests.memory"}) by (resource, type)`,
"cluster_pvc_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", resource="count/persistentvolumeclaims"}) by (resource, type)`,
"cluster_cronjobs_batch_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", resource="count/cronjobs.batch"}) by (resource, type)`,
"cluster_ingresses_extensions_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", resource="count/ingresses.extensions"}) by (resource, type)`,
"cluster_cpu_limit_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", resource="limits.cpu"}) by (resource, type)`,
"cluster_storage_request_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", resource="requests.storage"}) by (resource, type)`,
"cluster_deployment_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", resource="count/deployments.apps"}) by (resource, type)`,
"cluster_pod_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", resource="count/pods"}) by (resource, type)`,
"cluster_statefulset_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", resource="count/statefulsets.apps"}) by (resource, type)`,
"cluster_daemonset_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", resource="count/daemonsets.apps"}) by (resource, type)`,
"cluster_secret_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", resource="count/secrets"}) by (resource, type)`,
"cluster_service_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", resource="count/services"}) by (resource, type)`,
"cluster_cpu_request_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", resource="requests.cpu"}) by (resource, type)`,
"cluster_service_loadbalancer_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", resource="services.loadbalancers"}) by (resource, type)`,
"cluster_cronjob_count": `sum(kube_cronjob_labels)`,
"cluster_pvc_count": `sum(kube_persistentvolumeclaim_info)`,
"cluster_daemonset_count": `sum(kube_daemonset_labels)`,
"cluster_deployment_count": `sum(kube_deployment_labels)`,
"cluster_endpoint_count": `sum(kube_endpoint_labels)`,
"cluster_hpa_count": `sum(kube_hpa_labels)`,
"cluster_job_count": `sum(kube_job_labels)`,
"cluster_statefulset_count": `sum(kube_statefulset_labels)`,
"cluster_replicaset_count": `count(kube_replicaset_created)`,
"cluster_service_count": `sum(kube_service_info)`,
"cluster_secret_count": `sum(kube_secret_info)`,
"cluster_pv_count": `sum(kube_persistentvolume_labels)`,
"cluster_load1": `sum(node_load1{job="node-exporter"}) / sum(node:node_num_cpu:sum)`,
"cluster_load5": `sum(node_load5{job="node-exporter"}) / sum(node:node_num_cpu:sum)`,
"cluster_load15": `sum(node_load15{job="node-exporter"}) / sum(node:node_num_cpu:sum)`,
//node
"node_cpu_utilisation": "node:node_cpu_utilisation:avg1m",
"node_cpu_total": "node:node_num_cpu:sum",
"node_memory_utilisation": "node:node_memory_utilisation:",
"node_memory_available": "node:node_memory_bytes_available:sum",
"node_memory_total": "node:node_memory_bytes_total:sum",
"node_memory_usage_wo_cache": "node:node_memory_bytes_total:sum$1 - node:node_memory_bytes_available:sum$1",
"node_net_utilisation": "node:node_net_utilisation:sum_irate",
"node_net_bytes_transmitted": "node:node_net_bytes_transmitted:sum_irate",
"node_net_bytes_received": "node:node_net_bytes_received:sum_irate",
"node_disk_read_iops": "node:data_volume_iops_reads:sum",
"node_disk_write_iops": "node:data_volume_iops_writes:sum",
"node_disk_read_throughput": "node:data_volume_throughput_bytes_read:sum",
"node_disk_write_throughput": "node:data_volume_throughput_bytes_written:sum",
"node_disk_size_capacity": `max(node_filesystem_size{device=~"/dev/.+", job="node-exporter"} * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:$1) by (node)`,
"node_disk_size_available": `max(node_filesystem_avail{device=~"/dev/.+", job="node-exporter"} * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:$1) by (node)`,
"node_disk_size_usage": `max((node_filesystem_size{device=~"/dev/.+", job="node-exporter"} - node_filesystem_avail{device=~"/dev/.+", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:$1) by (node)`,
"node_disk_size_utilisation": `max(((node_filesystem_size{device=~"/dev/.+", job="node-exporter"} - node_filesystem_avail{device=~"/dev/.+", job="node-exporter"}) / node_filesystem_size{device=~"/dev/.+", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:$1) by (node)`,
"node_disk_inode_total": `node:disk_inodes_total:$1`,
"node_disk_inode_usage": `node:disk_inodes_total:$1 - node:disk_inodes_free:$1`,
"node_disk_inode_utilisation": `(1 - (node:disk_inodes_free:$1 / node:disk_inodes_total:$1))`,
"node_pod_count": `sum by (node) ((kube_pod_status_scheduled{condition="true"} > 0) * on (namespace, pod) group_left(node) kube_pod_info$1 unless on (node) (kube_node_status_condition{condition="Ready",status=~"unknown|false"} > 0))`,
"node_pod_quota": `sum(kube_node_status_capacity_pods$1) by (node) unless on (node) (kube_node_status_condition{condition="Ready",status=~"unknown|false"} > 0)`,
"node_pod_utilisation": `(sum(kube_pod_info$1) by (node) / sum(kube_node_status_capacity_pods$1) by (node)) unless on (node) (kube_node_status_condition{condition="Ready",status=~"unknown|false"} > 0)`,
"node_pod_running_count": `count(kube_pod_info$1 unless on (pod) (kube_pod_status_phase{phase=~"Failed|Pending|Unknown|Succeeded"} > 0)) by (node) unless on (node) (kube_node_status_condition{condition="Ready",status=~"unknown|false"} > 0)`,
"node_pod_succeeded_count": `count(kube_pod_info$1 unless on (pod) (kube_pod_status_phase{phase=~"Failed|Pending|Unknown|Running"} > 0)) by (node) unless on (node) (kube_node_status_condition{condition="Ready",status=~"unknown|false"} > 0)`,
"node_pod_abnormal_count": `count(kube_pod_info$1 unless on (pod) (kube_pod_status_phase{phase=~"Succeeded|Running"} > 0)) by (node) unless on (node) (kube_node_status_condition{condition="Ready",status=~"unknown|false"} > 0)`,
// without log node: unless on(node) kube_node_labels{label_role="log"}
"node_cpu_usage": `node:node_cpu_utilisation:avg1m$1 * node:node_num_cpu:sum$1`,
"node_load1": `sum by (node) (node_load1{job="node-exporter"} * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:$1) / node:node_num_cpu:sum`,
"node_load5": `sum by (node) (node_load5{job="node-exporter"} * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:$1) / node:node_num_cpu:sum`,
"node_load15": `sum by (node) (node_load15{job="node-exporter"} * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:$1) / node:node_num_cpu:sum`,
//namespace
"namespace_cpu_usage": `namespace:container_cpu_usage_seconds_total:sum_rate{namespace!="", namespace=~"$1"} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_memory_usage": `namespace:container_memory_usage_bytes:sum{namespace!="", namespace=~"$1"} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_memory_usage_wo_cache": `namespace:container_memory_usage_bytes_wo_cache:sum{namespace!="", namespace=~"$1"}* on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_net_bytes_transmitted": `sum by (namespace) (irate(container_network_transmit_bytes_total{namespace!="", namespace=~"$1", pod_name!="", ` + ExcludedVirtualNetworkInterfaces + `, job="kubelet"}[5m]))* on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_net_bytes_received": `sum by (namespace) (irate(container_network_receive_bytes_total{namespace!="", namespace=~"$1", pod_name!="", ` + ExcludedVirtualNetworkInterfaces + `, job="kubelet"}[5m])) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_pod_count": `sum(kube_pod_status_phase{phase!~"Failed|Succeeded", namespace!="", namespace=~"$1"}) by (namespace) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_pod_running_count": `sum(kube_pod_status_phase{phase="Running", namespace!="", namespace=~"$1"}) by (namespace) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_pod_succeeded_count": `sum(kube_pod_status_phase{phase="Succeeded", namespace!="", namespace=~"$1"}) by (namespace) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_pod_abnormal_count": `sum(kube_pod_status_phase{phase=~"Failed|Pending|Unknown", namespace!="", namespace=~"$1"}) by (namespace) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_configmap_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace=~"$1", resource="count/configmaps"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_jobs_batch_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace=~"$1", resource="count/jobs.batch"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_roles_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace=~"$1", resource="count/roles.rbac.authorization.k8s.io"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_memory_limit_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace=~"$1", resource="limits.memory"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_pvc_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace=~"$1", resource="persistentvolumeclaims"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_memory_request_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace=~"$1", resource="requests.memory"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_pvc_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace=~"$1", resource="count/persistentvolumeclaims"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_cronjobs_batch_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace=~"$1", resource="count/cronjobs.batch"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_ingresses_extensions_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace=~"$1", resource="count/ingresses.extensions"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_cpu_limit_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace=~"$1", resource="limits.cpu"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_storage_request_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace=~"$1", resource="requests.storage"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_deployment_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace=~"$1", resource="count/deployments.apps"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_pod_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace=~"$1", resource="count/pods"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_statefulset_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace=~"$1", resource="count/statefulsets.apps"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_daemonset_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace=~"$1", resource="count/daemonsets.apps"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_secret_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace=~"$1", resource="count/secrets"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_service_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace=~"$1", resource="count/services"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_cpu_request_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace=~"$1", resource="requests.cpu"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_service_loadbalancer_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace=~"$1", resource="services.loadbalancers"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_configmap_count_hard": `sum(kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", namespace=~"$1", resource="count/configmaps"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_jobs_batch_count_hard": `sum(kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", namespace=~"$1", resource="count/jobs.batch"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_roles_count_hard": `sum(kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", namespace=~"$1", resource="count/roles.rbac.authorization.k8s.io"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_memory_limit_hard": `sum(kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", namespace=~"$1", resource="limits.memory"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_pvc_hard": `sum(kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", namespace=~"$1", resource="persistentvolumeclaims"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_memory_request_hard": `sum(kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", namespace=~"$1", resource="requests.memory"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_pvc_count_hard": `sum(kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", namespace=~"$1", resource="count/persistentvolumeclaims"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_cronjobs_batch_count_hard": `sum(kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", namespace=~"$1", resource="count/cronjobs.batch"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_ingresses_extensions_count_hard": `sum(kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", namespace=~"$1", resource="count/ingresses.extensions"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_cpu_limit_hard": `sum(kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", namespace=~"$1", resource="limits.cpu"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_storage_request_hard": `sum(kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", namespace=~"$1", resource="requests.storage"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_deployment_count_hard": `sum(kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", namespace=~"$1", resource="count/deployments.apps"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_pod_count_hard": `sum(kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", namespace=~"$1", resource="count/pods"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_statefulset_count_hard": `sum(kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", namespace=~"$1", resource="count/statefulsets.apps"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_daemonset_count_hard": `sum(kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", namespace=~"$1", resource="count/daemonsets.apps"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_secret_count_hard": `sum(kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", namespace=~"$1", resource="count/secrets"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_service_count_hard": `sum(kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", namespace=~"$1", resource="count/services"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_cpu_request_hard": `sum(kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", namespace=~"$1", resource="requests.cpu"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_service_loadbalancer_hard": `sum(kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", namespace=~"$1", resource="services.loadbalancers"}) by (namespace, resource, type) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_cronjob_count": `sum(kube_cronjob_labels{namespace!="", namespace=~"$1"}) by (namespace) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_pvc_count": `sum(kube_persistentvolumeclaim_info{namespace!="", namespace=~"$1"}) by (namespace) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_daemonset_count": `sum(kube_daemonset_labels{namespace!="", namespace=~"$1"}) by (namespace) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_deployment_count": `sum(kube_deployment_labels{namespace!="", namespace=~"$1"}) by (namespace) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_endpoint_count": `sum(kube_endpoint_labels{namespace!="", namespace=~"$1"}) by (namespace) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_hpa_count": `sum(kube_hpa_labels{namespace!="", namespace=~"$1"}) by (namespace) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_job_count": `sum(kube_job_labels{namespace!="", namespace=~"$1"}) by (namespace) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_statefulset_count": `sum(kube_statefulset_labels{namespace!="", namespace=~"$1"}) by (namespace) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_replicaset_count": `count(kube_replicaset_created{namespace!="", namespace=~"$1"}) by (namespace) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_service_count": `sum(kube_service_info{namespace!="", namespace=~"$1"}) by (namespace) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_secret_count": `sum(kube_secret_info{namespace!="", namespace=~"$1"}) by (namespace) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
"namespace_ingresses_extensions_count": `sum(kube_ingress_labels{namespace!="", namespace=~"$1"}) by (namespace) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels)`,
// pod
"pod_cpu_usage": `sum(irate(container_cpu_usage_seconds_total{job="kubelet", namespace="$1", pod_name!="", pod_name="$2", image!=""}[5m])) by (namespace, pod_name)`,
"pod_memory_usage": `sum(container_memory_usage_bytes{job="kubelet", namespace="$1", pod_name!="", pod_name="$2", image!=""}) by (namespace, pod_name)`,
"pod_memory_usage_wo_cache": `sum(container_memory_usage_bytes{job="kubelet", namespace="$1", pod_name!="", pod_name="$2", image!=""} - container_memory_cache{job="kubelet", namespace="$1", pod_name!="", pod_name="$2",image!=""}) by (namespace, pod_name)`,
"pod_net_bytes_transmitted": `sum by (namespace, pod_name) (irate(container_network_transmit_bytes_total{namespace="$1", pod_name!="", pod_name="$2", ` + ExcludedVirtualNetworkInterfaces + `, job="kubelet"}[5m]))`,
"pod_net_bytes_received": `sum by (namespace, pod_name) (irate(container_network_receive_bytes_total{namespace="$1", pod_name!="", pod_name="$2", ` + ExcludedVirtualNetworkInterfaces + `, job="kubelet"}[5m]))`,
"pod_cpu_usage_all": `sum(irate(container_cpu_usage_seconds_total{job="kubelet", namespace="$1", pod_name!="", pod_name=~"$2", image!=""}[5m])) by (namespace, pod_name)`,
"pod_memory_usage_all": `sum(container_memory_usage_bytes{job="kubelet", namespace="$1", pod_name!="", pod_name=~"$2", image!=""}) by (namespace, pod_name)`,
"pod_memory_usage_wo_cache_all": `sum(container_memory_usage_bytes{job="kubelet", namespace="$1", pod_name!="", pod_name=~"$2", image!=""} - container_memory_cache{job="kubelet", namespace="$1", pod_name!="", pod_name=~"$2", image!=""}) by (namespace, pod_name)`,
"pod_net_bytes_transmitted_all": `sum by (namespace, pod_name) (irate(container_network_transmit_bytes_total{namespace="$1", pod_name!="", pod_name=~"$2", ` + ExcludedVirtualNetworkInterfaces + `, job="kubelet"}[5m]))`,
"pod_net_bytes_received_all": `sum by (namespace, pod_name) (irate(container_network_receive_bytes_total{namespace="$1", pod_name!="", pod_name=~"$2", ` + ExcludedVirtualNetworkInterfaces + `, job="kubelet"}[5m]))`,
"pod_cpu_usage_node": `sum by (node, pod_name) (irate(container_cpu_usage_seconds_total{job="kubelet",pod_name!="", pod_name=~"$2", image!=""}[5m]) * on (namespace, pod_name) group_left(node) label_join(node_namespace_pod:kube_pod_info:{node="$3"}, "pod_name", "", "pod", "_name"))`,
"pod_memory_usage_node": `sum by (node, pod_name) (container_memory_usage_bytes{job="kubelet",pod_name!="", pod_name=~"$2", image!=""} * on (namespace, pod_name) group_left(node) label_join(node_namespace_pod:kube_pod_info:{node="$3"}, "pod_name", "", "pod", "_name"))`,
"pod_memory_usage_wo_cache_node": `sum by (node, pod_name) ((container_memory_usage_bytes{job="kubelet",pod_name!="", pod_name=~"$2", image!=""} - container_memory_cache{job="kubelet",pod_name!="", pod_name=~"$2", image!=""}) * on (namespace, pod_name) group_left(node) label_join(node_namespace_pod:kube_pod_info:{node="$3"}, "pod_name", "", "pod", "_name"))`,
"pod_net_bytes_transmitted_node": `sum by (node, pod_name) (irate(container_network_transmit_bytes_total{pod_name!="", pod_name=~"$2", ` + ExcludedVirtualNetworkInterfaces + `, job="kubelet"}[5m]) * on (pod_name) group_left(node) label_join(node_namespace_pod:kube_pod_info:{node="$3"}, "pod_name", "", "pod", "_name"))`,
"pod_net_bytes_received_node": `sum by (node, pod_name) (irate(container_network_receive_bytes_total{pod_name!="", pod_name=~"$2", ` + ExcludedVirtualNetworkInterfaces + `, job="kubelet"}[5m]) * on (pod_name) group_left(node) label_join(node_namespace_pod:kube_pod_info:{node="$3"}, "pod_name", "", "pod", "_name"))`,
// workload
"workload_pod_cpu_usage": `label_join(sum(label_replace(label_replace(label_replace(label_join(label_join(label_replace(sum(irate(container_cpu_usage_seconds_total{job="kubelet", namespace="$2", pod_name!="", pod_name=~"$3", image!=""}[5m])) by (namespace, pod_name) * on (pod_name) group_left(owner_kind) label_join(label_replace(kube_pod_owner{namespace="$2", pod=~".*"}, "owner_kind", "POD", "owner_kind", "<none>"), "pod_name", "", "pod", "_name") , "postfix", "-POD", "owner_kind", "POD"), "pod_name", "", "pod_name", "postfix"), "dist", "-", "owner_kind", "pod_name"), "pod_name", "$1", "dist", "ReplicaSet-(.+)-(.+)"), "workload", "$1", "pod_name", "(.+)-(.+)"), "owner_kind", "Deployment", "owner_kind", "ReplicaSet.*")) by (namespace, workload, owner_kind), "workload", ":", "owner_kind", "workload")`,
"workload_pod_memory_usage": `label_join(sum(label_replace(label_replace(label_replace(label_join(label_join(label_replace(sum(container_memory_usage_bytes{job="kubelet", namespace="$2", pod_name!="", pod_name=~"$3", image!=""}) by (namespace, pod_name) * on (pod_name) group_left(owner_kind) label_join(label_replace(kube_pod_owner{namespace="$2", pod=~".*"}, "owner_kind", "POD", "owner_kind", "<none>"), "pod_name", "", "pod", "_name") , "postfix", "-POD", "owner_kind", "POD"), "pod_name", "", "pod_name", "postfix"), "dist", "-", "owner_kind", "pod_name"), "pod_name", "$1", "dist", "ReplicaSet-(.+)-(.+)"), "workload", "$1", "pod_name", "(.+)-(.+)"), "owner_kind", "Deployment", "owner_kind", "ReplicaSet.*")) by (namespace, workload, owner_kind), "workload", ":", "owner_kind", "workload")`,
"workload_pod_memory_usage_wo_cache": `label_join(sum(label_replace(label_replace(label_replace(label_join(label_join(label_replace(sum(container_memory_usage_bytes{job="kubelet", namespace="$2", pod_name!="", pod_name=~"$3", image!=""} - container_memory_cache{job="kubelet", namespace="$2", pod_name!="", pod_name=~"$3", image!=""}) by (namespace, pod_name) * on (pod_name) group_left(owner_kind) label_join(label_replace(kube_pod_owner{namespace="$2", pod=~".*"}, "owner_kind", "POD", "owner_kind", "<none>"), "pod_name", "", "pod", "_name") , "postfix", "-POD", "owner_kind", "POD"), "pod_name", "", "pod_name", "postfix"), "dist", "-", "owner_kind", "pod_name"), "pod_name", "$1", "dist", "ReplicaSet-(.+)-(.+)"), "workload", "$1", "pod_name", "(.+)-(.+)"), "owner_kind", "Deployment", "owner_kind", "ReplicaSet.*")) by (namespace, workload, owner_kind), "workload", ":", "owner_kind", "workload")`,
"workload_pod_net_bytes_transmitted": `label_join(sum(label_replace(label_replace(label_replace(label_join(label_join(label_replace(sum(irate(container_network_transmit_bytes_total{namespace="$2", pod_name!="", pod_name=~"$3", ` + ExcludedVirtualNetworkInterfaces + `, job="kubelet"}[5m])) by (namespace, pod_name) * on (pod_name) group_left(owner_kind) label_join(label_replace(kube_pod_owner{namespace="$2", pod=~".*"}, "owner_kind", "POD", "owner_kind", "<none>"), "pod_name", "", "pod", "_name") , "postfix", "-POD", "owner_kind", "POD"), "pod_name", "", "pod_name", "postfix"), "dist", "-", "owner_kind", "pod_name"), "pod_name", "$1", "dist", "ReplicaSet-(.+)-(.+)"), "workload", "$1", "pod_name", "(.+)-(.+)"), "owner_kind", "Deployment", "owner_kind", "ReplicaSet.*")) by (namespace, workload, owner_kind), "workload", ":", "owner_kind", "workload")`,
"workload_pod_net_bytes_received": `label_join(sum(label_replace(label_replace(label_replace(label_join(label_join(label_replace(sum(irate(container_network_receive_bytes_total{namespace="$2", pod_name!="", pod_name=~"$3", ` + ExcludedVirtualNetworkInterfaces + `, job="kubelet"}[5m])) by (namespace, pod_name) * on (pod_name) group_left(owner_kind) label_join(label_replace(kube_pod_owner{namespace="$2", pod=~".*"}, "owner_kind", "POD", "owner_kind", "<none>"), "pod_name", "", "pod", "_name") , "postfix", "-POD", "owner_kind", "POD"), "pod_name", "", "pod_name", "postfix"), "dist", "-", "owner_kind", "pod_name"), "pod_name", "$1", "dist", "ReplicaSet-(.+)-(.+)"), "workload", "$1", "pod_name", "(.+)-(.+)"), "owner_kind", "Deployment", "owner_kind", "ReplicaSet.*")) by (namespace, workload, owner_kind), "workload", ":", "owner_kind", "workload")`,
"workload_deployment_replica": `label_join(sum (label_join(label_replace(kube_deployment_spec_replicas{namespace="$2", deployment=~"$3"}, "owner_kind", "Deployment", "", ""), "workload", "", "deployment")) by (namespace, owner_kind, workload), "workload", ":", "owner_kind", "workload")`,
"workload_deployment_replica_available": `label_join(sum (label_join(label_replace(kube_deployment_status_replicas_available{namespace="$2", deployment=~"$3"}, "owner_kind", "Deployment", "", ""), "workload", "", "deployment")) by (namespace, owner_kind, workload), "workload", ":", "owner_kind", "workload")`,
"workload_statefulset_replica": `label_join(sum (label_join(label_replace(kube_statefulset_replicas{namespace="$2", statefulset=~"$3"}, "owner_kind", "StatefulSet", "", ""), "workload", "", "statefulset")) by (namespace, owner_kind, workload), "workload", ":", "owner_kind", "workload")`,
"workload_statefulset_replica_available": `label_join(sum (label_join(label_replace(kube_statefulset_status_replicas_current{namespace="$2", statefulset=~"$3"}, "owner_kind", "StatefulSet", "", ""), "workload", "", "statefulset")) by (namespace, owner_kind, workload), "workload", ":", "owner_kind", "workload")`,
"workload_daemonset_replica": `label_join(sum (label_join(label_replace(kube_daemonset_status_desired_number_scheduled{namespace="$2", daemonset=~"$3"}, "owner_kind", "DaemonSet", "", ""), "workload", "", "daemonset")) by (namespace, owner_kind, workload), "workload", ":", "owner_kind", "workload")`,
"workload_daemonset_replica_available": `label_join(sum (label_join(label_replace(kube_daemonset_status_number_available{namespace="$2", daemonset=~"$3"}, "owner_kind", "DaemonSet", "", ""), "workload", "", "daemonset")) by (namespace, owner_kind, workload), "workload", ":", "owner_kind", "workload")`,
// container
"container_cpu_usage": `sum(irate(container_cpu_usage_seconds_total{namespace="$1", pod_name="$2", container_name!="POD", container_name=~"$3"}[5m])) by (namespace, pod_name, container_name)`,
"container_memory_usage": `sum(container_memory_usage_bytes{namespace="$1", pod_name="$2", container_name!="POD", container_name=~"$3"}) by (namespace, pod_name, container_name)`,
"container_memory_usage_wo_cache": `container_memory_usage_bytes{namespace="$1", pod_name="$2", container_name!="POD", container_name=~"$3"} - ignoring(id, image, endpoint, instance, job, name, service) container_memory_cache{namespace="$1", pod_name="$2", container_name!="POD", container_name=~"$3"}`,
"container_net_bytes_transmitted": `sum(irate(container_network_transmit_bytes_total{job="kubelet", namespace="$1", pod_name="$2", container_name="POD", ` + ExcludedVirtualNetworkInterfaces + `}[5m])) by (namespace, pod_name, container_name)`,
"container_net_bytes_received": `sum(irate(container_network_receive_bytes_total{job="kubelet", namespace="$1", pod_name="$2", container_name="POD", ` + ExcludedVirtualNetworkInterfaces + `}[5m])) by (namespace, pod_name, container_name)`,
"container_cpu_usage_node": `sum by (node, pod_name, container_name) (irate(container_cpu_usage_seconds_total{job="kubelet", pod_name="$2", container_name!="POD", container_name!="", container_name=~"$3", image!=""}[5m]) * on (pod_name) group_left(node) label_join(node_namespace_pod:kube_pod_info:{node="$1"}, "pod_name", "", "pod", "_name"))`,
"container_memory_usage_node": `sum by (node, pod_name, container_name) (container_memory_usage_bytes{job="kubelet", pod_name="$2", container_name!="POD", container_name!="", container_name=~"$3", image!=""} * on (pod_name) group_left(node) label_join(node_namespace_pod:kube_pod_info:{node="$1"}, "pod_name", "", "pod", "_name"))`,
"container_memory_usage_wo_cache_node": `sum by (node, pod_name, container_name) ((container_memory_usage_bytes{job="kubelet", pod_name="$2", container_name!="POD", container_name!="", container_name=~"$3", image!=""} - container_memory_cache{job="kubelet", pod_name="$2", container_name!="POD", container_name!="", container_name=~"$3", image!=""}) * on (pod_name) group_left(node) label_join(node_namespace_pod:kube_pod_info:{node="$1"}, "pod_name", "", "pod", "_name"))`,
"container_net_bytes_transmitted_node": `sum by (node, pod_name, container_name) (irate(container_network_transmit_bytes_total{job="kubelet", ` + ExcludedVirtualNetworkInterfaces + `, pod_name="$2", container_name="POD", container_name!="", image!=""}[5m]) * on (pod_name) group_left(node) label_join(node_namespace_pod:kube_pod_info:{node="$1"}, "pod_name", "", "pod", "_name"))`,
"container_net_bytes_received_node": `sum by (node, pod_name, container_name) (irate(container_network_receive_bytes_total{job="kubelet", ` + ExcludedVirtualNetworkInterfaces + `, pod_name="$2", container_name="POD", container_name!="", image!=""}[5m]) * on (pod_name) group_left(node) label_join(node_namespace_pod:kube_pod_info:{node="$1"}, "pod_name", "", "pod", "_name"))`,
// workspace
"workspace_cpu_usage": `sum(namespace:container_cpu_usage_seconds_total:sum_rate{namespace!="", namespace$1})`,
"workspace_memory_usage": `sum(namespace:container_memory_usage_bytes:sum{namespace!="", namespace$1})`,
"workspace_memory_usage_wo_cache": `sum(namespace:container_memory_usage_bytes_wo_cache:sum{namespace!="", namespace$1})`,
"workspace_net_bytes_transmitted": `sum(sum by (namespace) (irate(container_network_transmit_bytes_total{namespace!="", namespace$1, pod_name!="", ` + ExcludedVirtualNetworkInterfaces + `, job="kubelet"}[5m])))`,
"workspace_net_bytes_received": `sum(sum by (namespace) (irate(container_network_receive_bytes_total{namespace!="", namespace$1, pod_name!="", ` + ExcludedVirtualNetworkInterfaces + `, job="kubelet"}[5m])))`,
"workspace_pod_count": `sum(kube_pod_status_phase{phase!~"Failed|Succeeded", namespace!="", namespace$1})`,
"workspace_pod_running_count": `sum(kube_pod_status_phase{phase="Running", namespace!="", namespace$1})`,
"workspace_pod_succeeded_count": `sum(kube_pod_status_phase{phase="Succeeded", namespace!="", namespace$1})`,
"workspace_pod_abnormal_count": `sum(kube_pod_status_phase{phase=~"Failed|Pending|Unknown", namespace!="", namespace$1})`,
"workspace_configmap_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace$1, resource="count/configmaps"}) by (resource, type)`,
"workspace_jobs_batch_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace$1, resource="count/jobs.batch"}) by (resource, type)`,
"workspace_roles_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace$1, resource="count/roles.rbac.authorization.k8s.io"}) by (resource, type)`,
"workspace_memory_limit_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace$1, resource="limits.memory"}) by (resource, type)`,
"workspace_pvc_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace$1, resource="persistentvolumeclaims"}) by (resource, type)`,
"workspace_memory_request_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace$1, resource="requests.memory"}) by (resource, type)`,
"workspace_pvc_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace$1, resource="count/persistentvolumeclaims"}) by (resource, type)`,
"workspace_cronjobs_batch_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace$1, resource="count/cronjobs.batch"}) by (resource, type)`,
"workspace_ingresses_extensions_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace$1, resource="count/ingresses.extensions"}) by (resource, type)`,
"workspace_cpu_limit_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace$1, resource="limits.cpu"}) by (resource, type)`,
"workspace_storage_request_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace$1, resource="requests.storage"}) by (resource, type)`,
"workspace_deployment_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace$1, resource="count/deployments.apps"}) by (resource, type)`,
"workspace_pod_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace$1, resource="count/pods"}) by (resource, type)`,
"workspace_statefulset_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace$1, resource="count/statefulsets.apps"}) by (resource, type)`,
"workspace_daemonset_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace$1, resource="count/daemonsets.apps"}) by (resource, type)`,
"workspace_secret_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace$1, resource="count/secrets"}) by (resource, type)`,
"workspace_service_count_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace$1, resource="count/services"}) by (resource, type)`,
"workspace_cpu_request_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace$1, resource="requests.cpu"}) by (resource, type)`,
"workspace_service_loadbalancer_used": `sum(kube_resourcequota{resourcequota!="quota", type="used", namespace!="", namespace$1, resource="services.loadbalancers"}) by (resource, type)`,
"workspace_ingresses_extensions_count": `sum(kube_ingress_labels{namespace!="", namespace$1})`,
"workspace_cronjob_count": `sum(kube_cronjob_labels{namespace!="", namespace$1})`,
"workspace_pvc_count": `sum(kube_persistentvolumeclaim_info{namespace!="", namespace$1})`,
"workspace_daemonset_count": `sum(kube_daemonset_labels{namespace!="", namespace$1})`,
"workspace_deployment_count": `sum(kube_deployment_labels{namespace!="", namespace$1})`,
"workspace_endpoint_count": `sum(kube_endpoint_labels{namespace!="", namespace$1})`,
"workspace_hpa_count": `sum(kube_hpa_labels{namespace!="", namespace$1})`,
"workspace_job_count": `sum(kube_job_labels{namespace!="", namespace$1})`,
"workspace_statefulset_count": `sum(kube_statefulset_labels{namespace!="", namespace$1})`,
"workspace_replicaset_count": `count(kube_replicaset_created{namespace!="", namespace$1})`,
"workspace_service_count": `sum(kube_service_info{namespace!="", namespace$1})`,
"workspace_secret_count": `sum(kube_secret_info{namespace!="", namespace$1})`,
"workspace_all_project_count": `count(kube_namespace_annotations)`,
}

View File

@@ -0,0 +1,51 @@
package metrics
import (
"net/url"
"strings"
"k8s.io/api/core/v1"
"kubesphere.io/kubesphere/pkg/client"
)
func GetNamespacesWithMetrics(namespaces []*v1.Namespace) []*v1.Namespace {
var nsNameList []string
for i := range namespaces {
nsNameList = append(nsNameList, namespaces[i].Name)
}
nsFilter := "^(" + strings.Join(nsNameList, "|") + ")$"
var timeRelateParams = make(url.Values)
params := client.MonitoringRequestParams{
NsFilter: nsFilter,
Params: timeRelateParams,
QueryType: client.DefaultQueryType,
MetricsFilter: "namespace_cpu_usage|namespace_memory_usage_wo_cache|namespace_pod_count",
}
rawMetrics := MonitorAllMetrics(&params, MetricLevelNamespace)
for _, result := range rawMetrics.Results {
for _, data := range result.Data.Result {
metricDescMap, ok := data["metric"].(map[string]interface{})
if ok {
if ns, exist := metricDescMap["namespace"]; exist {
timeAndValue, ok := data["value"].([]interface{})
if ok && len(timeAndValue) == 2 {
for i := 0; i < len(namespaces); i++ {
if namespaces[i].Name == ns {
if namespaces[i].Annotations == nil {
namespaces[i].Annotations = make(map[string]string, 0)
}
namespaces[i].Annotations[result.MetricName] = timeAndValue[1].(string)
}
}
}
}
}
}
}
return namespaces
}

306
pkg/models/metrics/util.go Normal file
View File

@@ -0,0 +1,306 @@
/*
Copyright 2018 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 metrics
import (
"math"
"sort"
"strconv"
"unicode"
"runtime/debug"
"github.com/golang/glog"
)
const (
DefaultPageLimit = 5
DefaultPage = 1
)
type FormatedMetricDataWrapper struct {
fmtMetricData FormatedMetricData
by func(p, q *map[string]interface{}) bool
}
func (wrapper FormatedMetricDataWrapper) Len() int {
return len(wrapper.fmtMetricData.Result)
}
func (wrapper FormatedMetricDataWrapper) Less(i, j int) bool {
return wrapper.by(&wrapper.fmtMetricData.Result[i], &wrapper.fmtMetricData.Result[j])
}
func (wrapper FormatedMetricDataWrapper) Swap(i, j int) {
wrapper.fmtMetricData.Result[i], wrapper.fmtMetricData.Result[j] = wrapper.fmtMetricData.Result[j], wrapper.fmtMetricData.Result[i]
}
// sorted metric by ascending or descending order
func Sort(sortMetricName string, sortType string, fmtLevelMetric *FormatedLevelMetric, resourceType string) (*FormatedLevelMetric, int) {
defer func() {
if err := recover(); err != nil {
glog.Errorln(err)
debug.PrintStack()
}
}()
if sortMetricName == "" {
return fmtLevelMetric, -1
}
// default sort type is descending order
if sortType == "" {
sortType = ResultSortTypeDesc
}
var currentResourceMap = make(map[string]int)
// indexMap store sorted index for each node/namespace/pod
var indexMap = make(map[string]int)
i := 0
for _, metricItem := range fmtLevelMetric.Results {
if metricItem.Data.ResultType == ResultTypeVector && metricItem.Status == MetricStatusSuccess {
if metricItem.MetricName == sortMetricName {
if sortType == ResultSortTypeAsc {
// desc
sort.Sort(FormatedMetricDataWrapper{metricItem.Data, func(p, q *map[string]interface{}) bool {
value1 := (*p)[ResultItemValue].([]interface{})
value2 := (*q)[ResultItemValue].([]interface{})
v1, _ := strconv.ParseFloat(value1[len(value1)-1].(string), 64)
v2, _ := strconv.ParseFloat(value2[len(value2)-1].(string), 64)
if v1 == v2 {
resourceName1 := (*p)[ResultItemMetric].(map[string]interface{})[resourceType]
resourceName2 := (*q)[ResultItemMetric].(map[string]interface{})[resourceType]
return resourceName1.(string) < resourceName2.(string)
}
return v1 < v2
}})
} else {
// desc
sort.Sort(FormatedMetricDataWrapper{metricItem.Data, func(p, q *map[string]interface{}) bool {
value1 := (*p)[ResultItemValue].([]interface{})
value2 := (*q)[ResultItemValue].([]interface{})
v1, _ := strconv.ParseFloat(value1[len(value1)-1].(string), 64)
v2, _ := strconv.ParseFloat(value2[len(value2)-1].(string), 64)
if v1 == v2 {
resourceName1 := (*p)[ResultItemMetric].(map[string]interface{})[resourceType]
resourceName2 := (*q)[ResultItemMetric].(map[string]interface{})[resourceType]
return resourceName1.(string) > resourceName2.(string)
}
return v1 > v2
}})
}
for _, r := range metricItem.Data.Result {
// for some reasons, 'metric' may not contain `resourceType` field
// example: {"metric":{},"value":[1541142931.731,"3"]}
k, exist := r[ResultItemMetric].(map[string]interface{})[resourceType]
key := k.(string)
if exist {
if _, exist := indexMap[key]; !exist {
indexMap[key] = i
i = i + 1
}
}
}
}
// iterator all metric to find max metricItems length
for _, r := range metricItem.Data.Result {
k, ok := r[ResultItemMetric].(map[string]interface{})[resourceType]
if ok {
currentResourceMap[k.(string)] = 1
}
}
}
}
var keys []string
for k := range currentResourceMap {
keys = append(keys, k)
}
sort.Strings(keys)
for _, resource := range keys {
if _, exist := indexMap[resource]; !exist {
indexMap[resource] = i
i = i + 1
}
}
// sort other metric
for i := 0; i < len(fmtLevelMetric.Results); i++ {
re := fmtLevelMetric.Results[i]
if re.Data.ResultType == ResultTypeVector && re.Status == MetricStatusSuccess {
sortedMetric := make([]map[string]interface{}, len(indexMap))
for j := 0; j < len(re.Data.Result); j++ {
r := re.Data.Result[j]
k, exist := r[ResultItemMetric].(map[string]interface{})[resourceType]
if exist {
index, exist := indexMap[k.(string)]
if exist {
sortedMetric[index] = r
}
}
}
fmtLevelMetric.Results[i].Data.Result = sortedMetric
}
}
return fmtLevelMetric, len(indexMap)
}
func Page(pageNum string, limitNum string, fmtLevelMetric *FormatedLevelMetric, maxLength int) interface{} {
if maxLength <= 0 {
return fmtLevelMetric
}
// matrix type can not be sorted
for _, metricItem := range fmtLevelMetric.Results {
// if metric reterieved field, resultType is ""
if metricItem.Data.ResultType == ResultTypeMatrix {
return fmtLevelMetric
}
}
var page = DefaultPage
if pageNum != "" {
p, err := strconv.Atoi(pageNum)
if err != nil {
glog.Errorln(err)
} else {
if p > 0 {
page = p
}
}
} else {
// the default mode is none paging
return fmtLevelMetric
}
var limit = DefaultPageLimit
if limitNum != "" {
l, err := strconv.Atoi(limitNum)
if err != nil {
glog.Errorln(err)
} else {
if l > 0 {
limit = l
}
}
}
// the i page: [(page-1) * limit, (page) * limit - 1]
start := (page - 1) * limit
end := (page)*limit - 1
for i := 0; i < len(fmtLevelMetric.Results); i++ {
// only pageing when result type is `vector` and result status is `success`
if fmtLevelMetric.Results[i].Data.ResultType != ResultTypeVector || fmtLevelMetric.Results[i].Status != MetricStatusSuccess {
continue
}
resultLen := len(fmtLevelMetric.Results[i].Data.Result)
if start >= resultLen {
fmtLevelMetric.Results[i].Data.Result = nil
continue
}
if end >= resultLen {
end = resultLen - 1
}
slice := fmtLevelMetric.Results[i].Data.Result[start : end+1]
fmtLevelMetric.Results[i].Data.Result = slice
}
allPage := int(math.Ceil(float64(maxLength) / float64(limit)))
// add page fields
return &struct {
*FormatedLevelMetric
CurrentPage int `json:"page"`
TotalPage int `json:"total_page"`
TotalItem int `json:"total_item"`
Message string `json:"msg"`
}{
FormatedLevelMetric: fmtLevelMetric,
CurrentPage: page,
TotalItem: maxLength,
TotalPage: allPage,
Message: "paged",
}
}
// maybe this function is time consuming
func ReformatJson(metric string, metricsName string, needDelParams ...string) *FormatedMetric {
var formatMetric FormatedMetric
err := jsonIter.Unmarshal([]byte(metric), &formatMetric)
if err != nil {
glog.Errorln("Unmarshal metric json failed", err.Error(), metric)
}
if formatMetric.MetricName == "" {
if metricsName != "" {
formatMetric.MetricName = metricsName
}
}
// retrive metrics success
if formatMetric.Status == MetricStatusSuccess {
result := formatMetric.Data.Result
for _, res := range result {
metric, exist := res[ResultItemMetric]
metricMap, sure := metric.(map[string]interface{})
if exist && sure {
delete(metricMap, "__name__")
}
if len(needDelParams) > 0 {
for _, p := range needDelParams {
delete(metricMap, p)
}
}
}
}
return &formatMetric
}
func ReformatNodeStatusField(nodeMetric *FormatedMetric) *FormatedMetric {
metricCount := len(nodeMetric.Data.Result)
for i := 0; i < metricCount; i++ {
metric, exist := nodeMetric.Data.Result[i][ResultItemMetric]
if exist {
status, exist := metric.(map[string]interface{})[MetricStatus]
if exist {
status = UpperFirstLetter(status.(string))
metric.(map[string]interface{})[MetricStatus] = status
}
}
}
return nodeMetric
}
func UpperFirstLetter(str string) string {
for i, ch := range str {
return string(unicode.ToUpper(ch)) + str[i+1:]
}
return ""
}

285
pkg/models/nodes/nodes.go Normal file
View File

@@ -0,0 +1,285 @@
/*
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 nodes
import (
"fmt"
"math"
"strings"
"time"
"github.com/golang/glog"
"k8s.io/api/apps/v1beta2"
"k8s.io/api/core/v1"
policy "k8s.io/api/policy/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"kubesphere.io/kubesphere/pkg/client"
"kubesphere.io/kubesphere/pkg/errors"
)
func DrainNode(nodename string) (err error) {
k8sclient := client.K8sClient()
node, err := k8sclient.CoreV1().Nodes().Get(nodename, metav1.GetOptions{})
if err != nil {
return err
}
if node.Spec.Unschedulable {
return errors.New(errors.Conflict, fmt.Sprintf("node %s have been drained", nodename))
}
data := []byte(" {\"spec\":{\"unschedulable\":true}}")
nodestatus, err := k8sclient.CoreV1().Nodes().Patch(nodename, types.StrategicMergePatchType, data)
if err != nil {
return err
}
glog.Info(nodestatus)
donech := make(chan bool)
errch := make(chan error)
go drainEviction(nodename, donech, errch)
for {
select {
case err := <-errch:
return err
case <-donech:
return nil
}
}
}
func drainEviction(nodename string, donech chan bool, errch chan error) {
k8sclient := client.K8sClient()
var options metav1.ListOptions
pods := make([]v1.Pod, 0)
options.FieldSelector = "spec.nodeName=" + nodename
podList, err := k8sclient.CoreV1().Pods("").List(options)
if err != nil {
glog.Fatal(err)
errch <- err
}
options.FieldSelector = ""
daemonsetList, err := k8sclient.AppsV1beta2().DaemonSets("").List(options)
if err != nil {
glog.Fatal(err)
errch <- err
}
// remove mirror pod static pod
if len(podList.Items) > 0 {
for _, pod := range podList.Items {
if !containDaemonset(pod, *daemonsetList) {
//static or mirror pod
if isStaticPod(&pod) || isMirrorPod(&pod) {
continue
} else {
pods = append(pods, pod)
}
}
}
}
if len(pods) == 0 {
donech <- true
} else {
//create eviction
getPodFn := func(namespace, name string) (*v1.Pod, error) {
k8sclient := client.K8sClient()
return k8sclient.CoreV1().Pods(namespace).Get(name, metav1.GetOptions{})
}
evicerr := evictPods(pods, 0, getPodFn)
if evicerr == nil {
donech <- true
} else {
glog.Fatal(evicerr)
errch <- err
}
}
}
func getPodSource(pod *v1.Pod) (string, error) {
if pod.Annotations != nil {
if source, ok := pod.Annotations["kubernetes.io/config.source"]; ok {
return source, nil
}
}
return "", fmt.Errorf("cannot get source of pod %q", pod.UID)
}
func isStaticPod(pod *v1.Pod) bool {
source, err := getPodSource(pod)
return err == nil && source != "api"
}
func isMirrorPod(pod *v1.Pod) bool {
_, ok := pod.Annotations[v1.MirrorPodAnnotationKey]
return ok
}
func containDaemonset(pod v1.Pod, daemonsetList v1beta2.DaemonSetList) bool {
flag := false
for _, daemonset := range daemonsetList.Items {
if strings.Contains(pod.Name, daemonset.Name) {
flag = true
}
}
return flag
}
func evictPod(pod v1.Pod, GracePeriodSeconds int) error {
k8sclient := client.K8sClient()
deleteOptions := &metav1.DeleteOptions{}
if GracePeriodSeconds >= 0 {
gracePeriodSeconds := int64(GracePeriodSeconds)
deleteOptions.GracePeriodSeconds = &gracePeriodSeconds
}
var eviction policy.Eviction
eviction.Kind = "Eviction"
eviction.APIVersion = "policy/v1beta1"
eviction.Namespace = pod.Namespace
eviction.Name = pod.Name
eviction.DeleteOptions = deleteOptions
err := k8sclient.CoreV1().Pods(pod.Namespace).Evict(&eviction)
if err != nil {
return err
}
return nil
}
func evictPods(pods []v1.Pod, GracePeriodSeconds int, getPodFn func(namespace, name string) (*v1.Pod, error)) error {
doneCh := make(chan bool, len(pods))
errCh := make(chan error, 1)
for _, pod := range pods {
go func(pod v1.Pod, doneCh chan bool, errCh chan error) {
var err error
var count int
for {
err = evictPod(pod, GracePeriodSeconds)
if err == nil {
count++
if count > 2 {
break
} else {
continue
}
} else if apierrors.IsNotFound(err) {
count = 0
doneCh <- true
glog.Info(fmt.Sprintf("pod %s evict", pod.Name))
return
} else if apierrors.IsTooManyRequests(err) {
count = 0
time.Sleep(5 * time.Second)
} else {
count = 0
errCh <- fmt.Errorf("error when evicting pod %q: %v", pod.Name, err)
return
}
}
podArray := []v1.Pod{pod}
_, err = waitForDelete(podArray, time.Second, time.Duration(math.MaxInt64), getPodFn)
if err == nil {
doneCh <- true
glog.Info(fmt.Sprintf("pod %s delete", pod.Name))
} else {
errCh <- fmt.Errorf("error when waiting for pod %q terminating: %v", pod.Name, err)
}
}(pod, doneCh, errCh)
}
Timeout := 300 * power(10, 9)
doneCount := 0
// 0 timeout means infinite, we use MaxInt64 to represent it.
var globalTimeout time.Duration
if Timeout == 0 {
globalTimeout = time.Duration(math.MaxInt64)
} else {
globalTimeout = time.Duration(Timeout)
}
for {
select {
case err := <-errCh:
return err
case <-doneCh:
doneCount++
if doneCount == len(pods) {
return nil
}
case <-time.After(globalTimeout):
return fmt.Errorf("drain did not complete within %v", globalTimeout)
}
}
}
func waitForDelete(pods []v1.Pod, interval, timeout time.Duration, getPodFn func(string, string) (*v1.Pod, error)) ([]v1.Pod, error) {
err := wait.PollImmediate(interval, timeout, func() (bool, error) {
var pendingPods []v1.Pod
for i, pod := range pods {
p, err := getPodFn(pod.Namespace, pod.Name)
if apierrors.IsNotFound(err) || (p != nil && p.ObjectMeta.UID != pod.ObjectMeta.UID) {
continue
} else if err != nil {
return false, err
} else {
pendingPods = append(pendingPods, pods[i])
}
}
pods = pendingPods
if len(pendingPods) > 0 {
return false, nil
}
return true, nil
})
return pods, err
}
func power(x int64, n int) int64 {
var res int64 = 1
for n != 0 {
res *= x
n--
}
return res
}

151
pkg/models/quotas/quotas.go Normal file
View File

@@ -0,0 +1,151 @@
/*
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 quotas
import (
"github.com/golang/glog"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/labels"
v12 "k8s.io/client-go/listers/core/v1"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models/resources"
)
const (
podsKey = "count/pods"
daemonsetsKey = "count/daemonsets.apps"
deploymentsKey = "count/deployments.apps"
ingressKey = "count/ingresses.extensions"
rolesKey = "count/roles.rbac.authorization.k8s.io"
clusterRolesKey = "count/cluster-role"
servicesKey = "count/services"
statefulsetsKey = "count/statefulsets.apps"
persistentvolumeclaimsKey = "persistentvolumeclaims"
storageClassesKey = "count/storageClass"
namespaceKey = "count/namespace"
jobsKey = "count/jobs.batch"
cronJobsKey = "count/cronjobs.batch"
)
var (
resourceMap = map[string]string{daemonsetsKey: resources.DaemonSets, deploymentsKey: resources.Deployments,
ingressKey: resources.Ingresses, rolesKey: resources.Roles, servicesKey: resources.Services,
statefulsetsKey: resources.StatefulSets, persistentvolumeclaimsKey: resources.PersistentVolumeClaims, podsKey: resources.Pods,
namespaceKey: resources.Namespaces, storageClassesKey: resources.StorageClasses, clusterRolesKey: resources.ClusterRoles,
jobsKey: resources.Jobs, cronJobsKey: resources.CronJobs}
resouceQuotaLister v12.ResourceQuotaLister
)
type ResourceQuota struct {
Namespace string `json:"namespace"`
Data v1.ResourceQuotaStatus `json:"data"`
}
func getUsage(namespace, resource string) (int, error) {
list, err := resources.ListNamespaceResource(namespace, resource, "", "", false, -1, 0)
if err != nil {
return 0, err
}
return list.TotalCount, nil
}
func init() {
resouceQuotaLister = informers.SharedInformerFactory().Core().V1().ResourceQuotas().Lister()
}
func GetClusterQuotas() (*ResourceQuota, error) {
quota := v1.ResourceQuotaStatus{Hard: make(v1.ResourceList), Used: make(v1.ResourceList)}
for k, v := range resourceMap {
used, err := getUsage("", v)
if err != nil {
return nil, err
}
var quantity resource.Quantity
quantity.Set(int64(used))
quota.Used[v1.ResourceName(k)] = quantity
}
return &ResourceQuota{Namespace: "\"\"", Data: quota}, nil
}
func GetNamespaceQuotas(namespace string) (*ResourceQuota, error) {
quota, err := getNamespaceResourceQuota(namespace)
if err != nil {
glog.Error(err)
return nil, err
}
if quota == nil {
quota = &v1.ResourceQuotaStatus{Hard: make(v1.ResourceList), Used: make(v1.ResourceList)}
}
for k, v := range resourceMap {
if _, exist := quota.Used[v1.ResourceName(k)]; !exist {
if k == namespaceKey || k == storageClassesKey {
continue
}
used, err := getUsage(namespace, v)
if err != nil {
return nil, err
}
var quantity resource.Quantity
quantity.Set(int64(used))
quota.Used[v1.ResourceName(k)] = quantity
}
}
return &ResourceQuota{Namespace: namespace, Data: *quota}, nil
}
func updateNamespaceQuota(tmpResourceList, resourceList v1.ResourceList) {
if tmpResourceList == nil {
tmpResourceList = resourceList
}
for res, usage := range resourceList {
tmpUsage, exist := tmpResourceList[res]
if !exist {
tmpResourceList[res] = usage
}
if tmpUsage.Cmp(usage) == 1 {
tmpResourceList[res] = usage
}
}
}
func getNamespaceResourceQuota(namespace string) (*v1.ResourceQuotaStatus, error) {
quotaList, err := resouceQuotaLister.ResourceQuotas(namespace).List(labels.Everything())
if err != nil || len(quotaList) == 0 {
return nil, err
}
quotaStatus := v1.ResourceQuotaStatus{Hard: make(v1.ResourceList), Used: make(v1.ResourceList)}
for _, quota := range quotaList {
updateNamespaceQuota(quotaStatus.Hard, quota.Status.Hard)
updateNamespaceQuota(quotaStatus.Used, quota.Status.Used)
}
return &quotaStatus, nil
}

View File

@@ -0,0 +1,67 @@
/*
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 registries
import (
"context"
"encoding/base64"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/golang/glog"
"kubesphere.io/kubesphere/pkg/errors"
)
type AuthInfo struct {
Username string `json:"username"`
Password string `json:"password"`
ServerHost string `json:"serverhost"`
}
const loginSuccess = "Login Succeeded"
func RegistryVerify(authInfo AuthInfo) error {
auth := base64.StdEncoding.EncodeToString([]byte(authInfo.Username + ":" + authInfo.Password))
ctx := context.Background()
cli, err := client.NewEnvClient()
if err != nil {
glog.Error(err)
}
config := types.AuthConfig{
Username: authInfo.Username,
Password: authInfo.Password,
Auth: auth,
ServerAddress: authInfo.ServerHost,
}
resp, err := cli.RegistryLogin(ctx, config)
cli.Close()
if err != nil {
return err
}
if resp.Status == loginSuccess {
return nil
} else {
return errors.New(errors.VerifyFailed, resp.Status)
}
}

View File

@@ -0,0 +1,121 @@
/*
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 (
"sort"
"strings"
rbac "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/listers/rbac/v1"
)
type clusterRoleSearcher struct {
clusterRoleLister v1.ClusterRoleLister
}
// exactly match
func (*clusterRoleSearcher) match(match map[string]string, item *rbac.ClusterRole) bool {
for k, v := range match {
switch k {
case name:
if item.Name != v && item.Labels[displayName] != v {
return false
}
default:
return false
}
}
return true
}
// fuzzy searchInNamespace
func (*clusterRoleSearcher) fuzzy(fuzzy map[string]string, item *rbac.ClusterRole) bool {
for k, v := range fuzzy {
switch k {
case name:
if !strings.Contains(item.Name, v) && !strings.Contains(item.Labels[displayName], v) {
return false
}
case label:
if !searchFuzzy(item.Labels, "", v) {
return false
}
case annotation:
if !searchFuzzy(item.Annotations, "", v) {
return false
}
return false
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
}
}
}
return true
}
func (*clusterRoleSearcher) compare(a, b *rbac.ClusterRole, orderBy string) bool {
switch orderBy {
case createTime:
return a.CreationTimestamp.Time.After(b.CreationTimestamp.Time)
case name:
fallthrough
default:
return strings.Compare(a.Name, b.Name) <= 0
}
}
func (s *clusterRoleSearcher) search(conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
clusterRoles, err := s.clusterRoleLister.List(labels.Everything())
if err != nil {
return nil, err
}
result := make([]*rbac.ClusterRole, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
result = clusterRoles
} else {
for _, item := range clusterRoles {
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 {
tmp := i
i = j
j = tmp
}
return s.compare(result[i], result[j], orderBy)
})
r := make([]interface{}, 0)
for _, i := range result {
r = append(r, i)
}
return r, nil
}

View File

@@ -0,0 +1,126 @@
/*
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 (
"sort"
"strings"
lister "k8s.io/client-go/listers/core/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
)
type configMapSearcher struct {
configMapLister lister.ConfigMapLister
}
// exactly match
func (*configMapSearcher) match(match map[string]string, item *v1.ConfigMap) bool {
for k, v := range match {
switch k {
case name:
if item.Name != v && item.Labels[displayName] != v {
return false
}
default:
return false
}
}
return true
}
// fuzzy searchInNamespace
func (*configMapSearcher) fuzzy(fuzzy map[string]string, item *v1.ConfigMap) bool {
for k, v := range fuzzy {
switch k {
case name:
if !strings.Contains(item.Name, v) && !strings.Contains(item.Labels[displayName], v) {
return false
}
case label:
if !searchFuzzy(item.Labels, "", v) {
return false
}
case annotation:
if !searchFuzzy(item.Annotations, "", v) {
return false
}
return false
case app:
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
}
}
}
return true
}
func (*configMapSearcher) compare(a, b *v1.ConfigMap, orderBy string) bool {
switch orderBy {
case createTime:
return a.CreationTimestamp.Time.After(b.CreationTimestamp.Time)
case name:
fallthrough
default:
return strings.Compare(a.Name, b.Name) <= 0
}
}
func (s *configMapSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
configMaps, err := s.configMapLister.ConfigMaps(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
result := make([]*v1.ConfigMap, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
result = configMaps
} else {
for _, item := range configMaps {
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 {
tmp := i
i = j
j = tmp
}
return s.compare(result[i], result[j], orderBy)
})
r := make([]interface{}, 0)
for _, i := range result {
r = append(r, i)
}
return r, nil
}

View File

@@ -0,0 +1,134 @@
/*
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 (
"sort"
"strings"
lister "k8s.io/client-go/listers/batch/v2alpha1"
"k8s.io/api/batch/v2alpha1"
"k8s.io/apimachinery/pkg/labels"
)
type cronJobSearcher struct {
cronJobLister lister.CronJobLister
}
func cronJobStatus(item *v2alpha1.CronJob) string {
if item.Spec.Suspend != nil && *item.Spec.Suspend {
return paused
}
return running
}
// Exactly match
func (*cronJobSearcher) match(match map[string]string, item *v2alpha1.CronJob) bool {
for k, v := range match {
switch k {
case status:
if cronJobStatus(item) != v {
return false
}
default:
return false
}
}
return true
}
func (*cronJobSearcher) fuzzy(fuzzy map[string]string, item *v2alpha1.CronJob) bool {
for k, v := range fuzzy {
switch k {
case name:
if !strings.Contains(item.Name, v) && !strings.Contains(item.Labels[displayName], v) {
return false
}
case label:
if !searchFuzzy(item.Labels, "", v) {
return false
}
case annotation:
if !searchFuzzy(item.Annotations, "", v) {
return false
}
return false
case app:
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
}
}
}
return true
}
func (*cronJobSearcher) compare(a, b *v2alpha1.CronJob, orderBy string) bool {
switch orderBy {
case createTime:
return a.CreationTimestamp.Time.After(b.CreationTimestamp.Time)
case name:
fallthrough
default:
return strings.Compare(a.Name, b.Name) <= 0
}
}
func (s *cronJobSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
cronJobs, err := s.cronJobLister.CronJobs(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
result := make([]*v2alpha1.CronJob, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
result = cronJobs
} else {
for _, item := range cronJobs {
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 {
tmp := i
i = j
j = tmp
}
return s.compare(result[i], result[j], orderBy)
})
r := make([]interface{}, 0)
for _, i := range result {
r = append(r, i)
}
return r, nil
}

View File

@@ -0,0 +1,137 @@
/*
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 (
"sort"
"strings"
lister "k8s.io/client-go/listers/apps/v1"
"k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/labels"
)
type daemonSetSearcher struct {
daemonSetLister lister.DaemonSetLister
}
func daemonSetStatus(item *v1.DaemonSet) string {
if item.Status.NumberAvailable == 0 {
return stopped
} else if item.Status.DesiredNumberScheduled == item.Status.NumberAvailable {
return running
} else {
return updating
}
}
// Exactly match
func (*daemonSetSearcher) match(match map[string]string, item *v1.DaemonSet) bool {
for k, v := range match {
switch k {
case status:
if daemonSetStatus(item) != v {
return false
}
default:
return false
}
}
return true
}
func (*daemonSetSearcher) fuzzy(fuzzy map[string]string, item *v1.DaemonSet) bool {
for k, v := range fuzzy {
switch k {
case name:
if !strings.Contains(item.Name, v) && !strings.Contains(item.Labels[displayName], v) {
return false
}
case label:
if !searchFuzzy(item.Labels, "", v) {
return false
}
case annotation:
if !searchFuzzy(item.Annotations, "", v) {
return false
}
return false
case app:
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
}
}
}
return true
}
func (*daemonSetSearcher) compare(a, b *v1.DaemonSet, orderBy string) bool {
switch orderBy {
case createTime:
return a.CreationTimestamp.Time.After(b.CreationTimestamp.Time)
case name:
fallthrough
default:
return strings.Compare(a.Name, b.Name) <= 0
}
}
func (s *daemonSetSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
daemonSets, err := s.daemonSetLister.DaemonSets(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
result := make([]*v1.DaemonSet, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
result = daemonSets
} else {
for _, item := range daemonSets {
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 {
tmp := i
i = j
j = tmp
}
return s.compare(result[i], result[j], orderBy)
})
r := make([]interface{}, 0)
for _, i := range result {
r = append(r, i)
}
return r, nil
}

View File

@@ -0,0 +1,141 @@
/*
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 (
"sort"
"strings"
lister "k8s.io/client-go/listers/apps/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/api/apps/v1"
)
type deploymentSearcher struct {
deploymentLister lister.DeploymentLister
}
func deploymentStatus(item *v1.Deployment) string {
if item.Spec.Replicas != nil {
if item.Status.ReadyReplicas == 0 && *item.Spec.Replicas == 0 {
return stopped
} else if item.Status.ReadyReplicas == *item.Spec.Replicas {
return running
} else {
return updating
}
}
return stopped
}
// Exactly match
func (*deploymentSearcher) match(match map[string]string, item *v1.Deployment) bool {
for k, v := range match {
switch k {
case status:
if deploymentStatus(item) != v {
return false
}
default:
return false
}
}
return true
}
func (*deploymentSearcher) fuzzy(fuzzy map[string]string, item *v1.Deployment) bool {
for k, v := range fuzzy {
switch k {
case name:
if !strings.Contains(item.Name, v) && !strings.Contains(item.Labels[displayName], v) {
return false
}
case label:
if !searchFuzzy(item.Labels, "", v) {
return false
}
case annotation:
if !searchFuzzy(item.Annotations, "", v) {
return false
}
return false
case app:
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
}
}
}
return true
}
func (*deploymentSearcher) compare(a, b *v1.Deployment, orderBy string) bool {
switch orderBy {
case createTime:
return a.CreationTimestamp.Time.After(b.CreationTimestamp.Time)
case name:
fallthrough
default:
return strings.Compare(a.Name, b.Name) <= 0
}
}
func (s *deploymentSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
deployments, err := s.deploymentLister.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 {
tmp := i
i = j
j = tmp
}
return s.compare(result[i], result[j], orderBy)
})
r := make([]interface{}, 0)
for _, i := range result {
r = append(r, i)
}
return r, nil
}

View File

@@ -0,0 +1,127 @@
/*
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 (
"sort"
"strings"
lister "k8s.io/client-go/listers/extensions/v1beta1"
extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/labels"
)
type ingressSearcher struct {
ingressLister lister.IngressLister
}
// exactly match
func (*ingressSearcher) match(match map[string]string, item *extensions.Ingress) bool {
for k, v := range match {
switch k {
case name:
if item.Name != v && item.Labels[displayName] != v {
return false
}
default:
return false
}
}
return true
}
// fuzzy searchInNamespace
func (*ingressSearcher) fuzzy(fuzzy map[string]string, item *extensions.Ingress) bool {
for k, v := range fuzzy {
switch k {
case name:
if !strings.Contains(item.Name, v) && !strings.Contains(item.Labels[displayName], v) {
return false
}
case label:
if !searchFuzzy(item.Labels, "", v) {
return false
}
case annotation:
if !searchFuzzy(item.Annotations, "", v) {
return false
}
return false
case app:
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
}
}
}
return true
}
func (*ingressSearcher) compare(a, b *extensions.Ingress, orderBy string) bool {
switch orderBy {
case createTime:
return a.CreationTimestamp.Time.After(b.CreationTimestamp.Time)
case name:
fallthrough
default:
return strings.Compare(a.Name, b.Name) <= 0
}
}
func (s *ingressSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
ingresses, err := s.ingressLister.Ingresses(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
result := make([]*extensions.Ingress, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
result = ingresses
} else {
for _, item := range ingresses {
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 {
tmp := i
i = j
j = tmp
}
return s.compare(result[i], result[j], orderBy)
})
r := make([]interface{}, 0)
for _, i := range result {
r = append(r, i)
}
return r, nil
}

View File

@@ -0,0 +1,156 @@
/*
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 (
"sort"
"strings"
"time"
lister "k8s.io/client-go/listers/batch/v1"
batchV1 "k8s.io/api/batch/v1"
coreV1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
)
type jobSearcher struct {
jobLister lister.JobLister
}
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
}
}
return status
}
// Exactly match
func (*jobSearcher) match(match map[string]string, item *batchV1.Job) bool {
for k, v := range match {
switch k {
case status:
if jobStatus(item) != v {
return false
}
default:
return false
}
}
return true
}
func (*jobSearcher) fuzzy(fuzzy map[string]string, item *batchV1.Job) bool {
for k, v := range fuzzy {
switch k {
case name:
if !strings.Contains(item.Name, v) && !strings.Contains(item.Labels[displayName], v) {
return false
}
case label:
if !searchFuzzy(item.Labels, "", v) {
return false
}
case annotation:
if !searchFuzzy(item.Annotations, "", v) {
return false
}
return false
case app:
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
}
}
}
return true
}
func jobUpdateTime(item *batchV1.Job) time.Time {
updateTime := item.CreationTimestamp.Time
for _, condition := range item.Status.Conditions {
if updateTime.Before(condition.LastProbeTime.Time) {
updateTime = condition.LastProbeTime.Time
}
if updateTime.Before(condition.LastTransitionTime.Time) {
updateTime = condition.LastTransitionTime.Time
}
}
return updateTime
}
func (*jobSearcher) compare(a, b *batchV1.Job, orderBy string) bool {
switch orderBy {
case updateTime:
return jobUpdateTime(a).After(jobUpdateTime(b))
case name:
fallthrough
default:
return strings.Compare(a.Name, b.Name) <= 0
}
}
func (s *jobSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
jobs, err := s.jobLister.Jobs(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
result := make([]*batchV1.Job, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
result = jobs
} else {
for _, item := range jobs {
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 {
tmp := i
i = j
j = tmp
}
return s.compare(result[i], result[j], orderBy)
})
r := make([]interface{}, 0)
for _, i := range result {
r = append(r, i)
}
return r, nil
}

View File

@@ -0,0 +1,125 @@
/*
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 (
"sort"
"strings"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
lister "k8s.io/client-go/listers/core/v1"
)
type namespaceSearcher struct {
namespaceLister lister.NamespaceLister
}
// exactly match
func (*namespaceSearcher) match(match map[string]string, item *v1.Namespace) bool {
for k, v := range match {
switch k {
case name:
if item.Name != v && item.Labels[displayName] != v {
return false
}
default:
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 name:
if !strings.Contains(item.Name, v) && !strings.Contains(item.Labels[displayName], v) {
return false
}
case label:
if !searchFuzzy(item.Labels, "", v) {
return false
}
case annotation:
if !searchFuzzy(item.Annotations, "", v) {
return false
}
return false
case app:
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
}
}
}
return true
}
func (*namespaceSearcher) compare(a, b *v1.Namespace, orderBy string) bool {
switch orderBy {
case createTime:
return a.CreationTimestamp.Time.After(b.CreationTimestamp.Time)
case name:
fallthrough
default:
return strings.Compare(a.Name, b.Name) <= 0
}
}
func (s *namespaceSearcher) search(conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
namespaces, err := s.namespaceLister.List(labels.Everything())
if err != nil {
return nil, err
}
result := make([]*v1.Namespace, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
result = namespaces
} else {
for _, item := range namespaces {
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 {
tmp := i
i = j
j = tmp
}
return s.compare(result[i], result[j], orderBy)
})
r := make([]interface{}, 0)
for _, i := range result {
r = append(r, i)
}
return r, nil
}

View File

@@ -0,0 +1,125 @@
/*
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 (
"sort"
"strings"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
lister "k8s.io/client-go/listers/core/v1"
)
type nodeSearcher struct {
nodeLister lister.NodeLister
}
// exactly match
func (*nodeSearcher) match(match map[string]string, item *v1.Node) bool {
for k, v := range match {
switch k {
case name:
if item.Name != v && item.Labels[displayName] != v {
return false
}
default:
return false
}
}
return true
}
// fuzzy searchInNamespace
func (*nodeSearcher) fuzzy(fuzzy map[string]string, item *v1.Node) bool {
for k, v := range fuzzy {
switch k {
case name:
if !strings.Contains(item.Name, v) && !strings.Contains(item.Labels[displayName], v) {
return false
}
case label:
if !searchFuzzy(item.Labels, "", v) {
return false
}
case annotation:
if !searchFuzzy(item.Annotations, "", v) {
return false
}
return false
case app:
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
}
}
}
return true
}
func (*nodeSearcher) compare(a, b *v1.Node, orderBy string) bool {
switch orderBy {
case createTime:
return a.CreationTimestamp.Time.After(b.CreationTimestamp.Time)
case name:
fallthrough
default:
return strings.Compare(a.Name, b.Name) <= 0
}
}
func (s *nodeSearcher) search(conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
nodes, err := s.nodeLister.List(labels.Everything())
if err != nil {
return nil, err
}
result := make([]*v1.Node, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
result = nodes
} else {
for _, item := range nodes {
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 {
tmp := i
i = j
j = tmp
}
return s.compare(result[i], result[j], orderBy)
})
r := make([]interface{}, 0)
for _, i := range result {
r = append(r, i)
}
return r, nil
}

View File

@@ -0,0 +1,126 @@
/*
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 (
"sort"
"strings"
lister "k8s.io/client-go/listers/core/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
)
type persistentVolumeClaimSearcher struct {
persistentVolumeClaimLister lister.PersistentVolumeClaimLister
}
// exactly match
func (*persistentVolumeClaimSearcher) match(match map[string]string, item *v1.PersistentVolumeClaim) bool {
for k, v := range match {
switch k {
case name:
if item.Name != v && item.Labels[displayName] != v {
return false
}
default:
return false
}
}
return true
}
// fuzzy searchInNamespace
func (*persistentVolumeClaimSearcher) fuzzy(fuzzy map[string]string, item *v1.PersistentVolumeClaim) bool {
for k, v := range fuzzy {
switch k {
case name:
if !strings.Contains(item.Name, v) && !strings.Contains(item.Labels[displayName], v) {
return false
}
case label:
if !searchFuzzy(item.Labels, "", v) {
return false
}
case annotation:
if !searchFuzzy(item.Annotations, "", v) {
return false
}
return false
case app:
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
}
}
}
return true
}
func (*persistentVolumeClaimSearcher) compare(a, b *v1.PersistentVolumeClaim, orderBy string) bool {
switch orderBy {
case createTime:
return a.CreationTimestamp.Time.After(b.CreationTimestamp.Time)
case name:
fallthrough
default:
return strings.Compare(a.Name, b.Name) <= 0
}
}
func (s *persistentVolumeClaimSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
persistentVolumeClaims, err := s.persistentVolumeClaimLister.PersistentVolumeClaims(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
result := make([]*v1.PersistentVolumeClaim, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
result = persistentVolumeClaims
} else {
for _, item := range persistentVolumeClaims {
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 {
tmp := i
i = j
j = tmp
}
return s.compare(result[i], result[j], orderBy)
})
r := make([]interface{}, 0)
for _, i := range result {
r = append(r, i)
}
return r, nil
}

View File

@@ -0,0 +1,127 @@
/*
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 (
"sort"
"strings"
v12 "k8s.io/client-go/listers/core/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
)
type podSearcher struct {
podLister v12.PodLister
}
// exactly match
func (*podSearcher) match(match map[string]string, item *v1.Pod) bool {
for k, v := range match {
switch k {
case name:
if item.Name != v && item.Labels[displayName] != v {
return false
}
default:
return false
}
}
return true
}
// fuzzy searchInNamespace
func (*podSearcher) fuzzy(fuzzy map[string]string, item *v1.Pod) bool {
for k, v := range fuzzy {
switch k {
case name:
if !strings.Contains(item.Name, v) && !strings.Contains(item.Labels[displayName], v) {
return false
}
case label:
if !searchFuzzy(item.Labels, "", v) {
return false
}
case annotation:
if !searchFuzzy(item.Annotations, "", v) {
return false
}
return false
case app:
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
}
}
}
return true
}
func (*podSearcher) compare(a, b *v1.Pod, orderBy string) bool {
switch orderBy {
case createTime:
return a.CreationTimestamp.Time.After(b.CreationTimestamp.Time)
case name:
fallthrough
default:
return strings.Compare(a.Name, b.Name) <= 0
}
}
func (s *podSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
pods, err := s.podLister.Pods(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
result := make([]*v1.Pod, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
result = pods
} else {
for _, item := range pods {
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 {
tmp := i
i = j
j = tmp
}
return s.compare(result[i], result[j], orderBy)
})
r := make([]interface{}, 0)
for _, i := range result {
r = append(r, i)
}
return r, nil
}

View File

@@ -0,0 +1,240 @@
/*
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 (
"regexp"
"strings"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/errors"
)
func init() {
namespacedResources[ConfigMaps] = &configMapSearcher{
configMapLister: informers.SharedInformerFactory().Core().V1().ConfigMaps().Lister(),
}
namespacedResources[CronJobs] = &cronJobSearcher{
cronJobLister: informers.SharedInformerFactory().Batch().V2alpha1().CronJobs().Lister(),
}
namespacedResources[DaemonSets] = &daemonSetSearcher{
daemonSetLister: informers.SharedInformerFactory().Apps().V1().DaemonSets().Lister(),
}
namespacedResources[Deployments] = &deploymentSearcher{
deploymentLister: informers.SharedInformerFactory().Apps().V1().Deployments().Lister(),
}
namespacedResources[Ingresses] = &ingressSearcher{
ingressLister: informers.SharedInformerFactory().Extensions().V1beta1().Ingresses().Lister(),
}
namespacedResources[Jobs] = &jobSearcher{
jobLister: informers.SharedInformerFactory().Batch().V1().Jobs().Lister(),
}
namespacedResources[PersistentVolumeClaims] = &persistentVolumeClaimSearcher{
persistentVolumeClaimLister: informers.SharedInformerFactory().Core().V1().PersistentVolumeClaims().Lister(),
}
namespacedResources[Secrets] = &secretSearcher{
secretLister: informers.SharedInformerFactory().Core().V1().Secrets().Lister(),
}
namespacedResources[Services] = &serviceSearcher{
serviceLister: informers.SharedInformerFactory().Core().V1().Services().Lister(),
}
namespacedResources[StatefulSets] = &statefulSetSearcher{
statefulSetLister: informers.SharedInformerFactory().Apps().V1().StatefulSets().Lister(),
}
namespacedResources[Pods] = &podSearcher{
podLister: informers.SharedInformerFactory().Core().V1().Pods().Lister(),
}
namespacedResources[Roles] = &roleSearcher{
roleLister: informers.SharedInformerFactory().Rbac().V1().Roles().Lister(),
}
clusterResources[Nodes] = &nodeSearcher{
nodeLister: informers.SharedInformerFactory().Core().V1().Nodes().Lister(),
}
clusterResources[Namespaces] = &namespaceSearcher{
namespaceLister: informers.SharedInformerFactory().Core().V1().Namespaces().Lister(),
}
clusterResources[ClusterRoles] = &clusterRoleSearcher{
clusterRoleLister: informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister(),
}
clusterResources[StorageClasses] = &storageClassesSearcher{
storageClassesLister: informers.SharedInformerFactory().Storage().V1().StorageClasses().Lister(),
}
}
var namespacedResources = make(map[string]namespacedSearcherInterface)
var clusterResources = make(map[string]clusterSearcherInterface)
type conditions struct {
match map[string]string
fuzzy map[string]string
}
const (
name = "name"
label = "label"
createTime = "createTime"
updateTime = "updateTime"
displayName = "displayName"
chart = "chart"
release = "release"
annotation = "annotation"
keyword = "keyword"
status = "status"
running = "running"
paused = "paused"
updating = "updating"
stopped = "stopped"
failed = "failed"
complete = "complete"
app = "app"
Deployments = "deployments"
DaemonSets = "daemonsets"
Roles = "roles"
CronJobs = "cronjobs"
ConfigMaps = "configmaps"
Ingresses = "ingresses"
Jobs = "jobs"
PersistentVolumeClaims = "persistentvolumeclaims"
Pods = "pods"
Secrets = "secrets"
Services = "services"
StatefulSets = "statefulsets"
Nodes = "nodes"
Namespaces = "namespaces"
StorageClasses = "storageclasses"
ClusterRoles = "clusterroles"
)
type namespacedSearcherInterface interface {
search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error)
}
type clusterSearcherInterface interface {
search(conditions *conditions, orderBy string, reverse bool) ([]interface{}, error)
}
func ListNamespaceResource(namespace, resource, conditionStr, orderBy string, reverse bool, limit, offset int) (*ResourceList, error) {
items := make([]interface{}, 0)
total := 0
var err error
conditions, err := parseToConditions(conditionStr)
if err != nil {
return nil, err
}
var result []interface{}
if searcher, ok := namespacedResources[resource]; ok {
result, err = searcher.search(namespace, conditions, orderBy, reverse)
} else {
return nil, errors.New(errors.NotImplement, "not support")
}
if err != nil {
return nil, errors.New(errors.Internal, err.Error())
}
total = len(result)
for i, d := range result {
if i >= offset && (limit == -1 || len(items) < limit) {
items = append(items, d)
}
}
return &ResourceList{TotalCount: total, Items: items}, nil
}
func ListClusterResource(resource, conditionStr, orderBy string, reverse bool, limit, offset int) (*ResourceList, error) {
items := make([]interface{}, 0)
total := 0
var err error
conditions, err := parseToConditions(conditionStr)
if err != nil {
return nil, err
}
var result []interface{}
if searcher, ok := clusterResources[resource]; ok {
result, err = searcher.search(conditions, orderBy, reverse)
} else {
return nil, errors.New(errors.NotImplement, "not support")
}
if err != nil {
return nil, errors.New(errors.Internal, err.Error())
}
total = len(result)
for i, d := range result {
if i >= offset && len(items) < limit {
items = append(items, d)
}
}
return &ResourceList{TotalCount: total, Items: items}, nil
}
func parseToConditions(str string) (*conditions, error) {
conditions := &conditions{match: make(map[string]string, 0), fuzzy: make(map[string]string, 0)}
if str == "" {
return conditions, nil
}
for _, item := range strings.Split(str, ",") {
if strings.Count(item, "=") > 1 || strings.Count(item, "~") > 1 {
return nil, errors.New(errors.InvalidArgument, "invalid condition")
}
if groups := regexp.MustCompile(`(\S+)([=~])(\S+)`).FindStringSubmatch(item); len(groups) == 4 {
if groups[2] == "=" {
conditions.match[groups[1]] = groups[3]
} else {
conditions.fuzzy[groups[1]] = groups[3]
}
} else {
return nil, errors.New(errors.InvalidArgument, "invalid condition")
}
}
return conditions, nil
}
type ResourceList struct {
TotalCount int `json:"total_count"`
Items []interface{} `json:"items"`
}
func searchFuzzy(m map[string]string, key, value string) bool {
for k, v := range m {
if key == "" {
if strings.Contains(k, value) || strings.Contains(v, value) {
return true
}
} else if k == key && strings.Contains(v, value) {
return true
}
}
return false
}

View File

@@ -0,0 +1,122 @@
/*
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 (
"sort"
"strings"
rbac "k8s.io/api/rbac/v1"
lister "k8s.io/client-go/listers/rbac/v1"
"k8s.io/apimachinery/pkg/labels"
)
type roleSearcher struct {
roleLister lister.RoleLister
}
// exactly match
func (*roleSearcher) match(match map[string]string, item *rbac.Role) bool {
for k, v := range match {
switch k {
case name:
if item.Name != v && item.Labels[displayName] != v {
return false
}
default:
return false
}
}
return true
}
// fuzzy searchInNamespace
func (*roleSearcher) fuzzy(fuzzy map[string]string, item *rbac.Role) bool {
for k, v := range fuzzy {
switch k {
case name:
if !strings.Contains(item.Name, v) && !strings.Contains(item.Labels[displayName], v) {
return false
}
case label:
if !searchFuzzy(item.Labels, "", v) {
return false
}
case annotation:
if !searchFuzzy(item.Annotations, "", v) {
return false
}
return false
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
}
}
}
return true
}
func (*roleSearcher) compare(a, b *rbac.Role, orderBy string) bool {
switch orderBy {
case createTime:
return a.CreationTimestamp.Time.After(b.CreationTimestamp.Time)
case name:
fallthrough
default:
return strings.Compare(a.Name, b.Name) <= 0
}
}
func (s *roleSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
roles, err := s.roleLister.Roles(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
result := make([]*rbac.Role, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
result = roles
} else {
for _, item := range roles {
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 {
tmp := i
i = j
j = tmp
}
return s.compare(result[i], result[j], orderBy)
})
r := make([]interface{}, 0)
for _, i := range result {
r = append(r, i)
}
return r, nil
}

View File

@@ -0,0 +1,130 @@
/*
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 (
"sort"
"strings"
lister "k8s.io/client-go/listers/core/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
)
type secretSearcher struct {
secretLister lister.SecretLister
}
// exactly match
func (*secretSearcher) match(match map[string]string, item *v1.Secret) bool {
for k, v := range match {
switch k {
case name:
if item.Name != v && item.Labels[displayName] != v {
return false
}
case "type":
if string(item.Type) != v {
return false
}
default:
return false
}
}
return true
}
// fuzzy searchInNamespace
func (*secretSearcher) fuzzy(fuzzy map[string]string, item *v1.Secret) bool {
for k, v := range fuzzy {
switch k {
case name:
if !strings.Contains(item.Name, v) && !strings.Contains(item.Labels[displayName], v) {
return false
}
case label:
if !searchFuzzy(item.Labels, "", v) {
return false
}
case annotation:
if !searchFuzzy(item.Annotations, "", v) {
return false
}
return false
case app:
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
}
}
}
return true
}
func (*secretSearcher) compare(a, b *v1.Secret, orderBy string) bool {
switch orderBy {
case createTime:
return a.CreationTimestamp.Time.After(b.CreationTimestamp.Time)
case name:
fallthrough
default:
return strings.Compare(a.Name, b.Name) <= 0
}
}
func (s *secretSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
secrets, err := s.secretLister.Secrets(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
result := make([]*v1.Secret, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
result = secrets
} else {
for _, item := range secrets {
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 {
tmp := i
i = j
j = tmp
}
return s.compare(result[i], result[j], orderBy)
})
r := make([]interface{}, 0)
for _, i := range result {
r = append(r, i)
}
return r, nil
}

View File

@@ -0,0 +1,126 @@
/*
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 (
"sort"
"strings"
lister "k8s.io/client-go/listers/core/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
)
type serviceSearcher struct {
serviceLister lister.ServiceLister
}
// exactly match
func (*serviceSearcher) match(match map[string]string, item *v1.Service) bool {
for k, v := range match {
switch k {
case name:
if item.Name != v && item.Labels[displayName] != v {
return false
}
default:
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 name:
if !strings.Contains(item.Name, v) && !strings.Contains(item.Labels[displayName], v) {
return false
}
case label:
if !searchFuzzy(item.Labels, "", v) {
return false
}
case annotation:
if !searchFuzzy(item.Annotations, "", v) {
return false
}
return false
case app:
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
}
}
}
return true
}
func (*serviceSearcher) compare(a, b *v1.Service, orderBy string) bool {
switch orderBy {
case createTime:
return a.CreationTimestamp.Time.After(b.CreationTimestamp.Time)
case name:
fallthrough
default:
return strings.Compare(a.Name, b.Name) <= 0
}
}
func (s *serviceSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
services, err := s.serviceLister.Services(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
result := make([]*v1.Service, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
result = services
} else {
for _, item := range services {
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 {
tmp := i
i = j
j = tmp
}
return s.compare(result[i], result[j], orderBy)
})
r := make([]interface{}, 0)
for _, i := range result {
r = append(r, i)
}
return r, nil
}

View File

@@ -0,0 +1,140 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package resources
import (
"sort"
"strings"
lister "k8s.io/client-go/listers/apps/v1"
"k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/labels"
)
type statefulSetSearcher struct {
statefulSetLister lister.StatefulSetLister
}
func statefulSetStatus(item *v1.StatefulSet) string {
if item.Spec.Replicas != nil {
if item.Status.ReadyReplicas == 0 && *item.Spec.Replicas == 0 {
return stopped
} else if item.Status.ReadyReplicas == *item.Spec.Replicas {
return running
} else {
return updating
}
}
return stopped
}
// Exactly match
func (*statefulSetSearcher) match(match map[string]string, item *v1.StatefulSet) bool {
for k, v := range match {
switch k {
case status:
if statefulSetStatus(item) != v {
return false
}
default:
return false
}
}
return true
}
func (*statefulSetSearcher) fuzzy(fuzzy map[string]string, item *v1.StatefulSet) bool {
for k, v := range fuzzy {
switch k {
case name:
if !strings.Contains(item.Name, v) && !strings.Contains(item.Labels[displayName], v) {
return false
}
case label:
if !searchFuzzy(item.Labels, "", v) {
return false
}
case annotation:
if !searchFuzzy(item.Annotations, "", v) {
return false
}
return false
case app:
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
}
}
}
return true
}
func (*statefulSetSearcher) compare(a, b *v1.StatefulSet, orderBy string) bool {
switch orderBy {
case createTime:
return a.CreationTimestamp.Time.After(b.CreationTimestamp.Time)
case name:
fallthrough
default:
return strings.Compare(a.Name, b.Name) <= 0
}
}
func (s *statefulSetSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
statefulSets, err := s.statefulSetLister.StatefulSets(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
result := make([]*v1.StatefulSet, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
result = statefulSets
} else {
for _, item := range statefulSets {
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 {
tmp := i
i = j
j = tmp
}
return s.compare(result[i], result[j], orderBy)
})
r := make([]interface{}, 0)
for _, i := range result {
r = append(r, i)
}
return r, nil
}

View File

@@ -0,0 +1,121 @@
/*
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 (
"sort"
"strings"
"k8s.io/api/storage/v1"
"k8s.io/apimachinery/pkg/labels"
lister "k8s.io/client-go/listers/storage/v1"
)
type storageClassesSearcher struct {
storageClassesLister lister.StorageClassLister
}
// exactly match
func (*storageClassesSearcher) match(match map[string]string, item *v1.StorageClass) bool {
for k, v := range match {
switch k {
case name:
if item.Name != v && item.Labels[displayName] != v {
return false
}
default:
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 name:
if !strings.Contains(item.Name, v) && !strings.Contains(item.Labels[displayName], v) {
return false
}
case label:
if !searchFuzzy(item.Labels, "", v) {
return false
}
case annotation:
if !searchFuzzy(item.Annotations, "", v) {
return false
}
return false
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
}
}
}
return true
}
func (*storageClassesSearcher) compare(a, b *v1.StorageClass, orderBy string) bool {
switch orderBy {
case createTime:
return a.CreationTimestamp.Time.After(b.CreationTimestamp.Time)
case name:
fallthrough
default:
return strings.Compare(a.Name, b.Name) <= 0
}
}
func (s *storageClassesSearcher) search(conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
storageClasses, err := s.storageClassesLister.List(labels.Everything())
if err != nil {
return nil, err
}
result := make([]*v1.StorageClass, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
result = storageClasses
} else {
for _, item := range storageClasses {
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 {
tmp := i
i = j
j = tmp
}
return s.compare(result[i], result[j], orderBy)
})
r := make([]interface{}, 0)
for _, i := range result {
r = append(r, i)
}
return r, nil
}

View File

@@ -0,0 +1,114 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package revisions
import (
"fmt"
"github.com/golang/glog"
"k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/labels"
lister "k8s.io/client-go/listers/apps/v1"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/informers"
)
var (
daemonSetLister lister.DaemonSetLister
deploymentLister lister.DeploymentLister
replicaSetLister lister.ReplicaSetLister
statefulSetLister lister.StatefulSetLister
controllerRevisionLister lister.ControllerRevisionLister
)
func init() {
daemonSetLister = informers.SharedInformerFactory().Apps().V1().DaemonSets().Lister()
deploymentLister = informers.SharedInformerFactory().Apps().V1().Deployments().Lister()
replicaSetLister = informers.SharedInformerFactory().Apps().V1().ReplicaSets().Lister()
statefulSetLister = informers.SharedInformerFactory().Apps().V1().StatefulSets().Lister()
controllerRevisionLister = informers.SharedInformerFactory().Apps().V1().ControllerRevisions().Lister()
}
func GetDeployRevision(namespace, name, revision string) (*v1.ReplicaSet, error) {
deploy, err := deploymentLister.Deployments(namespace).Get(name)
if err != nil {
glog.Errorf("get deployment %s failed, reason: %s", name, err)
return nil, err
}
labelMap := deploy.Spec.Template.Labels
labelSelector := labels.Set(labelMap).AsSelector()
rsList, err := replicaSetLister.ReplicaSets(namespace).List(labelSelector)
if err != nil {
return nil, err
}
for _, rs := range rsList {
if rs.Annotations["deployment.kubernetes.io/revision"] == revision {
return rs, nil
}
}
return nil, errors.New(errors.NotFound, fmt.Sprintf("revision not found %v#%v", name, revision))
}
func GetDaemonSetRevision(namespace, name string, revisionInt int) (*v1.ControllerRevision, error) {
ds, err := daemonSetLister.DaemonSets(namespace).Get(name)
if err != nil {
return nil, err
}
labels := ds.Spec.Template.Labels
return getControllerRevision(namespace, name, labels, revisionInt)
}
func GetStatefulSetRevision(namespace, name string, revisionInt int) (*v1.ControllerRevision, error) {
st, err := statefulSetLister.StatefulSets(namespace).Get(name)
if err != nil {
return nil, err
}
return getControllerRevision(namespace, name, st.Spec.Template.Labels, revisionInt)
}
func getControllerRevision(namespace, name string, labelMap map[string]string, revision int) (*v1.ControllerRevision, error) {
labelSelector := labels.Set(labelMap).AsSelector()
revisions, err := controllerRevisionLister.ControllerRevisions(namespace).List(labelSelector)
if err != nil {
return nil, err
}
for _, controllerRevision := range revisions {
if controllerRevision.Revision == int64(revision) {
return controllerRevision, nil
}
}
return nil, errors.New(errors.NotFound, fmt.Sprintf("revision not found %v#%v", name, revision))
}

View File

@@ -0,0 +1,327 @@
/*
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 routers
import (
"fmt"
"io/ioutil"
"k8s.io/apimachinery/pkg/labels"
v12 "k8s.io/client-go/listers/core/v1"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/informers"
"github.com/golang/glog"
coreV1 "k8s.io/api/core/v1"
extensionsV1beta1 "k8s.io/api/extensions/v1beta1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/api/rbac/v1"
"strings"
"kubesphere.io/kubesphere/pkg/client"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models/iam"
)
var (
serviceLister v12.ServiceLister
)
func init() {
serviceLister = informers.SharedInformerFactory().Core().V1().Services().Lister()
}
func GetAllRouters() ([]*coreV1.Service, error) {
selector := labels.SelectorFromSet(labels.Set{"app": "kubesphere", "component": "ks-router", "tier": "backend"})
services, err := serviceLister.Services(constants.IngressControllerNamespace).List(selector)
if err != nil {
glog.Error(err)
return nil, err
}
return services, nil
}
func GetAllRoutersOfUser(username string) ([]*coreV1.Service, error) {
allNamespace, namespaces, err := iam.GetUserNamespaces(username, v1.PolicyRule{
Verbs: []string{"get", "list"},
APIGroups: []string{""},
Resources: []string{"services"},
})
// return by cluster role
if err != nil {
glog.Error(err)
return nil, err
}
if allNamespace {
return GetAllRouters()
}
routers := make([]*coreV1.Service, 0)
for _, namespace := range namespaces {
router, err := GetRouter(namespace)
if err != nil {
glog.Error(err)
return routers, err
} else if router != nil {
routers = append(routers, router)
}
}
return routers, nil
}
// Get router from a namespace
func GetRouter(namespace string) (*coreV1.Service, error) {
serviceName := constants.IngressControllerPrefix + namespace
selector := labels.SelectorFromSet(labels.Set{"app": "kubesphere", "component": "ks-router", "tier": "backend", "project": namespace})
services, err := serviceLister.Services(constants.IngressControllerNamespace).List(selector)
if err != nil {
glog.Error(err)
return nil, err
}
for _, s := range services {
if s.Name == serviceName {
return s, nil
}
}
return nil, errors.New(errors.NotFound, fmt.Sprintf("resources not found %s", serviceName))
}
// Load all resource yamls
func LoadYamls() ([]string, error) {
var yamls []string
files, err := ioutil.ReadDir(constants.IngressControllerFolder)
if err != nil {
glog.Error(err)
return nil, err
}
for _, file := range files {
if file.IsDir() || !strings.HasSuffix(file.Name(), ".yaml") {
continue
}
content, err := ioutil.ReadFile(constants.IngressControllerFolder + "/" + file.Name())
if err != nil {
glog.Error(err)
return nil, err
} else {
yamls = append(yamls, string(content))
}
}
return yamls, nil
}
// Create a ingress controller in a namespace
func CreateRouter(namespace string, routerType coreV1.ServiceType, annotations map[string]string) (*coreV1.Service, error) {
k8sClient := client.K8sClient()
var router *coreV1.Service
yamls, err := LoadYamls()
if err != nil {
glog.Error(err)
}
for _, f := range yamls {
decode := scheme.Codecs.UniversalDeserializer().Decode
obj, _, err := decode([]byte(f), nil, nil)
if err != nil {
glog.Error(err)
return router, err
}
switch obj.(type) {
case *coreV1.Service:
service := obj.(*coreV1.Service)
service.SetAnnotations(annotations)
service.Spec.Type = routerType
service.Name = constants.IngressControllerPrefix + namespace
// Add project selector
service.Labels["project"] = namespace
service.Spec.Selector["project"] = namespace
service, err := k8sClient.CoreV1().Services(constants.IngressControllerNamespace).Create(service)
if err != nil {
glog.Error(err)
return nil, err
}
router = service
case *extensionsV1beta1.Deployment:
deployment := obj.(*extensionsV1beta1.Deployment)
deployment.Name = constants.IngressControllerPrefix + namespace
// Add project label
deployment.Spec.Selector.MatchLabels["project"] = namespace
deployment.Spec.Template.Labels["project"] = namespace
// Isolate namespace
deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, "--watch-namespace="+namespace)
// Choose self as master
deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, "--election-id="+deployment.Name)
if routerType == coreV1.ServiceTypeLoadBalancer {
deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, "--publish-service="+constants.IngressControllerNamespace+"/"+constants.IngressControllerPrefix+namespace)
} else {
deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, "--report-node-internal-ip-address")
}
deployment, err := k8sClient.ExtensionsV1beta1().Deployments(constants.IngressControllerNamespace).Create(deployment)
if err != nil {
glog.Error(err)
}
default:
//glog.Info("Default resource")
}
}
return router, nil
}
// DeleteRouter is used to delete ingress controller related resources in namespace
// It will not delete ClusterRole resource cause it maybe used by other controllers
func DeleteRouter(namespace string) (*coreV1.Service, error) {
k8sClient := client.K8sClient()
var err error
var router *coreV1.Service
if err != nil {
glog.Error(err)
}
// delete controller service
serviceName := constants.IngressControllerPrefix + namespace
deleteOptions := metaV1.DeleteOptions{}
listOptions := metaV1.ListOptions{
LabelSelector: "app=kubesphere,component=ks-router,tier=backend,project=" + namespace,
FieldSelector: "metadata.name=" + serviceName}
serviceList, err := k8sClient.CoreV1().Services(constants.IngressControllerNamespace).List(listOptions)
if err != nil {
glog.Error(err)
}
if len(serviceList.Items) > 0 {
router = &serviceList.Items[0]
err = k8sClient.CoreV1().Services(constants.IngressControllerNamespace).Delete(serviceName, &deleteOptions)
if err != nil {
glog.Error(err)
}
}
// delete controller deployment
deploymentName := constants.IngressControllerPrefix + namespace
listOptions = metaV1.ListOptions{
LabelSelector: "app=kubesphere,component=ks-router,tier=backend,project=" + namespace,
}
deployments, err := k8sClient.ExtensionsV1beta1().Deployments(constants.IngressControllerNamespace).List(listOptions)
if err != nil {
glog.Error(err)
}
if len(deployments.Items) > 0 {
err = k8sClient.ExtensionsV1beta1().Deployments(constants.IngressControllerNamespace).Delete(deploymentName, &deleteOptions)
if err != nil {
glog.Error(err)
}
}
return router, nil
}
// Update Ingress Controller Service, change type from NodePort to Loadbalancer or vice versa.
func UpdateRouter(namespace string, routerType coreV1.ServiceType, annotations map[string]string) (*coreV1.Service, error) {
k8sClient := client.K8sClient()
var router *coreV1.Service
router, err := GetRouter(namespace)
if err != nil {
glog.Error(err)
return router, nil
}
if router == nil {
glog.Error("Trying to update a non-existed router")
return nil, errors.New(errors.Internal, "router not created yet")
}
// from LoadBalancer to NodePort, or vice-versa
if router.Spec.Type != routerType {
router, err = DeleteRouter(namespace)
if err != nil {
glog.Error(err)
}
router, err = CreateRouter(namespace, routerType, annotations)
if err != nil {
glog.Error(err)
}
return router, err
} else {
router.SetAnnotations(annotations)
router, err = k8sClient.CoreV1().Services(constants.IngressControllerNamespace).Update(router)
if err != nil {
glog.Error(err)
return router, err
}
}
return router, nil
}

View File

@@ -0,0 +1,57 @@
/*
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 status
import (
"fmt"
"kubesphere.io/kubesphere/pkg/models/resources"
)
type workLoadStatus struct {
Namespace string `json:"namespace"`
Count map[string]int `json:"data"`
Items map[string]interface{} `json:"items,omitempty"`
}
func GetNamespacesResourceStatus(namespace string) (*workLoadStatus, error) {
res := workLoadStatus{Count: make(map[string]int), Namespace: namespace, Items: make(map[string]interface{})}
var notReadyList *resources.ResourceList
var err error
for _, resource := range []string{resources.Deployments, resources.StatefulSets, resources.DaemonSets, resources.PersistentVolumeClaims} {
notReadyStatus := "updating"
if resource == resources.PersistentVolumeClaims {
notReadyStatus = "pending"
}
notReadyList, err = resources.ListNamespaceResource(namespace, resource, fmt.Sprintf("status=%s", notReadyStatus), "", false, -1, 0)
if err != nil {
return nil, err
}
res.Count[resource] = notReadyList.TotalCount
}
return &res, nil
}
func GetClusterResourceStatus() (*workLoadStatus, error) {
return GetNamespacesResourceStatus("")
}

View File

@@ -0,0 +1,112 @@
/*
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 storage
import (
"strconv"
"k8s.io/api/core/v1"
storageV1 "k8s.io/api/storage/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/labels"
lister "k8s.io/client-go/listers/core/v1"
lister2 "k8s.io/client-go/listers/storage/v1"
"kubesphere.io/kubesphere/pkg/informers"
)
type ScMetrics struct {
Capacity string `json:"capacity,omitempty"`
Usage string `json:"usage,omitempty"`
PvcNumber string `json:"pvcNumber"`
}
var (
persistentVolumeClaimLister lister.PersistentVolumeClaimLister
persistentVolumeLister lister.PersistentVolumeLister
sotrageClassesLister lister2.StorageClassLister
)
func init() {
persistentVolumeClaimLister = informers.SharedInformerFactory().Core().V1().PersistentVolumeClaims().Lister()
persistentVolumeLister = informers.SharedInformerFactory().Core().V1().PersistentVolumes().Lister()
}
func GetPvcListBySc(scName string) ([]*v1.PersistentVolumeClaim, error) {
all, err := persistentVolumeClaimLister.List(labels.Everything())
if err != nil {
return nil, err
}
result := make([]*v1.PersistentVolumeClaim, 0)
for _, item := range all {
if item.Spec.StorageClassName != nil {
if *item.Spec.StorageClassName == scName {
result = append(result, item.DeepCopy())
}
} else if item.GetAnnotations()[v1.BetaStorageClassAnnotation] == scName {
result = append(result, item.DeepCopy())
}
}
return result, nil
}
// Get info of metrics
func GetScMetrics(scName string) (*ScMetrics, error) {
pvList, err := persistentVolumeLister.List(labels.Everything())
if err != nil {
return nil, err
}
// Get PVC
pvcList, err := GetPvcListBySc(scName)
if err != nil {
return nil, err
}
// Get storage usage
// Gathering usage of a specific StorageClass
var total resource.Quantity
for _, volume := range pvList {
if volume.Spec.StorageClassName != scName {
continue
}
total.Add(volume.Spec.Capacity[v1.ResourceStorage])
}
usage := total.String()
// Get PVC number
pvcNum := len(pvcList)
return &ScMetrics{Usage: usage, PvcNumber: strconv.Itoa(pvcNum)}, nil
}
// Get SC item list
func GetScList() ([]*storageV1.StorageClass, error) {
// Get StorageClass list
scList, err := sotrageClassesLister.List(labels.Everything())
if err != nil {
return nil, err
}
return scList, nil
}

View File

@@ -0,0 +1,57 @@
/*
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 storage
import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
v12 "k8s.io/client-go/listers/core/v1"
"kubesphere.io/kubesphere/pkg/informers"
)
var podLister v12.PodLister
func init() {
podLister = informers.SharedInformerFactory().Core().V1().Pods().Lister()
}
// List pods of a specific persistent volume claims
func GetPodListByPvc(pvc string, ns string) (res []*v1.Pod, err error) {
podList, err := podLister.Pods(ns).List(labels.Everything())
if err != nil {
return nil, err
}
for _, pod := range podList {
if IsPvcInPod(pod, pvc) == true {
res = append(res, pod.DeepCopy())
}
}
return res, nil
}
// Check if the persistent volume claim is related to the pod
func IsPvcInPod(pod *v1.Pod, pvcName string) bool {
for _, v := range pod.Spec.Volumes {
if v.VolumeSource.PersistentVolumeClaim != nil &&
v.VolumeSource.PersistentVolumeClaim.ClaimName == pvcName {
return true
}
}
return false
}

27
pkg/models/types.go Normal file
View File

@@ -0,0 +1,27 @@
/*
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 models
type MessageResponse struct {
Message string `json:"message"`
}
type PageableResponse struct {
Items []interface{} `json:"items"`
TotalCount int `json:"total_count"`
}

View File

@@ -0,0 +1,79 @@
/*
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 workloads
import (
"fmt"
"strings"
"time"
"github.com/golang/glog"
"k8s.io/api/batch/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"kubesphere.io/kubesphere/pkg/client"
)
const retryTimes = 3
func JobReRun(namespace, jobName string) error {
k8sClient := client.K8sClient()
job, err := k8sClient.BatchV1().Jobs(namespace).Get(jobName, metav1.GetOptions{})
if err != nil {
return err
}
newJob := *job
newJob.ResourceVersion = ""
newJob.Status = v1.JobStatus{}
newJob.ObjectMeta.UID = ""
newJob.Annotations["revisions"] = strings.Replace(job.Annotations["revisions"], "running", "unfinished", -1)
delete(newJob.Spec.Selector.MatchLabels, "controller-uid")
delete(newJob.Spec.Template.ObjectMeta.Labels, "controller-uid")
err = deleteJob(namespace, jobName)
if err != nil {
glog.Errorf("failed to rerun job %s, reason: %s", jobName, err)
return fmt.Errorf("failed to rerun job %s", jobName)
}
for i := 0; i < retryTimes; i++ {
_, err = k8sClient.BatchV1().Jobs(namespace).Create(&newJob)
if err != nil {
time.Sleep(time.Second)
continue
}
break
}
if err != nil {
glog.Errorf("failed to rerun job %s, reason: %s", jobName, err)
return fmt.Errorf("failed to rerun job %s", jobName)
}
return nil
}
func deleteJob(namespace, job string) error {
k8sClient := client.K8sClient()
deletePolicy := metav1.DeletePropagationBackground
err := k8sClient.BatchV1().Jobs(namespace).Delete(job, &metav1.DeleteOptions{PropagationPolicy: &deletePolicy})
return err
}

View File

@@ -0,0 +1,63 @@
/*
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 workspaces
import "time"
type Workspace struct {
Group `json:",inline"`
Admin string `json:"admin,omitempty"`
Namespaces []string `json:"namespaces"`
DevopsProjects []string `json:"devops_projects"`
}
type UserInvite struct {
Username string `json:"username"`
Role string `json:"role"`
}
type Group struct {
Path string `json:"path"`
Name string `json:"name"`
Gid string `json:"gid"`
Members []string `json:"members"`
Logo string `json:"logo"`
Creator string `json:"creator"`
CreateTime string `json:"create_time"`
ChildGroups []string `json:"child_groups,omitempty"`
Description string `json:"description"`
}
func (g Group) GetCreateTime() (time.Time, error) {
return time.Parse("2006-01-02T15:04:05Z", g.CreateTime)
}
type WorkspaceDPBinding struct {
Workspace string `gorm:"primary_key"`
DevOpsProject string `gorm:"primary_key"`
}
type DevopsProject struct {
ProjectId *string `json:"project_id,omitempty"`
Name string `json:"name"`
Description string `json:"description"`
Creator string `json:"creator"`
CreateTime *time.Time `json:"create_time,omitempty"`
Status *string `json:"status"`
Visibility *string `json:"visibility,omitempty"`
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
/*
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 monitoring
import (
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/monitoring/v1alpha2"
)
const apiGroup = "monitoring.kubesphere.io"
func AddToContainer(container *restful.Container) error {
container.Add(v1alpha2.WebService(apiGroup))
return nil
}

View File

@@ -0,0 +1,397 @@
/*
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 monitoring
import (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"kubesphere.io/kubesphere/pkg/client"
"kubesphere.io/kubesphere/pkg/models/metrics"
)
func Route(ws *restful.WebService) {
u := Monitor{}
ws.Route(ws.GET("/clusters").To(u.monitorCluster).
Doc("monitor cluster level metrics").
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("cluster_cpu_utilisation")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "cluster"}))
ws.Route(ws.GET("/nodes").To(u.monitorNode).
Doc("monitor nodes level metrics").
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("node_cpu_utilisation")).
Param(ws.QueryParameter("nodes_filter", "node re2 expression filter").DataType("string").Required(false).DefaultValue("")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "node"}))
ws.Route(ws.GET("/nodes/{node_id}").To(u.monitorNode).
Doc("monitor specific node level metrics").
Param(ws.PathParameter("node_id", "specific node").DataType("string").Required(true).DefaultValue("")).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("node_cpu_utilisation")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "node"}))
ws.Route(ws.GET("/namespaces").To(u.monitorNamespace).
Doc("monitor namespaces level metrics").
Param(ws.QueryParameter("namespaces_filter", "namespaces re2 expression filter").DataType("string").Required(false).DefaultValue("")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("namespace_memory_utilisation")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "namespace"}))
ws.Route(ws.GET("/namespaces/{ns_name}").To(u.monitorNamespace).
Doc("monitor specific namespace level metrics").
Param(ws.PathParameter("ns_name", "specific namespace").DataType("string").Required(true).DefaultValue("monitoring")).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("namespace_memory_utilisation")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "namespace"}))
ws.Route(ws.GET("/namespaces/{ns_name}/pods").To(u.monitorPod).
Doc("monitor pods level metrics").
Param(ws.PathParameter("ns_name", "specific namespace").DataType("string").Required(true).DefaultValue("monitoring")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("pod_memory_utilisation_wo_cache")).
Param(ws.QueryParameter("pods_filter", "pod re2 expression filter").DataType("string").Required(false).DefaultValue("")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "pod"}))
ws.Route(ws.GET("/namespaces/{ns_name}/pods/{pod_name}").To(u.monitorPod).
Doc("monitor specific pod level metrics").
Param(ws.PathParameter("ns_name", "specific namespace").DataType("string").Required(true).DefaultValue("monitoring")).
Param(ws.PathParameter("pod_name", "specific pod").DataType("string").Required(true).DefaultValue("")).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("pod_memory_utilisation_wo_cache")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "pod"}))
ws.Route(ws.GET("/nodes/{node_id}/pods").To(u.monitorPod).
Doc("monitor pods level metrics by nodeid").
Param(ws.PathParameter("node_id", "specific node").DataType("string").Required(true).DefaultValue("i-k89a62il")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("pod_memory_utilisation_wo_cache")).
Param(ws.QueryParameter("pods_filter", "pod re2 expression filter").DataType("string").Required(false).DefaultValue("openpitrix.*")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "pod"}))
ws.Route(ws.GET("/nodes/{node_id}/pods/{pod_name}").To(u.monitorPod).
Doc("monitor specific pod level metrics by nodeid").
Param(ws.PathParameter("node_id", "specific node").DataType("string").Required(true).DefaultValue("i-k89a62il")).
Param(ws.PathParameter("pod_name", "specific pod").DataType("string").Required(true).DefaultValue("")).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("pod_memory_utilisation_wo_cache")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "pod"}))
ws.Route(ws.GET("/nodes/{node_id}/pods/{pod_name}/containers").To(u.monitorContainer).
Doc("monitor specific pod level metrics by nodeid").
Param(ws.PathParameter("node_id", "specific node").DataType("string").Required(true)).
Param(ws.PathParameter("pod_name", "specific pod").DataType("string").Required(true)).
Param(ws.QueryParameter("containers_filter", "container re2 expression filter").DataType("string").Required(false).DefaultValue("")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...").DataType("string").Required(false)).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("pod_memory_utilisation_wo_cache")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
Param(ws.QueryParameter("type", "rank, statistic").DataType("string").Required(false).DefaultValue("rank")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "container"}))
ws.Route(ws.GET("/namespaces/{ns_name}/pods/{pod_name}/containers").To(u.monitorContainer).
Doc("monitor containers level metrics").
Param(ws.PathParameter("ns_name", "specific namespace").DataType("string").Required(true).DefaultValue("monitoring")).
Param(ws.PathParameter("pod_name", "specific pod").DataType("string").Required(true).DefaultValue("")).
Param(ws.QueryParameter("containers_filter", "container re2 expression filter").DataType("string").Required(false).DefaultValue("")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...").DataType("string").Required(false)).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("container_memory_utilisation_wo_cache")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
Param(ws.QueryParameter("type", "rank, statistic").DataType("string").Required(false).DefaultValue("rank")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "container"})).
Consumes(restful.MIME_JSON, restful.MIME_XML).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/namespaces/{ns_name}/pods/{pod_name}/containers/{container_name}").To(u.monitorContainer).
Doc("monitor specific container level metrics").
Param(ws.PathParameter("ns_name", "specific namespace").DataType("string").Required(true).DefaultValue("monitoring")).
Param(ws.PathParameter("pod_name", "specific pod").DataType("string").Required(true).DefaultValue("")).
Param(ws.PathParameter("container_name", "specific container").DataType("string").Required(true).DefaultValue("")).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("container_memory_utilisation_wo_cache")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "container"})).
Consumes(restful.MIME_JSON, restful.MIME_XML).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/namespaces/{ns_name}/workloads/{workload_kind}").To(u.monitorWorkload).
Doc("monitor specific workload level metrics").
Param(ws.PathParameter("ns_name", "namespace").DataType("string").Required(true).DefaultValue("kube-system")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...").DataType("string").Required(false)).
Param(ws.PathParameter("workload_kind", "workload kind").DataType("string").Required(false).DefaultValue("daemonset")).
Param(ws.QueryParameter("workload_name", "workload name").DataType("string").Required(true).DefaultValue("")).
Param(ws.QueryParameter("pods_filter", "pod re2 expression filter").DataType("string").Required(false).DefaultValue("openpitrix.*")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "max metric items in a page").DataType("string").Required(false).DefaultValue("4")).
Param(ws.QueryParameter("type", "rank, statistic").DataType("string").Required(false).DefaultValue("rank")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "workload"}))
ws.Route(ws.GET("/namespaces/{ns_name}/workloads").To(u.monitorWorkload).
Doc("monitor all workload level metrics").
Param(ws.PathParameter("ns_name", "namespace").DataType("string").Required(true).DefaultValue("kube-system")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...").DataType("string").Required(false)).
Param(ws.QueryParameter("workloads_filter", "pod re2 expression filter").DataType("string").Required(false).DefaultValue("")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
Param(ws.QueryParameter("type", "rank, statistic").DataType("string").Required(false).DefaultValue("rank")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "workload"}))
// list all namespace in this workspace by selected metrics
ws.Route(ws.GET("/workspaces/{workspace_name}").To(u.monitorOneWorkspace).
Doc("monitor workspaces level metrics").
Param(ws.PathParameter("workspace_name", "workspace name").DataType("string").Required(true)).
Param(ws.QueryParameter("namespaces_filter", "namespaces filter").DataType("string").Required(false).DefaultValue("k.*")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("namespace_memory_utilisation_wo_cache")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
Param(ws.QueryParameter("type", "rank, statistic").DataType("string").Required(false).DefaultValue("rank")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "workspace"}))
ws.Route(ws.GET("/workspaces").To(u.monitorAllWorkspaces).
Doc("monitor workspaces level metrics").
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("workspace_memory_utilisation")).
Param(ws.QueryParameter("workspaces_filter", "workspaces re2 expression filter").DataType("string").Required(false).DefaultValue(".*")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
Param(ws.QueryParameter("type", "rank, statistic").DataType("string").Required(false).DefaultValue("rank")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "workspace"}))
ws.Route(ws.GET("/components").To(u.monitorComponentStatus).
Doc("monitor k8s components status").
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "components"}))
}
func (u Monitor) monitorPod(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
podName := requestParams.PodName
metricName := requestParams.MetricsName
if podName != "" {
// single pod single metric
queryType, params, nullRule := metrics.AssemblePodMetricRequestInfo(requestParams, metricName)
var res *metrics.FormatedMetric
if !nullRule {
res = metrics.GetMetric(queryType, params, metricName)
}
response.WriteAsJson(res)
} else {
// multiple
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelPod)
// sorting
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelPodName)
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
}
}
func (u Monitor) monitorContainer(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
metricName := requestParams.MetricsName
if requestParams.MetricsFilter != "" {
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelContainer)
// sorting
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelContainerName)
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
} else {
res := metrics.MonitorContainer(requestParams, metricName)
response.WriteAsJson(res)
}
}
func (u Monitor) monitorWorkload(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelWorkload)
var sortedMetrics *metrics.FormatedLevelMetric
var maxMetricCount int
wlKind := requestParams.WorkloadKind
// sorting
if wlKind == "" {
sortedMetrics, maxMetricCount = metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelWorkload)
} else {
sortedMetrics, maxMetricCount = metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelPodName)
}
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
}
func (u Monitor) monitorAllWorkspaces(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
tp := requestParams.Tp
if tp == "_statistics" {
// merge multiple metric: all-devops, all-roles, all-projects...this api is designed for admin
res := metrics.MonitorAllWorkspacesStatistics()
response.WriteAsJson(res)
} else if tp == "rank" {
rawMetrics := metrics.MonitorAllWorkspaces(requestParams)
// sorting
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelWorkspace)
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
} else {
res := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelWorkspace)
response.WriteAsJson(res)
}
}
func (u Monitor) monitorOneWorkspace(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
tp := requestParams.Tp
if tp == "rank" {
// multiple
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelWorkspace)
// sorting
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelNamespace)
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
} else if tp == "_statistics" {
wsName := requestParams.WsName
// merge multiple metric: devops, roles, projects...
res := metrics.MonitorOneWorkspaceStatistics(wsName)
response.WriteAsJson(res)
} else {
res := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelWorkspace)
response.WriteAsJson(res)
}
}
func (u Monitor) monitorNamespace(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
metricName := requestParams.MetricsName
nsName := requestParams.NsName
if nsName != "" {
// single
queryType, params := metrics.AssembleNamespaceMetricRequestInfo(requestParams, metricName)
res := metrics.GetMetric(queryType, params, metricName)
response.WriteAsJson(res)
} else {
// multiple
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelNamespace)
// sorting
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelNamespace)
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
}
}
func (u Monitor) monitorCluster(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
metricName := requestParams.MetricsName
if metricName != "" {
// single
queryType, params := metrics.AssembleClusterMetricRequestInfo(requestParams, metricName)
res := metrics.GetMetric(queryType, params, metricName)
response.WriteAsJson(res)
} else {
// multiple
res := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelCluster)
response.WriteAsJson(res)
}
}
func (u Monitor) monitorNode(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
metricName := requestParams.MetricsName
if metricName != "" {
// single
queryType, params := metrics.AssembleNodeMetricRequestInfo(requestParams, metricName)
res := metrics.GetMetric(queryType, params, metricName)
nodeAddress := metrics.GetNodeAddressInfo()
metrics.AddNodeAddressMetric(res, nodeAddress)
response.WriteAsJson(res)
} else {
// multiple
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelNode)
nodeAddress := metrics.GetNodeAddressInfo()
for i := 0; i < len(rawMetrics.Results); i++ {
metrics.AddNodeAddressMetric(&rawMetrics.Results[i], nodeAddress)
}
// sorting
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelNode)
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
}
}
// k8s component(controller, scheduler, etcd) status
func (u Monitor) monitorComponentStatus(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
status := metrics.MonitorComponentStatus(requestParams)
response.WriteAsJson(status)
}
type Monitor struct {
}

View File

@@ -0,0 +1,26 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha2
import (
"kubesphere.io/kubesphere/pkg/monitoring/v1alpha2/monitoring"
)
func init() {
addToWebServiceFuncs = append(addToWebServiceFuncs, monitoring.Route)
}

View File

@@ -0,0 +1,36 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha2
import (
"github.com/emicklei/go-restful"
)
const apiVersion = "v1alpha2"
var addToWebServiceFuncs []func(ws *restful.WebService)
func WebService(apiGroup string) *restful.WebService {
ws := new(restful.WebService)
ws.Path("/apis/" + apiGroup + "/" + apiVersion).
Produces(restful.MIME_JSON).Consumes(restful.MIME_JSON)
for _, f := range addToWebServiceFuncs {
f(ws)
}
return ws
}

54
pkg/params/pagging.go Normal file
View File

@@ -0,0 +1,54 @@
/*
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 params
import (
"regexp"
"strconv"
"golang.org/x/tools/container/intsets"
)
const (
Paging = "paging"
OrderBy = "orderBy"
Conditions = "conditions"
Reserve = "reserve"
)
func ParsePaging(paging string) (limit, offset int) {
limit = intsets.MaxInt
offset = 0
if groups := regexp.MustCompile(`^limit=(\d+),page=(\d+)$`).FindStringSubmatch(paging); len(groups) == 3 {
limit, _ = strconv.Atoi(groups[1])
page, _ := strconv.Atoi(groups[2])
if page < 0 {
page = 1
}
offset = (page - 1) * limit
}
return
}
func ParseReserve(reserve string) bool {
b, err := strconv.ParseBool(reserve)
if err != nil {
return false
}
return b
}

43
pkg/signals/signal.go Normal file
View File

@@ -0,0 +1,43 @@
/*
Copyright 2017 The Kubernetes 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 signals
import (
"os"
"os/signal"
)
var onlyOneSignalHandler = make(chan struct{})
// SetupSignalHandler registered for SIGTERM and SIGINT. A stop channel is returned
// which is closed on one of these signals. If a second signal is caught, the program
// is terminated with exit code 1.
func SetupSignalHandler() (stopCh <-chan struct{}) {
close(onlyOneSignalHandler) // panics when called twice
stop := make(chan struct{})
c := make(chan os.Signal, 2)
signal.Notify(c, shutdownSignals...)
go func() {
<-c
close(stop)
<-c
os.Exit(1) // second signal. Exit directly.
}()
return stop
}

View File

@@ -0,0 +1,26 @@
// +build !windows
/*
Copyright 2017 The Kubernetes 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 signals
import (
"os"
"syscall"
)
var shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM}

View File

@@ -0,0 +1,23 @@
/*
Copyright 2017 The Kubernetes 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 signals
import (
"os"
)
var shutdownSignals = []os.Signal{os.Interrupt}

58
pkg/utils/jsonutils.go Normal file
View File

@@ -0,0 +1,58 @@
/*
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 utils
import (
"encoding/json"
"strings"
"github.com/golang/glog"
)
type JsonRawMessage []byte
func (m JsonRawMessage) Find(key string) JsonRawMessage {
var objmap map[string]json.RawMessage
err := json.Unmarshal(m, &objmap)
if err != nil {
glog.Errorf("Resolve JSON Key failed, find key =%s, err=%s",
key, err)
return nil
}
return JsonRawMessage(objmap[key])
}
func (m JsonRawMessage) ToList() []JsonRawMessage {
var lists []json.RawMessage
err := json.Unmarshal(m, &lists)
if err != nil {
glog.Errorf("Resolve JSON List failed, err=%s",
err)
return nil
}
var res []JsonRawMessage
for _, v := range lists {
res = append(res, JsonRawMessage(v))
}
return res
}
func (m JsonRawMessage) ToString() string {
res := strings.Replace(string(m[:]), "\"", "", -1)
return res
}

40
pkg/version/version.go Normal file
View File

@@ -0,0 +1,40 @@
/*
Copyright 2018 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 version
import (
"fmt"
"os"
"github.com/spf13/pflag"
"kubesphere.io/kubesphere/pkg/constants"
)
var (
versionFlag = pflag.Bool("version", false, "print the version of kubesphere")
)
// PrintAndExitIfRequested will check if the -version flag was passed
// and, if so, print the version and exit.
func PrintAndExitIfRequested() {
if *versionFlag {
fmt.Printf("Kubesphere %s\n", constants.APIVersion)
os.Exit(0)
}
}

37
pkg/webhook/webhook.go Normal file
View File

@@ -0,0 +1,37 @@
/*
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 webhook
import (
"sigs.k8s.io/controller-runtime/pkg/manager"
)
// AddToManagerFuncs is a list of functions to add all Controllers to the Manager
var AddToManagerFuncs []func(manager.Manager) error
// AddToManager adds all Controllers to the Manager
// +kubebuilder:rbac:groups=admissionregistration.k8s.io,resources=mutatingwebhookconfigurations;validatingwebhookconfigurations,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete
func AddToManager(m manager.Manager) error {
for _, f := range AddToManagerFuncs {
if err := f(m); err != nil {
return err
}
}
return nil
}