clean old devops code

todo impl use informer

Signed-off-by: runzexia <runzexia@yunify.com>
This commit is contained in:
runzexia
2020-04-01 15:54:57 +08:00
parent 56482f1feb
commit 9a6ba04a37
1000 changed files with 31387 additions and 139332 deletions

View File

@@ -2,24 +2,24 @@ package v1alpha2
import (
"github.com/emicklei/go-restful"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"kubesphere.io/kubesphere/pkg/api"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/models/iam/im"
"kubesphere.io/kubesphere/pkg/simple/client/cache"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
ldappool "kubesphere.io/kubesphere/pkg/simple/client/ldap"
"strings"
)
type iamHandler struct {
amOperator am.AccessManagementInterface
imOperator im.IdentityManagementInterface
am am.AccessManagementInterface
im im.IdentityManagementInterface
}
func newIAMHandler(k8sClient k8s.Client, factory informers.InformerFactory, ldapClient ldappool.Interface, cacheClient cache.Interface, options *authoptions.AuthenticationOptions) *iamHandler {
func newIAMHandler(im im.IdentityManagementInterface, am am.AccessManagementInterface, options *authoptions.AuthenticationOptions) *iamHandler {
return &iamHandler{
amOperator: am.NewAMOperator(k8sClient.Kubernetes(), factory.KubernetesSharedInformerFactory()),
imOperator: im.NewLDAPOperator(ldapClient),
am: am,
im: im,
}
}
@@ -36,7 +36,22 @@ func (h *iamHandler) ModifyUser(request *restful.Request, response *restful.Resp
}
func (h *iamHandler) DescribeUser(req *restful.Request, resp *restful.Response) {
panic("implement me")
username := req.PathParameter("user")
user, err := h.im.DescribeUser(username)
if err != nil {
api.HandleInternalError(resp, req, err)
return
}
globalRole, err := h.am.GetRoleOfUserInTargetScope(iamv1alpha2.GlobalScope, "", username)
if err != nil {
api.HandleInternalError(resp, req, err)
return
}
result := iamv1alpha2.UserDetail{User: user, GlobalRole: globalRole}
resp.WriteEntity(result)
}
func (h *iamHandler) ListUsers(req *restful.Request, resp *restful.Response) {
@@ -48,9 +63,39 @@ func (h *iamHandler) ListUserRoles(req *restful.Request, resp *restful.Response)
}
func (h *iamHandler) ListRoles(req *restful.Request, resp *restful.Response) {
panic("implement me")
}
func (h *iamHandler) ListRolesOfUser(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("user")
var roles []iamv1alpha2.Role
var err error
if strings.HasSuffix(req.Request.URL.Path, "workspaceroles") {
roles, err = h.am.ListRolesOfUser(iamv1alpha2.WorkspaceScope, username)
} else if strings.HasSuffix(req.Request.URL.Path, "clusterroles") {
roles, err = h.am.ListRolesOfUser(iamv1alpha2.ClusterScope, username)
} else if strings.HasSuffix(req.Request.URL.Path, "namespaceroles") {
roles, err = h.am.ListRolesOfUser(iamv1alpha2.NamespaceScope, username)
}
if err != nil {
api.HandleInternalError(resp, req, err)
return
}
result := iamv1alpha2.RoleList{
TypeMeta: v1.TypeMeta{
Kind: "List",
APIVersion: "v1",
},
ListMeta: v1.ListMeta{},
Items: roles,
}
resp.WriteEntity(result)
}
func (h *iamHandler) ListClusterRoles(req *restful.Request, resp *restful.Response) {
panic("implement me")
}

View File

@@ -22,15 +22,14 @@ import (
"github.com/emicklei/go-restful-openapi"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/api"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/models/iam/im"
"kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/simple/client/cache"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
ldappool "kubesphere.io/kubesphere/pkg/simple/client/ldap"
"net/http"
)
@@ -38,19 +37,38 @@ const groupName = "iam.kubesphere.io"
var GroupVersion = schema.GroupVersion{Group: groupName, Version: "v1alpha2"}
func AddToContainer(c *restful.Container, k8sClient k8s.Client, factory informers.InformerFactory, ldapClient ldappool.Interface, cacheClient cache.Interface, options *authoptions.AuthenticationOptions) error {
func AddToContainer(container *restful.Container, im im.IdentityManagementInterface, am am.AccessManagementInterface, options *authoptions.AuthenticationOptions) error {
ws := runtime.NewWebService(GroupVersion)
handler := newIAMHandler(k8sClient, factory, ldapClient, cacheClient, options)
handler := newIAMHandler(im, am, options)
// implemented by create CRD object.
//ws.Route(ws.POST("/users"))
//ws.Route(ws.DELETE("/users/{user}"))
//ws.Route(ws.PUT("/users/{user}"))
//ws.Route(ws.GET("/users/{user}"))
ws.Route(ws.GET("/users/{user}").
To(handler.DescribeUser).
Doc("Retrieve user details.").
Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.UserDetail{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
// TODO move to resources api
//ws.Route(ws.GET("/users"))
ws.Route(ws.GET("/users/{user}/workspaceroles").
To(handler.ListRolesOfUser).
Doc("Retrieve user roles in workspaces.").
Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.RoleList{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/users/{user}/clusterroles").
To(handler.ListRolesOfUser).
Doc("Retrieve user roles in clusters.").
Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.RoleList{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/users/{user}/namespaceroles").
To(handler.ListRolesOfUser).
Doc("Retrieve user roles in namespaces.").
Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.RoleList{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/namespaces/{namespace}/roles").
To(handler.ListRoles).
@@ -104,6 +122,6 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, factory informer
Returns(http.StatusOK, api.StatusOK, errors.Error{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
c.Add(ws)
container.Add(ws)
return nil
}

View File

@@ -1,145 +0,0 @@
/*
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"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/api/monitoring/v1alpha2"
model "kubesphere.io/kubesphere/pkg/models/monitoring"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/monitoring"
)
type handler struct {
k k8s.Client
mo model.MonitoringOperator
}
func newHandler(k k8s.Client, m monitoring.Interface) *handler {
return &handler{k, model.NewMonitoringOperator(m)}
}
func (h handler) handleClusterMetricsQuery(req *restful.Request, resp *restful.Response) {
p, err := h.parseRequestParams(req, monitoring.LevelCluster)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
h.handleNamedMetricsQuery(resp, p)
}
func (h handler) handleNodeMetricsQuery(req *restful.Request, resp *restful.Response) {
p, err := h.parseRequestParams(req, monitoring.LevelNode)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
h.handleNamedMetricsQuery(resp, p)
}
func (h handler) handleWorkspaceMetricsQuery(req *restful.Request, resp *restful.Response) {
p, err := h.parseRequestParams(req, monitoring.LevelWorkspace)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
h.handleNamedMetricsQuery(resp, p)
}
func (h handler) handleNamespaceMetricsQuery(req *restful.Request, resp *restful.Response) {
p, err := h.parseRequestParams(req, monitoring.LevelNamespace)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
h.handleNamedMetricsQuery(resp, p)
}
func (h handler) handleWorkloadMetricsQuery(req *restful.Request, resp *restful.Response) {
p, err := h.parseRequestParams(req, monitoring.LevelWorkload)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
h.handleNamedMetricsQuery(resp, p)
}
func (h handler) handlePodMetricsQuery(req *restful.Request, resp *restful.Response) {
p, err := h.parseRequestParams(req, monitoring.LevelPod)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
h.handleNamedMetricsQuery(resp, p)
}
func (h handler) handleContainerMetricsQuery(req *restful.Request, resp *restful.Response) {
p, err := h.parseRequestParams(req, monitoring.LevelContainer)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
h.handleNamedMetricsQuery(resp, p)
}
func (h handler) handlePVCMetricsQuery(req *restful.Request, resp *restful.Response) {
p, err := h.parseRequestParams(req, monitoring.LevelPVC)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
h.handleNamedMetricsQuery(resp, p)
}
func (h handler) handleComponentMetricsQuery(req *restful.Request, resp *restful.Response) {
p, err := h.parseRequestParams(req, monitoring.LevelComponent)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
h.handleNamedMetricsQuery(resp, p)
}
func (h handler) handleNamedMetricsQuery(resp *restful.Response, p params) {
var res v1alpha2.APIResponse
var err error
if p.isRangeQuery() {
res, err = h.mo.GetNamedMetricsOverTime(p.start, p.end, p.step, p.option)
if err != nil {
api.HandleInternalError(resp, nil, err)
return
}
} else {
res, err = h.mo.GetNamedMetrics(p.time, p.option)
if err != nil {
api.HandleInternalError(resp, nil, err)
return
}
if p.shouldSort() {
var rows int
res, rows = h.mo.SortMetrics(res, p.target, p.order, p.identifier)
res = h.mo.PageMetrics(res, p.page, p.limit, rows)
}
}
resp.WriteAsJson(res)
}

View File

@@ -1,217 +0,0 @@
package v1alpha2
import (
"fmt"
"github.com/emicklei/go-restful"
"github.com/pkg/errors"
corev1 "k8s.io/apimachinery/pkg/apis/meta/v1"
model "kubesphere.io/kubesphere/pkg/models/monitoring"
"kubesphere.io/kubesphere/pkg/simple/client/monitoring"
"strconv"
"time"
)
const (
DefaultStep = 10 * time.Minute
DefaultFilter = ".*"
DefaultOrder = model.OrderDescending
DefaultPage = 1
DefaultLimit = 5
)
type params struct {
time time.Time
start, end time.Time
step time.Duration
target string
identifier string
order string
page int
limit int
option monitoring.QueryOption
}
func (p params) isRangeQuery() bool {
return !p.time.IsZero()
}
func (p params) shouldSort() bool {
return p.target != ""
}
func (h handler) parseRequestParams(req *restful.Request, lvl monitoring.MonitoringLevel) (params, error) {
timestamp := req.QueryParameter("time")
start := req.QueryParameter("start")
end := req.QueryParameter("end")
step := req.QueryParameter("step")
target := req.QueryParameter("sort_metric")
order := req.QueryParameter("sort_type")
page := req.QueryParameter("page")
limit := req.QueryParameter("limit")
metricFilter := req.QueryParameter("metrics_filter")
resourceFilter := req.QueryParameter("resources_filter")
nodeName := req.PathParameter("node")
workspaceName := req.PathParameter("workspace")
namespaceName := req.PathParameter("namespace")
workloadKind := req.PathParameter("kind")
workloadName := req.PathParameter("workload")
podName := req.PathParameter("pod")
containerName := req.PathParameter("container")
pvcName := req.PathParameter("pvc")
storageClassName := req.PathParameter("storageclass")
componentType := req.PathParameter("component")
var p params
var err error
if start != "" && end != "" {
p.start, err = time.Parse(time.RFC3339, start)
if err != nil {
return p, err
}
p.end, err = time.Parse(time.RFC3339, end)
if err != nil {
return p, err
}
if step == "" {
p.step = DefaultStep
} else {
p.step, err = time.ParseDuration(step)
if err != nil {
return p, err
}
}
} else if start == "" && end == "" {
if timestamp == "" {
p.time = time.Now()
} else {
p.time, err = time.Parse(time.RFC3339, req.QueryParameter("time"))
if err != nil {
return p, err
}
}
} else {
return p, errors.Errorf("'time' and the combination of 'start' and 'end' are mutually exclusive.")
}
// hide metrics from a deleted namespace having the same name
namespace := req.QueryParameter("namespace")
if req.QueryParameter("namespace") != "" {
ns, err := h.k.Kubernetes().CoreV1().Namespaces().Get(namespace, corev1.GetOptions{})
if err != nil {
return p, err
}
cts := ns.CreationTimestamp.Time
if p.start.Before(cts) {
p.start = cts
}
if p.end.Before(cts) {
return p, errors.Errorf("End timestamp must not be before namespace creation time.")
}
}
if resourceFilter == "" {
resourceFilter = DefaultFilter
}
if metricFilter == "" {
metricFilter = DefaultFilter
}
if componentType != "" {
metricFilter = fmt.Sprintf("/^(?=.*%s)(?=.*%s)/s", componentType, metricFilter)
}
// should sort
if target != "" {
p.page = DefaultPage
p.limit = DefaultLimit
if order != model.OrderAscending {
p.order = DefaultOrder
}
if page != "" {
p.page, err = strconv.Atoi(req.QueryParameter("page"))
if err != nil || p.page <= 0 {
return p, errors.Errorf("Invalid parameter 'page'.")
}
}
if limit != "" {
p.limit, err = strconv.Atoi(req.QueryParameter("limit"))
if err != nil || p.limit <= 0 {
return p, errors.Errorf("Invalid parameter 'limit'.")
}
}
}
switch lvl {
case monitoring.LevelCluster:
p.option = monitoring.ClusterOption{MetricFilter: metricFilter}
case monitoring.LevelNode:
p.identifier = model.IdentifierNode
p.option = monitoring.NodeOption{
MetricFilter: metricFilter,
ResourceFilter: resourceFilter,
NodeName: nodeName,
}
case monitoring.LevelWorkspace:
p.identifier = model.IdentifierWorkspace
p.option = monitoring.WorkspaceOption{
MetricFilter: metricFilter,
ResourceFilter: resourceFilter,
WorkspaceName: workspaceName,
}
case monitoring.LevelNamespace:
p.identifier = model.IdentifierNamespace
p.option = monitoring.NamespaceOption{
MetricFilter: metricFilter,
ResourceFilter: resourceFilter,
WorkspaceName: workspaceName,
NamespaceName: namespaceName,
}
case monitoring.LevelWorkload:
p.identifier = model.IdentifierWorkload
p.option = monitoring.WorkloadOption{
MetricFilter: metricFilter,
ResourceFilter: resourceFilter,
NamespaceName: namespaceName,
WorkloadKind: workloadKind,
WorkloadName: workloadName,
}
case monitoring.LevelPod:
p.identifier = model.IdentifierPod
p.option = monitoring.PodOption{
MetricFilter: metricFilter,
ResourceFilter: resourceFilter,
NodeName: nodeName,
NamespaceName: namespaceName,
WorkloadKind: workloadKind,
WorkloadName: workloadName,
PodName: podName,
}
case monitoring.LevelContainer:
p.identifier = model.IdentifierContainer
p.option = monitoring.ContainerOption{
MetricFilter: metricFilter,
ResourceFilter: resourceFilter,
NamespaceName: namespaceName,
PodName: podName,
ContainerName: containerName,
}
case monitoring.LevelPVC:
p.identifier = model.IdentifierPVC
p.option = monitoring.PVCOption{
MetricFilter: metricFilter,
ResourceFilter: resourceFilter,
NamespaceName: namespaceName,
StorageClassName: storageClassName,
PersistentVolumeClaimName: pvcName,
}
case monitoring.LevelComponent:
p.option = monitoring.ComponentOption{
MetricFilter: metricFilter,
}
}
return p, nil
}

View File

@@ -0,0 +1,194 @@
/*
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 v1alpha3
import (
"github.com/emicklei/go-restful"
"k8s.io/client-go/kubernetes"
"kubesphere.io/kubesphere/pkg/api"
model "kubesphere.io/kubesphere/pkg/models/monitoring"
"kubesphere.io/kubesphere/pkg/simple/client/monitoring"
"regexp"
)
type handler struct {
k kubernetes.Interface
mo model.MonitoringOperator
}
func newHandler(k kubernetes.Interface, m monitoring.Interface) *handler {
return &handler{k, model.NewMonitoringOperator(m)}
}
func (h handler) handleClusterMetricsQuery(req *restful.Request, resp *restful.Response) {
params := parseRequestParams(req)
opt, err := h.makeQueryOptions(params, monitoring.LevelCluster)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
h.handleNamedMetricsQuery(resp, opt)
}
func (h handler) handleNodeMetricsQuery(req *restful.Request, resp *restful.Response) {
params := parseRequestParams(req)
opt, err := h.makeQueryOptions(params, monitoring.LevelNode)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
h.handleNamedMetricsQuery(resp, opt)
}
func (h handler) handleWorkspaceMetricsQuery(req *restful.Request, resp *restful.Response) {
params := parseRequestParams(req)
opt, err := h.makeQueryOptions(params, monitoring.LevelWorkspace)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
h.handleNamedMetricsQuery(resp, opt)
}
func (h handler) handleNamespaceMetricsQuery(req *restful.Request, resp *restful.Response) {
params := parseRequestParams(req)
opt, err := h.makeQueryOptions(params, monitoring.LevelNamespace)
if err != nil {
if err.Error() == ErrNoHit {
res := handleNoHit(opt.namedMetrics)
resp.WriteAsJson(res)
return
}
api.HandleBadRequest(resp, nil, err)
return
}
h.handleNamedMetricsQuery(resp, opt)
}
func (h handler) handleWorkloadMetricsQuery(req *restful.Request, resp *restful.Response) {
params := parseRequestParams(req)
opt, err := h.makeQueryOptions(params, monitoring.LevelWorkload)
if err != nil {
if err.Error() == ErrNoHit {
res := handleNoHit(opt.namedMetrics)
resp.WriteAsJson(res)
return
}
api.HandleBadRequest(resp, nil, err)
return
}
h.handleNamedMetricsQuery(resp, opt)
}
func (h handler) handlePodMetricsQuery(req *restful.Request, resp *restful.Response) {
params := parseRequestParams(req)
opt, err := h.makeQueryOptions(params, monitoring.LevelPod)
if err != nil {
if err.Error() == ErrNoHit {
res := handleNoHit(opt.namedMetrics)
resp.WriteAsJson(res)
return
}
api.HandleBadRequest(resp, nil, err)
return
}
h.handleNamedMetricsQuery(resp, opt)
}
func (h handler) handleContainerMetricsQuery(req *restful.Request, resp *restful.Response) {
params := parseRequestParams(req)
opt, err := h.makeQueryOptions(params, monitoring.LevelContainer)
if err != nil {
if err.Error() == ErrNoHit {
res := handleNoHit(opt.namedMetrics)
resp.WriteAsJson(res)
return
}
api.HandleBadRequest(resp, nil, err)
return
}
h.handleNamedMetricsQuery(resp, opt)
}
func (h handler) handlePVCMetricsQuery(req *restful.Request, resp *restful.Response) {
params := parseRequestParams(req)
opt, err := h.makeQueryOptions(params, monitoring.LevelPVC)
if err != nil {
if err.Error() == ErrNoHit {
res := handleNoHit(opt.namedMetrics)
resp.WriteAsJson(res)
return
}
api.HandleBadRequest(resp, nil, err)
return
}
h.handleNamedMetricsQuery(resp, opt)
}
func (h handler) handleComponentMetricsQuery(req *restful.Request, resp *restful.Response) {
params := parseRequestParams(req)
opt, err := h.makeQueryOptions(params, monitoring.LevelComponent)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
h.handleNamedMetricsQuery(resp, opt)
}
func handleNoHit(namedMetrics []string) model.Metrics {
var res model.Metrics
for _, metic := range namedMetrics {
res.Results = append(res.Results, monitoring.Metric{
MetricName: metic,
MetricData: monitoring.MetricData{},
})
}
return res
}
func (h handler) handleNamedMetricsQuery(resp *restful.Response, q queryOptions) {
var res model.Metrics
var metrics []string
for _, metric := range q.namedMetrics {
ok, _ := regexp.MatchString(q.metricFilter, metric)
if ok {
metrics = append(metrics, metric)
}
}
if len(metrics) == 0 {
resp.WriteAsJson(res)
return
}
if q.isRangeQuery() {
res = h.mo.GetNamedMetricsOverTime(metrics, q.start, q.end, q.step, q.option)
} else {
res = h.mo.GetNamedMetrics(metrics, q.time, q.option)
if q.shouldSort() {
res = *res.Sort(q.target, q.order, q.identifier).Page(q.page, q.limit)
}
}
resp.WriteAsJson(res)
}

View File

@@ -0,0 +1,268 @@
package v1alpha3
import (
"github.com/emicklei/go-restful"
"github.com/pkg/errors"
corev1 "k8s.io/apimachinery/pkg/apis/meta/v1"
model "kubesphere.io/kubesphere/pkg/models/monitoring"
"kubesphere.io/kubesphere/pkg/simple/client/monitoring"
"strconv"
"time"
)
const (
DefaultStep = 10 * time.Minute
DefaultFilter = ".*"
DefaultOrder = model.OrderDescending
DefaultPage = 1
DefaultLimit = 5
ComponentEtcd = "etcd"
ComponentAPIServer = "apiserver"
ComponentScheduler = "scheduler"
ErrNoHit = "'end' must be after the namespace creation time."
ErrParamConflict = "'time' and the combination of 'start' and 'end' are mutually exclusive."
ErrInvalidStartEnd = "'start' must be before 'end'."
ErrInvalidPage = "Invalid parameter 'page'."
ErrInvalidLimit = "Invalid parameter 'limit'."
)
type reqParams struct {
time string
start string
end string
step string
target string
order string
page string
limit string
metricFilter string
resourceFilter string
nodeName string
workspaceName string
namespaceName string
workloadKind string
workloadName string
podName string
containerName string
pvcName string
storageClassName string
componentType string
}
type queryOptions struct {
metricFilter string
namedMetrics []string
start time.Time
end time.Time
time time.Time
step time.Duration
target string
identifier string
order string
page int
limit int
option monitoring.QueryOption
}
func (q queryOptions) isRangeQuery() bool {
return !q.time.IsZero()
}
func (q queryOptions) shouldSort() bool {
return q.target != "" && q.identifier != ""
}
func parseRequestParams(req *restful.Request) reqParams {
var r reqParams
r.time = req.QueryParameter("time")
r.start = req.QueryParameter("start")
r.end = req.QueryParameter("end")
r.step = req.QueryParameter("step")
r.target = req.QueryParameter("sort_metric")
r.order = req.QueryParameter("sort_type")
r.page = req.QueryParameter("page")
r.limit = req.QueryParameter("limit")
r.metricFilter = req.QueryParameter("metrics_filter")
r.resourceFilter = req.QueryParameter("resources_filter")
r.nodeName = req.PathParameter("node")
r.workspaceName = req.PathParameter("workspace")
r.namespaceName = req.PathParameter("namespace")
r.workloadKind = req.PathParameter("kind")
r.workloadName = req.PathParameter("workload")
r.podName = req.PathParameter("pod")
r.containerName = req.PathParameter("container")
r.pvcName = req.PathParameter("pvc")
r.storageClassName = req.PathParameter("storageclass")
r.componentType = req.PathParameter("component")
return r
}
func (h handler) makeQueryOptions(r reqParams, lvl monitoring.Level) (q queryOptions, err error) {
if r.resourceFilter == "" {
r.resourceFilter = DefaultFilter
}
q.metricFilter = r.metricFilter
if r.metricFilter == "" {
q.metricFilter = DefaultFilter
}
switch lvl {
case monitoring.LevelCluster:
q.option = monitoring.ClusterOption{}
q.namedMetrics = model.ClusterMetrics
case monitoring.LevelNode:
q.identifier = model.IdentifierNode
q.namedMetrics = model.NodeMetrics
q.option = monitoring.NodeOption{
ResourceFilter: r.resourceFilter,
NodeName: r.nodeName,
}
case monitoring.LevelWorkspace:
q.identifier = model.IdentifierWorkspace
q.namedMetrics = model.WorkspaceMetrics
q.option = monitoring.WorkspaceOption{
ResourceFilter: r.resourceFilter,
WorkspaceName: r.workspaceName,
}
case monitoring.LevelNamespace:
q.identifier = model.IdentifierNamespace
q.namedMetrics = model.NamespaceMetrics
q.option = monitoring.NamespaceOption{
ResourceFilter: r.resourceFilter,
WorkspaceName: r.workspaceName,
NamespaceName: r.namespaceName,
}
case monitoring.LevelWorkload:
q.identifier = model.IdentifierWorkload
q.namedMetrics = model.WorkloadMetrics
q.option = monitoring.WorkloadOption{
ResourceFilter: r.resourceFilter,
NamespaceName: r.namespaceName,
WorkloadKind: r.workloadKind,
}
case monitoring.LevelPod:
q.identifier = model.IdentifierPod
q.namedMetrics = model.PodMetrics
q.option = monitoring.PodOption{
ResourceFilter: r.resourceFilter,
NodeName: r.nodeName,
NamespaceName: r.namespaceName,
WorkloadKind: r.workloadKind,
WorkloadName: r.workloadName,
PodName: r.podName,
}
case monitoring.LevelContainer:
q.identifier = model.IdentifierContainer
q.namedMetrics = model.ContainerMetrics
q.option = monitoring.ContainerOption{
ResourceFilter: r.resourceFilter,
NamespaceName: r.namespaceName,
PodName: r.podName,
ContainerName: r.containerName,
}
case monitoring.LevelPVC:
q.identifier = model.IdentifierPVC
q.namedMetrics = model.PVCMetrics
q.option = monitoring.PVCOption{
ResourceFilter: r.resourceFilter,
NamespaceName: r.namespaceName,
StorageClassName: r.storageClassName,
PersistentVolumeClaimName: r.pvcName,
}
case monitoring.LevelComponent:
q.option = monitoring.ComponentOption{}
switch r.componentType {
case ComponentEtcd:
q.namedMetrics = model.EtcdMetrics
case ComponentAPIServer:
q.namedMetrics = model.APIServerMetrics
case ComponentScheduler:
q.namedMetrics = model.SchedulerMetrics
}
}
// Parse time params
if r.start != "" && r.end != "" {
startInt, err := strconv.ParseInt(r.start, 10, 64)
if err != nil {
return q, err
}
q.start = time.Unix(startInt, 0)
endInt, err := strconv.ParseInt(r.end, 10, 64)
if err != nil {
return q, err
}
q.end = time.Unix(endInt, 0)
if r.step == "" {
q.step = DefaultStep
} else {
q.step, err = time.ParseDuration(r.step)
if err != nil {
return q, err
}
}
if q.start.After(q.end) {
return q, errors.New(ErrInvalidStartEnd)
}
} else if r.start == "" && r.end == "" {
if r.time == "" {
q.time = time.Now()
} else {
timeInt, err := strconv.ParseInt(r.time, 10, 64)
if err != nil {
return q, err
}
q.time = time.Unix(timeInt, 0)
}
} else {
return q, errors.Errorf(ErrParamConflict)
}
// Ensure query start time to be after the namespace creation time
if r.namespaceName != "" {
ns, err := h.k.CoreV1().Namespaces().Get(r.namespaceName, corev1.GetOptions{})
if err != nil {
return q, err
}
cts := ns.CreationTimestamp.Time
if q.start.Before(cts) {
q.start = cts
}
if q.end.Before(cts) {
return q, errors.New(ErrNoHit)
}
}
// Parse sorting and paging params
if r.target != "" {
q.page = DefaultPage
q.limit = DefaultLimit
if q.order != model.OrderAscending {
r.order = DefaultOrder
}
if r.page != "" {
q.page, err = strconv.Atoi(r.page)
if err != nil || q.page <= 0 {
return q, errors.New(ErrInvalidPage)
}
}
if r.limit != "" {
q.limit, err = strconv.Atoi(r.limit)
if err != nil || q.limit <= 0 {
return q, errors.New(ErrInvalidLimit)
}
}
}
return q, nil
}

View File

@@ -0,0 +1,148 @@
package v1alpha3
import (
"fmt"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
model "kubesphere.io/kubesphere/pkg/models/monitoring"
"kubesphere.io/kubesphere/pkg/simple/client/monitoring"
"reflect"
"testing"
"time"
)
func TestParseRequestParams(t *testing.T) {
tests := []struct {
params reqParams
lvl monitoring.Level
namespace corev1.Namespace
expected queryOptions
expectedErr bool
}{
{
params: reqParams{
time: "abcdef",
},
lvl: monitoring.LevelCluster,
expectedErr: true,
},
{
params: reqParams{
time: "1585831995",
},
lvl: monitoring.LevelCluster,
expected: queryOptions{
time: time.Unix(1585831995, 0),
metricFilter: ".*",
namedMetrics: model.ClusterMetrics,
option: monitoring.ClusterOption{},
},
expectedErr: false,
},
{
params: reqParams{
start: "1585830000",
end: "1585839999",
step: "1m",
namespaceName: "default",
},
lvl: monitoring.LevelNamespace,
namespace: corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
CreationTimestamp: metav1.Time{
Time: time.Unix(1585836666, 0),
},
},
},
expected: queryOptions{
start: time.Unix(1585836666, 0),
end: time.Unix(1585839999, 0),
step: time.Minute,
identifier: model.IdentifierNamespace,
metricFilter: ".*",
namedMetrics: model.NamespaceMetrics,
option: monitoring.NamespaceOption{
ResourceFilter: ".*",
NamespaceName: "default",
},
},
expectedErr: false,
},
{
params: reqParams{
start: "1585830000",
end: "1585839999",
step: "1m",
namespaceName: "default",
},
lvl: monitoring.LevelNamespace,
namespace: corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
CreationTimestamp: metav1.Time{
Time: time.Unix(1589999999, 0),
},
},
},
expectedErr: true,
},
{
params: reqParams{
start: "1585830000",
end: "1585839999",
step: "1m",
namespaceName: "non-exist",
},
lvl: monitoring.LevelNamespace,
namespace: corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
CreationTimestamp: metav1.Time{
Time: time.Unix(1589999999, 0),
},
},
},
expectedErr: true,
},
{
params: reqParams{
time: "1585830000",
componentType: "etcd",
metricFilter: "etcd_server_list",
},
lvl: monitoring.LevelComponent,
expected: queryOptions{
time: time.Unix(1585830000, 0),
metricFilter: "etcd_server_list",
namedMetrics: model.EtcdMetrics,
option: monitoring.ComponentOption{},
},
expectedErr: false,
},
}
for i, tt := range tests {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
client := fake.NewSimpleClientset(&tt.namespace)
handler := newHandler(client, nil)
result, err := handler.makeQueryOptions(tt.params, tt.lvl)
if err != nil {
if !tt.expectedErr {
t.Fatalf("unexpected err: %s.", err.Error())
}
return
}
if tt.expectedErr {
t.Fatalf("failed to catch error.")
}
if !reflect.DeepEqual(result, tt.expected) {
t.Fatalf("unexpected return: %v.", result)
}
})
}
}

View File

@@ -15,16 +15,16 @@
limitations under the License.
*/
package v1alpha2
package v1alpha3
import (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/api/monitoring/v1alpha2"
"k8s.io/client-go/kubernetes"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
model "kubesphere.io/kubesphere/pkg/models/monitoring"
"kubesphere.io/kubesphere/pkg/simple/client/monitoring"
"net/http"
)
@@ -36,7 +36,7 @@ const (
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient monitoring.Interface) error {
func AddToContainer(c *restful.Container, k8sClient kubernetes.Interface, monitoringClient monitoring.Interface) error {
ws := runtime.NewWebService(GroupVersion)
h := newHandler(k8sClient, monitoringClient)
@@ -50,8 +50,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)).
Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/nodes").
@@ -68,8 +68,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)).
Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.NodeMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/nodes/{node}").
@@ -82,8 +82,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)).
Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.NodeMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/workspaces").
@@ -100,8 +100,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)).
Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkspaceMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/workspaces/{workspace}").
@@ -114,8 +114,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)).
Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkspaceMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/workspaces/{workspace}/namespaces").
@@ -133,8 +133,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)).
Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/namespaces").
@@ -151,8 +151,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)).
Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/namespaces/{namespace}").
@@ -165,8 +165,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)).
Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/namespaces/{namespace}/workloads").
@@ -184,8 +184,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)).
Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkloadMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/namespaces/{namespace}/workloads/{kind}").
@@ -204,8 +204,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)).
Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkloadMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/namespaces/{namespace}/pods").
@@ -223,8 +223,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)).
Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.PodMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/namespaces/{namespace}/pods/{pod}").
@@ -238,8 +238,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)).
Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.PodMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/namespaces/{namespace}/workloads/{kind}/{workload}/pods").
@@ -259,8 +259,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)).
Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.PodMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/nodes/{node}/pods").
@@ -278,8 +278,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)).
Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.PodMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/nodes/{node}/pods/{pod}").
@@ -293,8 +293,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)).
Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.PodMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/namespaces/{namespace}/pods/{pod}/containers").
@@ -313,8 +313,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)).
Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.ContainerMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/namespaces/{namespace}/pods/{pod}/containers/{container}").
@@ -329,8 +329,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)).
Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.ContainerMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/storageclasses/{storageclass}/persistentvolumeclaims").
@@ -348,8 +348,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)).
Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.PVCMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/namespaces/{namespace}/persistentvolumeclaims").
@@ -367,8 +367,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)).
Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.PVCMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/namespaces/{namespace}/persistentvolumeclaims/{pvc}").
@@ -382,8 +382,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)).
Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.PVCMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/components/{component}").
@@ -396,8 +396,8 @@ func AddToContainer(c *restful.Container, k8sClient k8s.Client, monitoringClient
Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)).
Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.ComponentMetricsTag}).
Writes(v1alpha2.APIResponse{}).
Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})).
Writes(model.Metrics{}).
Returns(http.StatusOK, RespOK, model.Metrics{})).
Produces(restful.MIME_JSON)
c.Add(ws)

