Initial commit
This commit is contained in:
30
pkg/apis/apis.go
Normal file
30
pkg/apis/apis.go
Normal 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
18
pkg/apis/metrics/group.go
Normal 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
|
||||
33
pkg/apis/metrics/install/install.go
Normal file
33
pkg/apis/metrics/install/install.go
Normal 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))
|
||||
}
|
||||
45
pkg/apis/metrics/v1alpha2/register.go
Normal file
45
pkg/apis/metrics/v1alpha2/register.go
Normal 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
|
||||
}
|
||||
18
pkg/apis/operations/group.go
Normal file
18
pkg/apis/operations/group.go
Normal 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
|
||||
33
pkg/apis/operations/install/install.go
Normal file
33
pkg/apis/operations/install/install.go
Normal 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))
|
||||
}
|
||||
58
pkg/apis/operations/v1alpha2/register.go
Normal file
58
pkg/apis/operations/v1alpha2/register.go
Normal 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
|
||||
}
|
||||
18
pkg/apis/resources/group.go
Normal file
18
pkg/apis/resources/group.go
Normal 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
|
||||
33
pkg/apis/resources/install/install.go
Normal file
33
pkg/apis/resources/install/install.go
Normal 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))
|
||||
}
|
||||
57
pkg/apis/resources/v1alpha2/register.go
Normal file
57
pkg/apis/resources/v1alpha2/register.go
Normal 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
|
||||
}
|
||||
66
pkg/apiserver/components/components.go
Normal file
66
pkg/apiserver/components/components.go
Normal 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
52
pkg/apiserver/hpa/hpa.go
Normal 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)
|
||||
}
|
||||
76
pkg/apiserver/metrics/metrics.go
Normal file
76
pkg/apiserver/metrics/metrics.go
Normal 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)
|
||||
}
|
||||
51
pkg/apiserver/operations/job.go
Normal file
51
pkg/apiserver/operations/job.go
Normal 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)
|
||||
}
|
||||
39
pkg/apiserver/operations/node.go
Normal file
39
pkg/apiserver/operations/node.go
Normal 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)
|
||||
}
|
||||
69
pkg/apiserver/quotas/quotas.go
Normal file
69
pkg/apiserver/quotas/quotas.go
Normal 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)
|
||||
}
|
||||
74
pkg/apiserver/registries/registries.go
Normal file
74
pkg/apiserver/registries/registries.go
Normal 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)
|
||||
//}
|
||||
42
pkg/apiserver/resources/cluster_resources.go
Normal file
42
pkg/apiserver/resources/cluster_resources.go
Normal 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)
|
||||
}
|
||||
43
pkg/apiserver/resources/namespace_resources.go
Normal file
43
pkg/apiserver/resources/namespace_resources.go
Normal 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)
|
||||
}
|
||||
68
pkg/apiserver/resources/storage.go
Normal file
68
pkg/apiserver/resources/storage.go
Normal 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)
|
||||
}
|
||||
52
pkg/apiserver/resources/user.go
Normal file
52
pkg/apiserver/resources/user.go
Normal 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)
|
||||
}
|
||||
116
pkg/apiserver/revisions/revisions.go
Normal file
116
pkg/apiserver/revisions/revisions.go
Normal 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)
|
||||
}
|
||||
187
pkg/apiserver/routers/routers.go
Normal file
187
pkg/apiserver/routers/routers.go
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
64
pkg/apiserver/runtime/runtime.go
Normal file
64
pkg/apiserver/runtime/runtime.go
Normal 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
|
||||
}
|
||||
59
pkg/apiserver/workloadstatuses/workloadstatuses.go
Normal file
59
pkg/apiserver/workloadstatuses/workloadstatuses.go
Normal 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)
|
||||
}
|
||||
506
pkg/apiserver/workspaces/workspaces.go
Normal file
506
pkg/apiserver/workspaces/workspaces.go
Normal 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
51
pkg/client/dbclient.go
Normal 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
97
pkg/client/k8sclient.go
Normal 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
|
||||
|
||||
}
|
||||
192
pkg/client/prometheusclient.go
Normal file
192
pkg/client/prometheusclient.go
Normal 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)
|
||||
}
|
||||
55
pkg/constants/constants.go
Normal file
55
pkg/constants/constants.go
Normal 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
|
||||
}
|
||||
}
|
||||
34
pkg/controller/controller.go
Normal file
34
pkg/controller/controller.go
Normal 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
|
||||
}
|
||||
29
pkg/controller/controllers.go
Normal file
29
pkg/controller/controllers.go
Normal 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")
|
||||
})
|
||||
}
|
||||
10
pkg/controller/controllers_test.go
Normal file
10
pkg/controller/controllers_test.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// controller test
|
||||
func TestController(t *testing.T) {
|
||||
|
||||
}
|
||||
191
pkg/controller/namespace/namespaces.go
Normal file
191
pkg/controller/namespace/namespaces.go
Normal 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
17
pkg/errors/code.go
Normal 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
16
pkg/errors/code_string.go
Normal 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
7
pkg/errors/code_test.go
Normal 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
114
pkg/errors/errors.go
Normal 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
41
pkg/filter/logging.go
Normal 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,
|
||||
)
|
||||
}
|
||||
42
pkg/informers/informers.go
Normal file
42
pkg/informers/informers.go
Normal 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
|
||||
}
|
||||
204
pkg/models/components/components.go
Normal file
204
pkg/models/components/components.go
Normal 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
29
pkg/models/hpa/hpa.go
Normal 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
461
pkg/models/iam/iam.go
Normal 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
39
pkg/models/iam/types.go
Normal 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"`
|
||||
}
|
||||
294
pkg/models/kubeconfig/kubeconfig.go
Normal file
294
pkg/models/kubeconfig/kubeconfig.go
Normal 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
|
||||
}
|
||||
156
pkg/models/kubectl/kubectl.go
Normal file
156
pkg/models/kubectl/kubectl.go
Normal 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
|
||||
}
|
||||
1011
pkg/models/metrics/metrics.go
Normal file
1011
pkg/models/metrics/metrics.go
Normal file
File diff suppressed because it is too large
Load Diff
200
pkg/models/metrics/metricsrule.go
Normal file
200
pkg/models/metrics/metricsrule.go
Normal 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
|
||||
}
|
||||
579
pkg/models/metrics/metricsruleconst.go
Normal file
579
pkg/models/metrics/metricsruleconst.go
Normal 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)`,
|
||||
}
|
||||
51
pkg/models/metrics/namespaces.go
Normal file
51
pkg/models/metrics/namespaces.go
Normal 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(¶ms, 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
306
pkg/models/metrics/util.go
Normal 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
285
pkg/models/nodes/nodes.go
Normal 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
151
pkg/models/quotas/quotas.go
Normal 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 "aStatus, nil
|
||||
}
|
||||
67
pkg/models/registries/registries.go
Normal file
67
pkg/models/registries/registries.go
Normal 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)
|
||||
}
|
||||
}
|
||||
121
pkg/models/resources/clusterroles.go
Normal file
121
pkg/models/resources/clusterroles.go
Normal 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
|
||||
}
|
||||
126
pkg/models/resources/configmaps.go
Normal file
126
pkg/models/resources/configmaps.go
Normal 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
|
||||
}
|
||||
134
pkg/models/resources/cronjobs.go
Normal file
134
pkg/models/resources/cronjobs.go
Normal 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
|
||||
}
|
||||
137
pkg/models/resources/daemonsets.go
Normal file
137
pkg/models/resources/daemonsets.go
Normal 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
|
||||
}
|
||||
141
pkg/models/resources/deployments.go
Normal file
141
pkg/models/resources/deployments.go
Normal 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
|
||||
}
|
||||
127
pkg/models/resources/ingresses.go
Normal file
127
pkg/models/resources/ingresses.go
Normal 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
|
||||
}
|
||||
156
pkg/models/resources/jobs.go
Normal file
156
pkg/models/resources/jobs.go
Normal 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
|
||||
}
|
||||
125
pkg/models/resources/namespaces.go
Normal file
125
pkg/models/resources/namespaces.go
Normal 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
|
||||
}
|
||||
125
pkg/models/resources/nodes.go
Normal file
125
pkg/models/resources/nodes.go
Normal 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
|
||||
}
|
||||
126
pkg/models/resources/persistentvolumeclaims.go
Normal file
126
pkg/models/resources/persistentvolumeclaims.go
Normal 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
|
||||
}
|
||||
127
pkg/models/resources/pods.go
Normal file
127
pkg/models/resources/pods.go
Normal 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
|
||||
}
|
||||
240
pkg/models/resources/resources.go
Normal file
240
pkg/models/resources/resources.go
Normal 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
|
||||
}
|
||||
122
pkg/models/resources/roles.go
Normal file
122
pkg/models/resources/roles.go
Normal 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
|
||||
}
|
||||
130
pkg/models/resources/secrets.go
Normal file
130
pkg/models/resources/secrets.go
Normal 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
|
||||
}
|
||||
126
pkg/models/resources/services.go
Normal file
126
pkg/models/resources/services.go
Normal 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
|
||||
}
|
||||
140
pkg/models/resources/statefulsets.go
Normal file
140
pkg/models/resources/statefulsets.go
Normal 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
|
||||
}
|
||||
121
pkg/models/resources/storageclasses.go
Normal file
121
pkg/models/resources/storageclasses.go
Normal 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
|
||||
}
|
||||
114
pkg/models/revisions/revisions.go
Normal file
114
pkg/models/revisions/revisions.go
Normal 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))
|
||||
}
|
||||
327
pkg/models/routers/routers.go
Normal file
327
pkg/models/routers/routers.go
Normal 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
|
||||
}
|
||||
57
pkg/models/status/status.go
Normal file
57
pkg/models/status/status.go
Normal 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("")
|
||||
}
|
||||
112
pkg/models/storage/storage.go
Normal file
112
pkg/models/storage/storage.go
Normal 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
|
||||
}
|
||||
57
pkg/models/storage/volumes.go
Normal file
57
pkg/models/storage/volumes.go
Normal 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
27
pkg/models/types.go
Normal 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"`
|
||||
}
|
||||
79
pkg/models/workloads/jobs.go
Normal file
79
pkg/models/workloads/jobs.go
Normal 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
|
||||
}
|
||||
63
pkg/models/workspaces/types.go
Normal file
63
pkg/models/workspaces/types.go
Normal 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"`
|
||||
}
|
||||
1379
pkg/models/workspaces/workspaces.go
Normal file
1379
pkg/models/workspaces/workspaces.go
Normal file
File diff suppressed because it is too large
Load Diff
31
pkg/monitoring/monitoring.go
Normal file
31
pkg/monitoring/monitoring.go
Normal 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
|
||||
}
|
||||
397
pkg/monitoring/v1alpha2/monitoring/monitoring.go
Normal file
397
pkg/monitoring/v1alpha2/monitoring/monitoring.go
Normal 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 {
|
||||
}
|
||||
26
pkg/monitoring/v1alpha2/routes.go
Normal file
26
pkg/monitoring/v1alpha2/routes.go
Normal 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)
|
||||
}
|
||||
36
pkg/monitoring/v1alpha2/webservice.go
Normal file
36
pkg/monitoring/v1alpha2/webservice.go
Normal 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
54
pkg/params/pagging.go
Normal 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
43
pkg/signals/signal.go
Normal 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
|
||||
}
|
||||
26
pkg/signals/signal_posix.go
Normal file
26
pkg/signals/signal_posix.go
Normal 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}
|
||||
23
pkg/signals/signal_windows.go
Normal file
23
pkg/signals/signal_windows.go
Normal 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
58
pkg/utils/jsonutils.go
Normal 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
40
pkg/version/version.go
Normal 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
37
pkg/webhook/webhook.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user