View File

@@ -49,6 +49,7 @@ func AddToContainer(c *restful.Container, client kubernetes.Interface, factory i
webservice.Route(webservice.GET("/namespaces/{namespace}/{resources}").
To(handler.handleListNamespaceResources).
Deprecate().
Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}).
Doc("Namespace level resource query").
Param(webservice.PathParameter("namespace", "the name of the project")).
@@ -66,9 +67,10 @@ func AddToContainer(c *restful.Container, client kubernetes.Interface, factory i
webservice.Route(webservice.GET("/{resources}").
To(handler.handleListNamespaceResources).
Deprecate().
Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterResourcesTag}).
Doc("Cluster level resource query").
Doc("Cluster level resources").
Param(webservice.PathParameter("resources", "cluster level resource type, e.g. nodes,workspaces,storageclasses,clusterroles.")).
Param(webservice.QueryParameter(params.ConditionsParam, "query conditions, connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a").
Required(false).

View File

@@ -11,7 +11,7 @@ import (
)
type Handler struct {
namespacedResourceGetter *resource.NamespacedResourceGetter
namespacedResourceGetter *resource.ResourceGetter
componentsGetter components.ComponentsGetter
}
@@ -22,22 +22,8 @@ func New(factory informers.InformerFactory) *Handler {
}
}
func (h Handler) handleGetNamespacedResource(request *restful.Request, response *restful.Response) {
resource := request.PathParameter("resources")
namespace := request.PathParameter("namespace")
name := request.PathParameter("name")
result, err := h.namespacedResourceGetter.Get(resource, namespace, name)
if err != nil {
api.HandleInternalError(response, nil, err)
return
}
response.WriteHeaderAndEntity(http.StatusOK, result)
}
// handleListNamedResource retrieves namespaced scope resources
func (h Handler) handleListNamespacedResource(request *restful.Request, response *restful.Response) {
// handleListResources retrieves resources
func (h Handler) handleListResources(request *restful.Request, response *restful.Response) {
query := query.ParseQueryParameter(request)
resource := request.PathParameter("resources")
namespace := request.PathParameter("namespace")
@@ -48,7 +34,7 @@ func (h Handler) handleListNamespacedResource(request *restful.Request, response
return
}
response.WriteHeaderAndEntity(http.StatusOK, result)
response.WriteEntity(result)
}
func (h Handler) handleGetComponentStatus(request *restful.Request, response *restful.Response) {

View File

@@ -45,14 +45,26 @@ func AddToContainer(c *restful.Container, informerFactory informers.InformerFact
webservice := runtime.NewWebService(GroupVersion)
handler := New(informerFactory)
webservice.Route(webservice.GET("/{resources}").
To(handler.handleListResources).
Metadata(restfulspec.KeyOpenAPITags, []string{tagNamespacedResource}).
Doc("Cluster level resources").
Param(webservice.PathParameter("resources", "cluster level resource type, e.g. pods,jobs,configmaps,services.")).
Param(webservice.QueryParameter(query.ParameterName, "name used to do filtering").Required(false)).
Param(webservice.QueryParameter(query.ParameterPage, "page").Required(false).DataFormat("page=%d").DefaultValue("page=1")).
Param(webservice.QueryParameter(query.ParameterLimit, "limit").Required(false)).
Param(webservice.QueryParameter(query.ParameterAscending, "sort parameters, e.g. reverse=true").Required(false).DefaultValue("ascending=false")).
Param(webservice.QueryParameter(query.ParameterOrderBy, "sort parameters, e.g. orderBy=createTime")).
Returns(http.StatusOK, ok, api.ListResult{}))
webservice.Route(webservice.GET("/namespaces/{namespace}/{resources}").
To(handler.handleGetNamespacedResource).
To(handler.handleListResources).
Metadata(restfulspec.KeyOpenAPITags, []string{tagNamespacedResource}).
Doc("Namespace level resource query").
Param(webservice.PathParameter("namespace", "the name of the project")).
Param(webservice.PathParameter("resources", "namespace level resource type, e.g. pods,jobs,configmaps,services.")).
Param(webservice.QueryParameter(query.ParameterName, "name used to do filtering").Required(false)).
Param(webservice.QueryParameter(query.ParameterPage, "page").Required(false).DataFormat("page=%d").DefaultValue("page=0")).
Param(webservice.QueryParameter(query.ParameterPage, "page").Required(false).DataFormat("page=%d").DefaultValue("page=1")).
Param(webservice.QueryParameter(query.ParameterLimit, "limit").Required(false)).
Param(webservice.QueryParameter(query.ParameterAscending, "sort parameters, e.g. reverse=true").Required(false).DefaultValue("ascending=false")).
Param(webservice.QueryParameter(query.ParameterOrderBy, "sort parameters, e.g. orderBy=createTime")).

View File

@@ -1,196 +1,60 @@
package v1alpha2
import (
"errors"
"github.com/emicklei/go-restful"
v1 "k8s.io/api/core/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/apiserver/request"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/models/monitoring"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
"kubesphere.io/kubesphere/pkg/models/tenant"
apierr "kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"net/http"
)
type tenantHandler struct {
tenant tenant.Interface
am am.AccessManagementInterface
}
func newTenantHandler(k8sClient k8s.Client, factory informers.InformerFactory, db *mysql.Database) *tenantHandler {
func newTenantHandler(k8sClient k8s.Client, factory informers.InformerFactory) *tenantHandler {
return &tenantHandler{
tenant: tenant.New(k8sClient.Kubernetes(), factory.KubernetesSharedInformerFactory(), factory.KubeSphereSharedInformerFactory(), db),
am: am.NewAMOperator(k8sClient.Kubernetes(), factory.KubernetesSharedInformerFactory()),
tenant: tenant.New(k8sClient, factory),
}
}
func (h *tenantHandler) ListWorkspaces(req *restful.Request, resp *restful.Response) {
username := req.HeaderParameter(constants.UserNameHeader)
orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, v1alpha2.CreateTime)
limit, offset := params.ParsePaging(req)
reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true)
conditions, err := params.ParseConditions(req)
user, ok := request.UserFrom(req.Request.Context())
if err != nil {
if !ok {
err := errors.New("cannot obtain user info")
klog.Errorln(err)
api.HandleBadRequest(resp, nil, err)
api.HandleForbidden(resp, nil, err)
return
}
result, err := h.tenant.ListWorkspaces(username, conditions, orderBy, reverse, limit, offset)
if err != nil {
api.HandleInternalError(resp, nil, err)
return
}
resp.WriteAsJson(result)
}
func (h *tenantHandler) DescribeWorkspace(req *restful.Request, resp *restful.Response) {
username := req.HeaderParameter(constants.UserNameHeader)
workspaceName := req.PathParameter("workspace")
result, err := h.tenant.DescribeWorkspace(username, workspaceName)
if err != nil {
if k8serr.IsNotFound(err) {
api.HandleNotFound(resp, nil, err)
} else {
api.HandleInternalError(resp, nil, err)
}
return
}
resp.WriteAsJson(result)
}
func (h *tenantHandler) ListNamespaces(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.PathParameter("member")
// /workspaces/{workspace}/members/{username}/namespaces
if username == "" {
// /workspaces/{workspace}/namespaces
username = req.HeaderParameter(constants.UserNameHeader)
}
orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, v1alpha2.CreateTime)
limit, offset := params.ParsePaging(req)
reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true)
conditions, err := params.ParseConditions(req)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
conditions.Match[constants.WorkspaceLabelKey] = workspace
result, err := h.tenant.ListNamespaces(username, conditions, orderBy, reverse, limit, offset)
if err != nil {
api.HandleInternalError(resp, nil, err)
return
}
namespaces := make([]*v1.Namespace, 0)
for _, item := range result.Items {
namespaces = append(namespaces, item.(*v1.Namespace).DeepCopy())
}
namespaces = monitoring.GetNamespacesWithMetrics(namespaces)
items := make([]interface{}, 0)
for _, item := range namespaces {
items = append(items, item)
}
result.Items = items
resp.WriteAsJson(result)
}
func (h *tenantHandler) CreateNamespace(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
var namespace v1.Namespace
err := req.ReadEntity(&namespace)
if err != nil {
api.HandleNotFound(resp, nil, err)
return
}
_, err = h.tenant.DescribeWorkspace("", workspace)
if err != nil {
if k8serr.IsNotFound(err) {
api.HandleForbidden(resp, nil, err)
} else {
api.HandleInternalError(resp, nil, err)
}
return
}
created, err := h.tenant.CreateNamespace(workspace, &namespace, username)
if err != nil {
if k8serr.IsAlreadyExists(err) {
resp.WriteHeaderAndEntity(http.StatusConflict, err)
} else {
api.HandleInternalError(resp, nil, err)
}
return
}
resp.WriteAsJson(created)
}
func (h *tenantHandler) DeleteNamespace(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
namespace := req.PathParameter("namespace")
err := h.tenant.DeleteNamespace(workspace, namespace)
if err != nil {
if k8serr.IsNotFound(err) {
api.HandleNotFound(resp, nil, err)
} else {
api.HandleInternalError(resp, nil, err)
}
return
}
resp.WriteAsJson(apierr.None)
}
func (h *tenantHandler) ListDevopsProjects(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.PathParameter("member")
if username == "" {
username = req.HeaderParameter(constants.UserNameHeader)
}
orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, v1alpha2.CreateTime)
limit, offset := params.ParsePaging(req)
reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true)
conditions, err := params.ParseConditions(req)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
conditions.Match["workspace"] = workspace
result, err := h.tenant.ListDevopsProjects(conditions, orderBy, reverse, limit, offset)
result, err := h.tenant.ListWorkspaces(user.GetName())
if err != nil {
api.HandleInternalError(resp, nil, err)
return
}
resp.WriteEntity(result)
}
func (h *tenantHandler) ListNamespaces(req *restful.Request, resp *restful.Response) {
user, ok := request.UserFrom(req.Request.Context())
if !ok {
err := errors.New("cannot obtain user info")
klog.Errorln(err)
api.HandleForbidden(resp, nil, err)
return
}
worksapceName := req.PathParameter("workspace")
result, err := h.tenant.ListNamespaces(worksapceName, user.GetName())
if err != nil {
api.HandleInternalError(resp, nil, err)

View File

@@ -23,16 +23,11 @@ import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"net/http"
)
@@ -42,61 +37,21 @@ const (
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
func AddToContainer(c *restful.Container, k8sClient k8s.Client, factory informers.InformerFactory, db *mysql.Database) error {
func AddToContainer(c *restful.Container, k8sClient k8s.Client, factory informers.InformerFactory) error {
ws := runtime.NewWebService(GroupVersion)
handler := newTenantHandler(k8sClient, factory, db)
handler := newTenantHandler(k8sClient, factory)
ws.Route(ws.GET("/workspaces").
To(handler.ListWorkspaces).
Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}).
Doc("List all workspaces that belongs to the current user").
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}").
To(handler.DescribeWorkspace).
Doc("Describe the specified workspace").
Param(ws.PathParameter("workspace", "workspace name")).
Returns(http.StatusOK, api.StatusOK, v1alpha1.Workspace{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/namespaces").
To(handler.ListNamespaces).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("List the namespaces of the specified workspace for the current user").
Returns(http.StatusOK, api.StatusOK, []v1.Namespace{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/members/{member}/namespaces").
To(handler.ListNamespaces).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("member", "workspace member's username")).
Doc("List the namespaces for the workspace member").
Returns(http.StatusOK, api.StatusOK, []v1.Namespace{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.POST("/workspaces/{workspace}/namespaces").
To(handler.CreateNamespace).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("Create a namespace in the specified workspace").
Returns(http.StatusOK, api.StatusOK, []v1.Namespace{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.DELETE("/workspaces/{workspace}/namespaces/{namespace}").
To(handler.DeleteNamespace).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("namespace", "the name of the namespace")).
Doc("Delete the specified namespace from the workspace").
Returns(http.StatusOK, api.StatusOK, errors.Error{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/devops").
To(handler.ListDevopsProjects).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.QueryParameter(params.PagingParam, "page").
Required(false).
DataFormat("limit=%d,page=%d").
DefaultValue("limit=10,page=1")).
Param(ws.QueryParameter(params.ConditionsParam, "query conditions").
Required(false).
DataFormat("key=%s,key~%s")).
Doc("List devops projects for the current user").
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
c.Add(ws)
return nil
}