feat: kubesphere 4.0 (#6115)
* feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> * feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> --------- Signed-off-by: ci-bot <ci-bot@kubesphere.io> Co-authored-by: ks-ci-bot <ks-ci-bot@example.com> Co-authored-by: joyceliu <joyceliu@yunify.com>
This commit is contained in:
committed by
GitHub
parent
b5015ec7b9
commit
447a51f08b
@@ -1,534 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 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 v2alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
"github.com/pkg/errors"
|
||||
prommodel "github.com/prometheus/common/model"
|
||||
"github.com/prometheus/prometheus/model/timestamp"
|
||||
"github.com/prometheus/prometheus/promql/parser"
|
||||
"github.com/prometheus/prometheus/template"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
RuleLevelCluster RuleLevel = "cluster"
|
||||
RuleLevelNamespace RuleLevel = "namespace"
|
||||
|
||||
AnnotationKeyRuleUpdateTime = "rule_update_time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrThanosRulerNotEnabled = errors.New("The request operation to custom alerting rule could not be done because thanos ruler is not enabled")
|
||||
ErrAlertingRuleNotFound = errors.New("The alerting rule was not found")
|
||||
ErrAlertingRuleAlreadyExists = errors.New("The alerting rule already exists")
|
||||
ErrAlertingAPIV2NotEnabled = errors.New("The alerting v2 API is not enabled")
|
||||
|
||||
templateTestData = template.AlertTemplateData(map[string]string{}, map[string]string{}, "", 0)
|
||||
templateTestTextPrefix = "{{$labels := .Labels}}{{$externalLabels := .ExternalLabels}}{{$value := .Value}}"
|
||||
|
||||
ruleNameMatcher = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`)
|
||||
)
|
||||
|
||||
type RuleLevel string
|
||||
|
||||
type AlertingRule struct {
|
||||
Id string `json:"id,omitempty" description:"rule id is only used by built-in alerting rules"`
|
||||
Name string `json:"name,omitempty" description:"rule name should be unique in one namespace for custom alerting rules"`
|
||||
|
||||
Query string `json:"query,omitempty" description:"prometheus query expression, grammars of which may be referred to https://prometheus.io/docs/prometheus/latest/querying/basics/"`
|
||||
Duration string `json:"duration,omitempty" description:"duration an alert transitions from Pending to Firing state, which must match ^([0-9]+)(y|w|d|h|m|s|ms)$"`
|
||||
Labels map[string]string `json:"labels,omitempty" description:"extra labels to attach to the resulting alert sample vectors (the key string has to match [a-zA-Z_][a-zA-Z0-9_]*). eg: a typical label called severity, whose value may be info, warning, error, critical, is usually used to indicate the severity of an alert"`
|
||||
Annotations map[string]string `json:"annotations,omitempty" description:"non-identifying key/value pairs. summary, message, description are the commonly used annotation names"`
|
||||
}
|
||||
|
||||
type PostableAlertingRule struct {
|
||||
AlertingRule `json:",omitempty"`
|
||||
}
|
||||
|
||||
func (r *PostableAlertingRule) Validate() error {
|
||||
errs := []error{}
|
||||
|
||||
if r.Name == "" {
|
||||
errs = append(errs, errors.New("name can not be empty"))
|
||||
} else {
|
||||
if !ruleNameMatcher.MatchString(r.Name) {
|
||||
errs = append(errs, errors.New("rule name must match regular expression ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$"))
|
||||
}
|
||||
}
|
||||
|
||||
if r.Query == "" {
|
||||
errs = append(errs, errors.New("query can not be empty"))
|
||||
} else if _, err := parser.ParseExpr(r.Query); err != nil {
|
||||
errs = append(errs, errors.Wrapf(err, "query is invalid: %s", r.Query))
|
||||
}
|
||||
if r.Duration != "" {
|
||||
if _, err := prommodel.ParseDuration(r.Duration); err != nil {
|
||||
errs = append(errs, errors.Wrapf(err, "duration is invalid: %s", r.Duration))
|
||||
}
|
||||
}
|
||||
|
||||
parseTest := func(text string) error {
|
||||
tmpl := template.NewTemplateExpander(
|
||||
context.TODO(),
|
||||
templateTestTextPrefix+text,
|
||||
"__alert_"+r.Name,
|
||||
templateTestData,
|
||||
prommodel.Time(timestamp.FromTime(time.Now())),
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
return tmpl.ParseTest()
|
||||
}
|
||||
|
||||
if len(r.Labels) > 0 {
|
||||
for name, v := range r.Labels {
|
||||
if !prommodel.LabelName(name).IsValid() || strings.HasPrefix(name, "__") {
|
||||
errs = append(errs, errors.Errorf(
|
||||
"label name (%s) is not valid. The name must match [a-zA-Z_][a-zA-Z0-9_]* and has not the __ prefix (label names with this prefix are for internal use)", name))
|
||||
}
|
||||
if !prommodel.LabelValue(v).IsValid() {
|
||||
errs = append(errs, errors.Errorf("invalid label value: %s", v))
|
||||
}
|
||||
if err := parseTest(v); err != nil {
|
||||
errs = append(errs, errors.Errorf("invalid label value: %s", v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.Annotations) > 0 {
|
||||
for name, v := range r.Annotations {
|
||||
if !prommodel.LabelName(name).IsValid() {
|
||||
errs = append(errs, errors.Errorf("invalid annotation name: %s", v))
|
||||
}
|
||||
if err := parseTest(v); err != nil {
|
||||
errs = append(errs, errors.Errorf("invalid annotation value: %s", v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return utilerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
type GettableAlertingRule struct {
|
||||
AlertingRule `json:",omitempty"`
|
||||
|
||||
State string `json:"state,omitempty" description:"state of a rule based on its alerts, one of firing, pending, inactive"`
|
||||
Health string `json:"health,omitempty" description:"health state of a rule based on the last execution, one of ok, err, unknown"`
|
||||
LastError string `json:"lastError,omitempty" description:"error for the last execution"`
|
||||
EvaluationDurationSeconds float64 `json:"evaluationTime,omitempty" description:"taken seconds for evaluation of query expression"`
|
||||
LastEvaluation *time.Time `json:"lastEvaluation,omitempty" description:"time for last evaluation of query expression"`
|
||||
|
||||
Alerts []*Alert `json:"alerts,omitempty" description:"alerts"`
|
||||
}
|
||||
|
||||
type GettableAlertingRuleList struct {
|
||||
Items []*GettableAlertingRule `json:"items"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type Alert struct {
|
||||
ActiveAt *time.Time `json:"activeAt,omitempty" description:"time when alert is active"`
|
||||
Annotations map[string]string `json:"annotations,omitempty" description:"annotations"`
|
||||
Labels map[string]string `json:"labels,omitempty" description:"labels"`
|
||||
State string `json:"state,omitempty" description:"state"`
|
||||
Value string `json:"value,omitempty" description:"the value at the last evaluation of the query expression"`
|
||||
|
||||
RuleId string `json:"ruleId,omitempty" description:"rule id triggering the alert"`
|
||||
RuleName string `json:"ruleName,omitempty" description:"rule name triggering the alert"`
|
||||
}
|
||||
|
||||
type AlertList struct {
|
||||
Items []*Alert `json:"items"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type AlertingRuleQueryParams struct {
|
||||
NameContainFilter string
|
||||
State string
|
||||
Health string
|
||||
LabelEqualFilters map[string]string
|
||||
LabelContainFilters map[string]string
|
||||
|
||||
PageNum int
|
||||
Limit int
|
||||
SortField string
|
||||
SortType string
|
||||
}
|
||||
|
||||
func (q *AlertingRuleQueryParams) Filter(rules []*GettableAlertingRule) []*GettableAlertingRule {
|
||||
var ret []*GettableAlertingRule
|
||||
for _, rule := range rules {
|
||||
if rule == nil {
|
||||
continue
|
||||
}
|
||||
if q == nil || q.matches(rule) {
|
||||
ret = append(ret, rule)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (q *AlertingRuleQueryParams) matches(rule *GettableAlertingRule) bool {
|
||||
if q.NameContainFilter != "" && !containsCaseInsensitive(rule.Name, q.NameContainFilter) {
|
||||
return false
|
||||
}
|
||||
if q.State != "" && q.State != rule.State {
|
||||
return false
|
||||
}
|
||||
if q.Health != "" && q.Health != rule.Health {
|
||||
return false
|
||||
}
|
||||
if len(rule.Labels) == 0 {
|
||||
return len(q.LabelEqualFilters) == 0 && len(q.LabelContainFilters) == 0
|
||||
}
|
||||
for k, v := range q.LabelEqualFilters {
|
||||
if fv, ok := rule.Labels[k]; !ok || fv != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for k, v := range q.LabelContainFilters {
|
||||
if fv, ok := rule.Labels[k]; !ok || !containsCaseInsensitive(fv, v) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AlertingRuleIdCompare defines the default order for the alerting rules.
|
||||
// For the alerting rule list, it guarantees a stable sort. For the custom alerting rules with possible same names
|
||||
// and the builtin alerting rules with possible same ids, it guarantees the stability of get operations.
|
||||
func AlertingRuleIdCompare(leftId, rightId string) bool {
|
||||
// default to ascending order of id
|
||||
return leftId <= rightId
|
||||
}
|
||||
|
||||
func (q *AlertingRuleQueryParams) Sort(rules []*GettableAlertingRule) {
|
||||
baseCompare := func(left, right *GettableAlertingRule) bool {
|
||||
var leftUpdateTime, rightUpdateTime string
|
||||
if len(left.Annotations) > 0 {
|
||||
leftUpdateTime = left.Annotations[AnnotationKeyRuleUpdateTime]
|
||||
}
|
||||
if len(right.Annotations) > 0 {
|
||||
rightUpdateTime = right.Annotations[AnnotationKeyRuleUpdateTime]
|
||||
}
|
||||
|
||||
if leftUpdateTime != rightUpdateTime {
|
||||
return leftUpdateTime > rightUpdateTime
|
||||
}
|
||||
|
||||
return AlertingRuleIdCompare(left.Id, right.Id)
|
||||
}
|
||||
var compare = baseCompare
|
||||
if q != nil {
|
||||
reverse := q.SortType == "desc"
|
||||
switch q.SortField {
|
||||
case "name":
|
||||
compare = func(left, right *GettableAlertingRule) bool {
|
||||
if c := strings.Compare(left.Name, right.Name); c != 0 {
|
||||
if reverse {
|
||||
return c > 0
|
||||
}
|
||||
return c < 0
|
||||
}
|
||||
return baseCompare(left, right)
|
||||
}
|
||||
case "lastEvaluation":
|
||||
compare = func(left, right *GettableAlertingRule) bool {
|
||||
if left.LastEvaluation == nil {
|
||||
if right.LastEvaluation != nil {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if right.LastEvaluation == nil {
|
||||
return true
|
||||
} else if left.LastEvaluation.Equal(*right.LastEvaluation) {
|
||||
if reverse {
|
||||
return left.LastEvaluation.After(*right.LastEvaluation)
|
||||
}
|
||||
return left.LastEvaluation.Before(*right.LastEvaluation)
|
||||
}
|
||||
}
|
||||
return baseCompare(left, right)
|
||||
}
|
||||
case "evaluationTime":
|
||||
compare = func(left, right *GettableAlertingRule) bool {
|
||||
if left.EvaluationDurationSeconds != right.EvaluationDurationSeconds {
|
||||
if reverse {
|
||||
return left.EvaluationDurationSeconds > right.EvaluationDurationSeconds
|
||||
}
|
||||
return left.EvaluationDurationSeconds < right.EvaluationDurationSeconds
|
||||
}
|
||||
return baseCompare(left, right)
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Slice(rules, func(i, j int) bool {
|
||||
return compare(rules[i], rules[j])
|
||||
})
|
||||
}
|
||||
|
||||
func (q *AlertingRuleQueryParams) Sub(rules []*GettableAlertingRule) []*GettableAlertingRule {
|
||||
start, stop := 0, 10
|
||||
if q != nil {
|
||||
start, stop = (q.PageNum-1)*q.Limit, q.PageNum*q.Limit
|
||||
}
|
||||
total := len(rules)
|
||||
if start < total {
|
||||
if stop > total {
|
||||
stop = total
|
||||
}
|
||||
return rules[start:stop]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type AlertQueryParams struct {
|
||||
State string
|
||||
LabelEqualFilters map[string]string
|
||||
LabelContainFilters map[string]string
|
||||
|
||||
PageNum int
|
||||
Limit int
|
||||
}
|
||||
|
||||
func (q *AlertQueryParams) Filter(alerts []*Alert) []*Alert {
|
||||
var ret []*Alert
|
||||
for _, alert := range alerts {
|
||||
if alert == nil {
|
||||
continue
|
||||
}
|
||||
if q == nil || q.matches(alert) {
|
||||
ret = append(ret, alert)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (q *AlertQueryParams) matches(alert *Alert) bool {
|
||||
if q.State != "" && q.State != alert.State {
|
||||
return false
|
||||
}
|
||||
if len(alert.Labels) == 0 {
|
||||
return len(q.LabelEqualFilters) == 0 && len(q.LabelContainFilters) == 0
|
||||
}
|
||||
for k, v := range q.LabelEqualFilters {
|
||||
if fv, ok := alert.Labels[k]; !ok || fv != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for k, v := range q.LabelContainFilters {
|
||||
if fv, ok := alert.Labels[k]; !ok || !containsCaseInsensitive(fv, v) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (q *AlertQueryParams) Sort(alerts []*Alert) {
|
||||
compare := func(left, right *Alert) bool {
|
||||
if left.ActiveAt == nil {
|
||||
if right.ActiveAt != nil {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if right.ActiveAt == nil {
|
||||
return true
|
||||
} else if !left.ActiveAt.Equal(*right.ActiveAt) {
|
||||
return left.ActiveAt.After(*right.ActiveAt)
|
||||
}
|
||||
}
|
||||
return prommodel.LabelsToSignature(left.Labels) <= prommodel.LabelsToSignature(right.Labels)
|
||||
}
|
||||
sort.Slice(alerts, func(i, j int) bool {
|
||||
return compare(alerts[i], alerts[j])
|
||||
})
|
||||
}
|
||||
|
||||
func (q *AlertQueryParams) Sub(alerts []*Alert) []*Alert {
|
||||
start, stop := 0, 10
|
||||
if q != nil {
|
||||
start, stop = (q.PageNum-1)*q.Limit, q.PageNum*q.Limit
|
||||
}
|
||||
total := len(alerts)
|
||||
if start < total {
|
||||
if stop > total {
|
||||
stop = total
|
||||
}
|
||||
return alerts[start:stop]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseAlertingRuleQueryParams(req *restful.Request) (*AlertingRuleQueryParams, error) {
|
||||
var (
|
||||
q = &AlertingRuleQueryParams{}
|
||||
err error
|
||||
)
|
||||
|
||||
q.NameContainFilter = req.QueryParameter("name")
|
||||
q.State = req.QueryParameter("state")
|
||||
q.Health = req.QueryParameter("health")
|
||||
q.PageNum, err = strconv.Atoi(req.QueryParameter("page"))
|
||||
if err != nil {
|
||||
q.PageNum = 1
|
||||
}
|
||||
if q.PageNum <= 0 {
|
||||
q.PageNum = 1
|
||||
}
|
||||
q.Limit, err = strconv.Atoi(req.QueryParameter("limit"))
|
||||
if err != nil {
|
||||
q.Limit = 10
|
||||
err = nil
|
||||
}
|
||||
q.LabelEqualFilters, q.LabelContainFilters = parseLabelFilters(req)
|
||||
q.SortField = req.QueryParameter("sort_field")
|
||||
q.SortType = req.QueryParameter("sort_type")
|
||||
return q, err
|
||||
}
|
||||
|
||||
func ParseAlertQueryParams(req *restful.Request) (*AlertQueryParams, error) {
|
||||
var (
|
||||
q = &AlertQueryParams{}
|
||||
err error
|
||||
)
|
||||
|
||||
q.State = req.QueryParameter("state")
|
||||
q.PageNum, err = strconv.Atoi(req.QueryParameter("page"))
|
||||
if err != nil {
|
||||
q.PageNum = 1
|
||||
}
|
||||
if q.PageNum <= 0 {
|
||||
q.PageNum = 1
|
||||
}
|
||||
q.Limit, err = strconv.Atoi(req.QueryParameter("limit"))
|
||||
if err != nil {
|
||||
q.Limit = 10
|
||||
err = nil
|
||||
}
|
||||
q.LabelEqualFilters, q.LabelContainFilters = parseLabelFilters(req)
|
||||
return q, err
|
||||
}
|
||||
|
||||
func parseLabelFilters(req *restful.Request) (map[string]string, map[string]string) {
|
||||
var (
|
||||
labelEqualFilters = make(map[string]string)
|
||||
labelContainFilters = make(map[string]string)
|
||||
labelFiltersString = req.QueryParameter("label_filters")
|
||||
)
|
||||
for _, filter := range strings.Split(labelFiltersString, ",") {
|
||||
if i := strings.Index(filter, "="); i > 0 && len(filter) > i+1 {
|
||||
labelEqualFilters[filter[:i]] = filter[i+1:]
|
||||
} else if i := strings.Index(filter, "~"); i > 0 && len(filter) > i+1 {
|
||||
labelContainFilters[filter[:i]] = filter[i+1:]
|
||||
}
|
||||
}
|
||||
return labelEqualFilters, labelContainFilters
|
||||
}
|
||||
|
||||
const (
|
||||
ErrBadData ErrorType = "bad_data"
|
||||
ErrDuplicateName ErrorType = "duplicate_name"
|
||||
ErrNotFound ErrorType = "not_found"
|
||||
ErrServer ErrorType = "server_error"
|
||||
|
||||
StatusSuccess Status = "success"
|
||||
StatusError Status = "error"
|
||||
|
||||
ResultCreated Result = "created"
|
||||
ResultUpdated Result = "updated"
|
||||
ResultDeleted Result = "deleted"
|
||||
)
|
||||
|
||||
type Status string
|
||||
|
||||
type ErrorType string
|
||||
|
||||
type Result string
|
||||
|
||||
type BulkResponse struct {
|
||||
Errors bool `json:"errors" description:"If true, one or more operations in the bulk request don't complete successfully"`
|
||||
Items []*BulkItemResponse `json:"items" description:"It contains the result of each operation in the bulk request"`
|
||||
}
|
||||
|
||||
// MakeBulkResponse tidies the internal items and sets the errors
|
||||
func (br *BulkResponse) MakeBulkResponse() *BulkResponse {
|
||||
var (
|
||||
items []*BulkItemResponse
|
||||
itemMap = make(map[string]*BulkItemResponse)
|
||||
)
|
||||
for i, item := range br.Items {
|
||||
if item.Status == StatusError {
|
||||
br.Errors = true
|
||||
}
|
||||
pitem, ok := itemMap[item.RuleName]
|
||||
if !ok || (pitem.Status == StatusSuccess || item.Status == StatusError) {
|
||||
itemMap[item.RuleName] = br.Items[i]
|
||||
}
|
||||
}
|
||||
for k := range itemMap {
|
||||
item := itemMap[k]
|
||||
if item.Error != nil {
|
||||
item.ErrorStr = item.Error.Error()
|
||||
}
|
||||
items = append(items, itemMap[k])
|
||||
}
|
||||
br.Items = items
|
||||
return br
|
||||
}
|
||||
|
||||
type BulkItemResponse struct {
|
||||
RuleName string `json:"ruleName,omitempty"`
|
||||
Status Status `json:"status,omitempty" description:"It may be success or error"`
|
||||
Result Result `json:"result,omitempty" description:"It may be created, updated or deleted, and only for successful operations"`
|
||||
ErrorType ErrorType `json:"errorType,omitempty" description:"It may be bad_data, duplicate_name, not_found or server_error, and only for failed operations"`
|
||||
Error error `json:"-"`
|
||||
ErrorStr string `json:"error,omitempty" description:"It is only returned for failed operations"`
|
||||
}
|
||||
|
||||
func NewBulkItemSuccessResponse(ruleName string, result Result) *BulkItemResponse {
|
||||
return &BulkItemResponse{
|
||||
RuleName: ruleName,
|
||||
Status: StatusSuccess,
|
||||
Result: result,
|
||||
}
|
||||
}
|
||||
|
||||
func NewBulkItemErrorServerResponse(ruleName string, err error) *BulkItemResponse {
|
||||
return &BulkItemResponse{
|
||||
RuleName: ruleName,
|
||||
Status: StatusError,
|
||||
ErrorType: ErrServer,
|
||||
Error: err,
|
||||
}
|
||||
}
|
||||
|
||||
// containsCaseInsensitive reports whether substr is case-insensitive within s.
|
||||
func containsCaseInsensitive(s, substr string) bool {
|
||||
return strings.Contains(
|
||||
strings.ToLower(s),
|
||||
strings.ToLower(substr))
|
||||
}
|
||||
@@ -1,384 +0,0 @@
|
||||
package v2alpha1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
"github.com/prometheus/prometheus/rules"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
alertingRule = &AlertingRule{
|
||||
Id: "ca7f09e76954e67c",
|
||||
Name: "test-cpu-usage-high",
|
||||
Query: `namespace:workload_cpu_usage:sum{namespace="test"} > 1`,
|
||||
Duration: "1m",
|
||||
Annotations: map[string]string{
|
||||
"alias": "The alias is here",
|
||||
"description": "The description is here",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"flags": "The flags is here",
|
||||
"cpu_num": "The cpu_num is here",
|
||||
},
|
||||
}
|
||||
|
||||
postableAlertingRule = &PostableAlertingRule{
|
||||
AlertingRule: *alertingRule,
|
||||
}
|
||||
|
||||
gettableAlertingRule = &GettableAlertingRule{
|
||||
AlertingRule: *alertingRule,
|
||||
Health: string(rules.HealthGood),
|
||||
State: rules.StateInactive.String(),
|
||||
}
|
||||
|
||||
gettableAlertingRules = []*GettableAlertingRule{
|
||||
{
|
||||
AlertingRule: *alertingRule,
|
||||
Health: string(rules.HealthGood),
|
||||
State: rules.StateInactive.String(),
|
||||
EvaluationDurationSeconds: 1,
|
||||
LastEvaluation: &time.Time{},
|
||||
},
|
||||
{
|
||||
AlertingRule: AlertingRule{
|
||||
Id: "ca7f09e76954e688",
|
||||
Name: "test-cpu-usage-high-2",
|
||||
Query: `namespace:workload_cpu_usage:sum{namespace="test"} > 1`,
|
||||
Duration: "1m",
|
||||
Annotations: map[string]string{
|
||||
"alias": "The alias is here",
|
||||
"description": "The description is here",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"flags": "The flags is here",
|
||||
"cpu_num": "The cpu_num is here",
|
||||
},
|
||||
},
|
||||
State: rules.StateInactive.String(),
|
||||
Health: string(rules.HealthGood),
|
||||
EvaluationDurationSeconds: 0,
|
||||
LastEvaluation: &time.Time{},
|
||||
},
|
||||
}
|
||||
|
||||
alertingRuleQueryParams = &AlertingRuleQueryParams{
|
||||
NameContainFilter: "test-cpu-usage-high",
|
||||
State: rules.StateInactive.String(),
|
||||
Health: string(rules.HealthGood),
|
||||
LabelEqualFilters: map[string]string{
|
||||
"flags": "The flags is here",
|
||||
"cpu_num": "The cpu_num is here",
|
||||
},
|
||||
LabelContainFilters: map[string]string{
|
||||
"alias": "The alias is here",
|
||||
"description": "The description is here",
|
||||
},
|
||||
PageNum: 1,
|
||||
Limit: 10,
|
||||
SortField: "name",
|
||||
SortType: "desc",
|
||||
}
|
||||
)
|
||||
|
||||
func TestPostableAlertingRule_Validate(t *testing.T) {
|
||||
// validate AlertingRule name field
|
||||
// Name is empty
|
||||
postableAlertingRule.Name = ""
|
||||
err := postableAlertingRule.Validate()
|
||||
assert.Equal(t, "name can not be empty", err.Error())
|
||||
|
||||
// Name do not match regx
|
||||
postableAlertingRule.Name = "TestCPUUsageHigh"
|
||||
err = postableAlertingRule.Validate()
|
||||
assert.Equal(t, "rule name must match regular expression ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$", err.Error())
|
||||
|
||||
// validate AlertingRule Query field
|
||||
postableAlertingRule.Name = "test-cpu-usage-high"
|
||||
// Query is empty
|
||||
postableAlertingRule.Query = ""
|
||||
err = postableAlertingRule.Validate()
|
||||
assert.Equal(t, "query can not be empty", err.Error())
|
||||
|
||||
postableAlertingRule.Query = `namespace:workload_cpu_usage:sum{namespace="test"} > 1`
|
||||
// test no error
|
||||
|
||||
err = postableAlertingRule.Validate()
|
||||
assert.Empty(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestAlertQueryParams_Filter(t *testing.T) {
|
||||
// empty
|
||||
gettableAlertingRules := []*GettableAlertingRule{}
|
||||
queryParams := &AlertingRuleQueryParams{}
|
||||
ret := queryParams.Filter(gettableAlertingRules)
|
||||
assert.Empty(t, ret)
|
||||
|
||||
queryParams.NameContainFilter = "test-cpu-usage-high"
|
||||
gettableAlertingRules = []*GettableAlertingRule{
|
||||
gettableAlertingRule,
|
||||
}
|
||||
ret = queryParams.Filter(gettableAlertingRules)
|
||||
assert.NotEmpty(t, ret)
|
||||
}
|
||||
|
||||
func TestAlertQueryParams_match(t *testing.T) {
|
||||
// empty
|
||||
rule := &GettableAlertingRule{}
|
||||
queryParams := &AlertingRuleQueryParams{}
|
||||
ret := queryParams.matches(rule)
|
||||
assert.True(t, ret)
|
||||
|
||||
// Not empty
|
||||
// test false case
|
||||
queryParams.NameContainFilter = "test-cpu"
|
||||
ret = queryParams.matches(rule)
|
||||
assert.False(t, ret)
|
||||
|
||||
//test true case
|
||||
rule = gettableAlertingRule
|
||||
queryParams.NameContainFilter = "test-cpu-usage-high"
|
||||
ret = queryParams.matches(rule)
|
||||
assert.True(t, ret)
|
||||
}
|
||||
|
||||
func TestAlertingRuleIdCompare(t *testing.T) {
|
||||
leftId := "test-id-1"
|
||||
rightId := "test-id-2"
|
||||
|
||||
ret := AlertingRuleIdCompare(leftId, rightId)
|
||||
assert.True(t, ret)
|
||||
}
|
||||
|
||||
func TestAlertQueryParams_Sort(t *testing.T) {
|
||||
queryParams := alertingRuleQueryParams
|
||||
rules := []*GettableAlertingRule{
|
||||
gettableAlertingRule,
|
||||
gettableAlertingRule,
|
||||
}
|
||||
|
||||
// sort by name
|
||||
queryParams.Sort(rules)
|
||||
|
||||
// sort by lastEvaluation
|
||||
queryParams.SortField = "lastEvaluation"
|
||||
queryParams.Sort(rules)
|
||||
|
||||
// sort by evaluationTime
|
||||
queryParams.SortField = "evaluationTime"
|
||||
queryParams.Sort(rules)
|
||||
}
|
||||
|
||||
func TestAlertQueryParams_Sub(t *testing.T) {
|
||||
rules := gettableAlertingRules
|
||||
queryParams := alertingRuleQueryParams
|
||||
queryParams.Sub(rules)
|
||||
}
|
||||
|
||||
var (
|
||||
alertQueryParams = &AlertQueryParams{
|
||||
State: rules.StateFiring.String(),
|
||||
LabelEqualFilters: map[string]string{
|
||||
"alias": "The alias is here",
|
||||
"description": "The description is here",
|
||||
},
|
||||
LabelContainFilters: map[string]string{
|
||||
"alias": "The alias is here",
|
||||
"description": "The description is here",
|
||||
},
|
||||
PageNum: 1,
|
||||
Limit: 10,
|
||||
}
|
||||
|
||||
alert = &Alert{
|
||||
RuleId: "ca7f09e76954e67c",
|
||||
RuleName: "test-cpu-usage-high",
|
||||
State: rules.StateFiring.String(),
|
||||
Value: `namespace:workload_cpu_usage:sum{namespace="test"} > 1`,
|
||||
Annotations: map[string]string{
|
||||
"alias": "The alias is here",
|
||||
"description": "The description is here",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alias": "The alias is here",
|
||||
"description": "The description is here",
|
||||
},
|
||||
ActiveAt: &time.Time{},
|
||||
}
|
||||
|
||||
alerts = []*Alert{
|
||||
alert,
|
||||
{
|
||||
RuleId: "ca7f09e76954e699",
|
||||
RuleName: "test-cpu-usage-high-2",
|
||||
State: rules.StateFiring.String(),
|
||||
Value: `namespace:workload_cpu_usage:sum{namespace="test"} > 1`,
|
||||
Annotations: map[string]string{
|
||||
"alias": "The alias is here",
|
||||
"description": "The description is here",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alias": "The alias is here",
|
||||
"description": "The description is here",
|
||||
},
|
||||
ActiveAt: &time.Time{},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestAlertingRuleQueryParams_Filter(t *testing.T) {
|
||||
// empty
|
||||
// Alert Array is empty
|
||||
loacalAlerts := []*Alert{}
|
||||
queryParam := &AlertQueryParams{}
|
||||
ret := queryParam.Filter(loacalAlerts)
|
||||
assert.Empty(t, ret)
|
||||
|
||||
// Alert Array has nil
|
||||
loacalAlerts = []*Alert{nil}
|
||||
ret = queryParam.Filter(loacalAlerts)
|
||||
assert.Empty(t, ret)
|
||||
|
||||
// all pass
|
||||
loacalAlerts = alerts
|
||||
ret = queryParam.Filter(loacalAlerts)
|
||||
assert.NotEmpty(t, ret)
|
||||
}
|
||||
|
||||
func TestAlertingRuleQueryParams_matches(t *testing.T) {
|
||||
loacalAlert := alert
|
||||
queryParam := alertQueryParams
|
||||
queryParam.LabelEqualFilters = map[string]string{
|
||||
"flags": "The flags is here",
|
||||
"cpu_num": "The cpu_num is here",
|
||||
}
|
||||
ret := queryParam.matches(loacalAlert)
|
||||
assert.False(t, ret)
|
||||
|
||||
queryParam.LabelEqualFilters = map[string]string{
|
||||
"alias": "The alias is here",
|
||||
"description": "The description is here",
|
||||
}
|
||||
ret = queryParam.matches(loacalAlert)
|
||||
assert.True(t, ret)
|
||||
}
|
||||
|
||||
func TestAlertingRuleQueryParams_Sort(t *testing.T) {
|
||||
loacalAlerts := alerts
|
||||
queryParam := alertQueryParams
|
||||
queryParam.Sort(loacalAlerts)
|
||||
}
|
||||
|
||||
func TestAlertingRuleQueryParams_Sub(t *testing.T) {
|
||||
loacalAlerts := alerts
|
||||
queryParam := alertQueryParams
|
||||
queryParam.Sub(loacalAlerts)
|
||||
}
|
||||
|
||||
var (
|
||||
succBulkItemResponse = BulkItemResponse{
|
||||
RuleName: "test-cpu-usage-high",
|
||||
Status: StatusSuccess,
|
||||
Result: ResultCreated,
|
||||
ErrorType: "",
|
||||
Error: nil,
|
||||
ErrorStr: "",
|
||||
}
|
||||
|
||||
errBulkItemResponse = BulkItemResponse{
|
||||
RuleName: "test-mem-usage-high",
|
||||
Status: StatusError,
|
||||
Result: ResultUpdated,
|
||||
ErrorType: ErrBadData,
|
||||
Error: errors.New(string(ErrBadData)),
|
||||
ErrorStr: string(ErrBadData),
|
||||
}
|
||||
|
||||
bulkResponse = BulkResponse{
|
||||
Items: []*BulkItemResponse{
|
||||
&succBulkItemResponse,
|
||||
&errBulkItemResponse,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestParseAlertingRuleQueryParams(t *testing.T) {
|
||||
queryParam := "name=test-cpu&state=firing&health=ok&sort_field=lastEvaluation&sort_type=desc&label_filters=name~test"
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost/alerting.kubesphere.io/v2alpha1/rules?%s", queryParam), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := AlertingRuleQueryParams{
|
||||
NameContainFilter: "test-cpu",
|
||||
State: "firing",
|
||||
Health: "ok",
|
||||
LabelEqualFilters: make(map[string]string),
|
||||
LabelContainFilters: map[string]string{"name": "test"},
|
||||
|
||||
PageNum: 1,
|
||||
Limit: 10,
|
||||
SortField: "lastEvaluation",
|
||||
SortType: "desc",
|
||||
}
|
||||
|
||||
request := restful.NewRequest(req)
|
||||
actual, err := ParseAlertingRuleQueryParams(request)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &expected, actual)
|
||||
}
|
||||
|
||||
func TestParseAlertQueryParams(t *testing.T) {
|
||||
queryParam := "state=firing&label_filters=name~test"
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost/alerting.kubesphere.io/v2alpha1/alerts?%s", queryParam), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := AlertQueryParams{
|
||||
State: "firing",
|
||||
LabelEqualFilters: make(map[string]string),
|
||||
LabelContainFilters: map[string]string{"name": "test"},
|
||||
|
||||
PageNum: 1,
|
||||
Limit: 10,
|
||||
}
|
||||
|
||||
request := restful.NewRequest(req)
|
||||
actual, err := ParseAlertQueryParams(request)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &expected, actual)
|
||||
}
|
||||
|
||||
func TestBulkResponse_MakeBulkResponse(t *testing.T) {
|
||||
br := bulkResponse.MakeBulkResponse()
|
||||
assert.True(t, br.Errors)
|
||||
}
|
||||
|
||||
func TestBulkResponse_NewBulkItemSuccessResponse(t *testing.T) {
|
||||
ruleName := "test-cpu-usage-high"
|
||||
result := ResultCreated
|
||||
ret := NewBulkItemSuccessResponse(ruleName, result)
|
||||
assert.Equal(t, ResultCreated, ret.Result)
|
||||
}
|
||||
|
||||
func TestBulkResponse_NewBulkItemErrorServerResponse(t *testing.T) {
|
||||
ruleName := "test-cpu-usage-high"
|
||||
err := errors.New(string(ErrBadData))
|
||||
ret := NewBulkItemErrorServerResponse(ruleName, err)
|
||||
assert.Equal(t, errors.New(string(ErrBadData)), ret.Error)
|
||||
}
|
||||
|
||||
func TestContainsCaseInsensitive(t *testing.T) {
|
||||
left := "left"
|
||||
right := "RIGHT"
|
||||
ret := containsCaseInsensitive(left, right)
|
||||
assert.False(t, ret)
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 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 v2beta1
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
alertingv2beta1 "kubesphere.io/api/alerting/v2beta1"
|
||||
)
|
||||
|
||||
const (
|
||||
// for rulegroup/alert
|
||||
FieldState = "state"
|
||||
FieldBuiltin = "builtin"
|
||||
|
||||
// for rulegroup
|
||||
FieldRuleGroupEvaluationTime = "evaluationTime"
|
||||
FieldRuleGroupLastEvaluation = "lastEvalution"
|
||||
|
||||
// for alert
|
||||
FieldAlertLabelFilters = "label_filters"
|
||||
FieldAlertActiveAt = "activeAt"
|
||||
FieldAlertLabelMatcher = "label_matcher"
|
||||
)
|
||||
|
||||
var SortableFields = []string{
|
||||
FieldRuleGroupEvaluationTime,
|
||||
FieldRuleGroupLastEvaluation,
|
||||
FieldAlertActiveAt,
|
||||
}
|
||||
|
||||
var ComparableFields = []string{
|
||||
FieldState,
|
||||
FieldAlertLabelFilters,
|
||||
}
|
||||
|
||||
type RuleGroup struct {
|
||||
alertingv2beta1.RuleGroup `json:",inline"`
|
||||
Status RuleGroupStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
type ClusterRuleGroup struct {
|
||||
alertingv2beta1.ClusterRuleGroup `json:",inline"`
|
||||
Status RuleGroupStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
type GlobalRuleGroup struct {
|
||||
alertingv2beta1.GlobalRuleGroup `json:",inline"`
|
||||
Status RuleGroupStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
type RuleGroupStatus struct {
|
||||
EvaluationTime *float64 `json:"evaluationTime,omitempty" description:"time spent on rule group evaluation in seconds"`
|
||||
LastEvaluation *time.Time `json:"lastEvaluation,omitempty" description:"time of last evaluation"`
|
||||
RulesStatus []RuleStatus `json:"rulesStatus,omitempty" description:"status of rules in one RuleGroup"`
|
||||
RulesStats RulesStats `json:"rulesStats,omitempty" description:"statistics of rules in one RuleGroup"`
|
||||
}
|
||||
|
||||
type RulesStats struct {
|
||||
Inactive int `json:"inactive" description:"count of rules in the inactive state"`
|
||||
Pending int `json:"pending" description:"count of rules in the pending state"`
|
||||
Firing int `json:"firing" description:"count of rules in the firing state"`
|
||||
Disabled int `json:"disabled" description:"count of disabled rules"`
|
||||
}
|
||||
|
||||
type RuleStatus struct {
|
||||
Expr string `json:"expr,omitempty" description:"expression evaluated, for global rules only"`
|
||||
State string `json:"state,omitempty" description:"state of a rule, one of firing, pending, inactive or disabled depending on the rule and its alerts"`
|
||||
Health string `json:"health,omitempty" description:"health state of a rule, one of ok, err, unknown depending on the last execution result"`
|
||||
LastError string `json:"lastError,omitempty" description:"error of the last evaluation"`
|
||||
EvaluationTime *float64 `json:"evaluationTime,omitempty" description:"time spent on the expression evaluation in seconds"`
|
||||
LastEvaluation *time.Time `json:"lastEvaluation,omitempty" description:"time of last evaluation"`
|
||||
ActiveAt *time.Time `json:"activeAt,omitempty" description:"time when this rule became active"`
|
||||
|
||||
Alerts []*Alert `json:"alerts,omitempty" description:"alerts"`
|
||||
}
|
||||
|
||||
type Alert struct {
|
||||
ActiveAt *time.Time `json:"activeAt,omitempty" description:"time when this alert became active"`
|
||||
Annotations map[string]string `json:"annotations,omitempty" description:"annotations"`
|
||||
Labels map[string]string `json:"labels,omitempty" description:"labels"`
|
||||
State string `json:"state,omitempty" description:"state"`
|
||||
Value string `json:"value,omitempty" description:"the value from the last expression evaluation"`
|
||||
}
|
||||
|
||||
type LabelFilterOperator string
|
||||
|
||||
const (
|
||||
LabelFilterOperatorEqual = "="
|
||||
LabelFilterOperatorContain = "~"
|
||||
)
|
||||
|
||||
type LabelFilter struct {
|
||||
LabelName string
|
||||
LabelValue string
|
||||
Operator LabelFilterOperator
|
||||
}
|
||||
|
||||
func (f *LabelFilter) Matches(labels map[string]string) bool {
|
||||
v, ok := labels[f.LabelName]
|
||||
if ok {
|
||||
switch f.Operator {
|
||||
case LabelFilterOperatorEqual:
|
||||
return v == f.LabelValue
|
||||
case LabelFilterOperatorContain:
|
||||
return strings.Contains(v, f.LabelValue)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type LabelFilters []LabelFilter
|
||||
|
||||
func (fs LabelFilters) Matches(labels map[string]string) bool {
|
||||
for _, f := range fs {
|
||||
if !f.Matches(labels) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func ParseLabelFilters(filters string) LabelFilters {
|
||||
var fs LabelFilters
|
||||
for _, filter := range strings.Split(filters, ",") {
|
||||
if i := strings.Index(filter, LabelFilterOperatorEqual); i > 0 {
|
||||
fs = append(fs, LabelFilter{
|
||||
Operator: LabelFilterOperatorEqual,
|
||||
LabelName: filter[:i],
|
||||
LabelValue: filter[i+1:],
|
||||
})
|
||||
} else if i := strings.Index(filter, LabelFilterOperatorContain); i > 0 {
|
||||
fs = append(fs, LabelFilter{
|
||||
Operator: LabelFilterOperatorContain,
|
||||
LabelName: filter[:i],
|
||||
LabelValue: filter[i+1:],
|
||||
})
|
||||
}
|
||||
}
|
||||
return fs
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 The KubeSphere Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/auditing"
|
||||
)
|
||||
|
||||
type APIResponse struct {
|
||||
Events *auditing.Events `json:"query,omitempty" description:"query results"`
|
||||
Statistics *auditing.Statistics `json:"statistics,omitempty" description:"statistics results"`
|
||||
Histogram *auditing.Histogram `json:"histogram,omitempty" description:"histogram results"`
|
||||
}
|
||||
|
||||
type Query struct {
|
||||
Operation string `json:"operation,omitempty"`
|
||||
WorkspaceFilter string `json:"workspace_filter,omitempty"`
|
||||
WorkspaceSearch string `json:"workspace_search,omitempty"`
|
||||
ObjectRefNamespaceFilter string `json:"objectref_namespace_filter,omitempty"`
|
||||
ObjectRefNamespaceSearch string `json:"objectref_namespace_search,omitempty"`
|
||||
ObjectRefNameFilter string `json:"objectref_name_filter,omitempty"`
|
||||
ObjectRefNameSearch string `json:"objectref_name_search,omitempty"`
|
||||
LevelFilter string `json:"level_filter,omitempty"`
|
||||
VerbFilter string `json:"verb_filter,omitempty"`
|
||||
UserFilter string `json:"user_filter,omitempty"`
|
||||
UserSearch string `json:"user_search,omitempty"`
|
||||
GroupSearch string `json:"group_search,omitempty"`
|
||||
SourceIpSearch string `json:"source_ip_search,omitempty"`
|
||||
ObjectRefResourceFilter string `json:"objectref_resource_filter,omitempty"`
|
||||
ObjectRefSubresourceFilter string `json:"objectref_subresource_filter,omitempty"`
|
||||
ResponseCodeFilter string `json:"response_code_filter,omitempty"`
|
||||
ResponseStatusFilter string `json:"response_status_filter,omitempty"`
|
||||
|
||||
StartTime time.Time `json:"start_time,omitempty"`
|
||||
EndTime time.Time `json:"end_time,omitempty"`
|
||||
|
||||
Interval string `json:"interval,omitempty"`
|
||||
Sort string `json:"sort,omitempty"`
|
||||
From int64 `json:"from,omitempty"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
}
|
||||
|
||||
func ParseQueryParameter(req *restful.Request) (*Query, error) {
|
||||
q := &Query{}
|
||||
|
||||
q.Operation = req.QueryParameter("operation")
|
||||
q.WorkspaceFilter = req.QueryParameter("workspace_filter")
|
||||
q.WorkspaceSearch = req.QueryParameter("workspace_search")
|
||||
q.ObjectRefNamespaceFilter = req.QueryParameter("objectref_namespace_filter")
|
||||
q.ObjectRefNamespaceSearch = req.QueryParameter("objectref_namespace_search")
|
||||
q.ObjectRefNameFilter = req.QueryParameter("objectref_name_filter")
|
||||
q.ObjectRefNameSearch = req.QueryParameter("objectref_name_search")
|
||||
q.LevelFilter = req.QueryParameter("level_filter")
|
||||
q.VerbFilter = req.QueryParameter("verb_filter")
|
||||
q.SourceIpSearch = req.QueryParameter("source_ip_search")
|
||||
q.UserFilter = req.QueryParameter("user_filter")
|
||||
q.UserSearch = req.QueryParameter("user_search")
|
||||
q.GroupSearch = req.QueryParameter("group_search")
|
||||
q.ObjectRefResourceFilter = req.QueryParameter("objectref_resource_filter")
|
||||
q.ObjectRefSubresourceFilter = req.QueryParameter("objectref_subresource_filter")
|
||||
q.ResponseCodeFilter = req.QueryParameter("response_code_filter")
|
||||
q.ResponseStatusFilter = req.QueryParameter("response_status_filter")
|
||||
|
||||
if tstr := req.QueryParameter("start_time"); tstr != "" {
|
||||
sec, err := strconv.ParseInt(tstr, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t := time.Unix(sec, 0)
|
||||
q.StartTime = t
|
||||
}
|
||||
if tstr := req.QueryParameter("end_time"); tstr != "" {
|
||||
sec, err := strconv.ParseInt(tstr, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t := time.Unix(sec, 0)
|
||||
q.EndTime = t
|
||||
}
|
||||
if q.Interval = req.QueryParameter("interval"); q.Interval == "" {
|
||||
q.Interval = "15m"
|
||||
}
|
||||
q.From, _ = strconv.ParseInt(req.QueryParameter("from"), 10, 64)
|
||||
size, err := strconv.ParseInt(req.QueryParameter("size"), 10, 64)
|
||||
if err != nil {
|
||||
size = 10
|
||||
}
|
||||
q.Size = size
|
||||
if q.Sort = req.QueryParameter("sort"); q.Sort != "asc" {
|
||||
q.Sort = "desc"
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseQueryParameter(t *testing.T) {
|
||||
queryParam := "operation=query&workspace_filter=my-ws,demo-ws&workspace_search=my,demo&objectref_namespace_filter=my-ns,my-test&objectref_namespace_search=my&objectref_name_filter=my-ref,demo-ref&objectref_name_search=ref&source_ip_search=192.168.&user_filter=user1&user_search=my,demo&group_search=my,demo&start_time=1136214245&end_time=1136214245&from=0&sort=desc"
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost/tenant.kubesphere.io/v2alpha1/auditing/events?%s", queryParam), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := Query{
|
||||
Operation: "query",
|
||||
WorkspaceFilter: "my-ws,demo-ws",
|
||||
WorkspaceSearch: "my,demo",
|
||||
ObjectRefNamespaceFilter: "my-ns,my-test",
|
||||
ObjectRefNamespaceSearch: "my",
|
||||
ObjectRefNameFilter: "my-ref,demo-ref",
|
||||
ObjectRefNameSearch: "ref",
|
||||
UserFilter: "user1",
|
||||
UserSearch: "my,demo",
|
||||
GroupSearch: "my,demo",
|
||||
SourceIpSearch: "192.168.",
|
||||
|
||||
StartTime: time.Unix(1136214245, 0),
|
||||
EndTime: time.Unix(1136214245, 0),
|
||||
|
||||
Interval: "15m",
|
||||
Sort: "desc",
|
||||
From: int64(0),
|
||||
Size: int64(10),
|
||||
}
|
||||
|
||||
request := restful.NewRequest(req)
|
||||
actual, err := ParseQueryParameter(request)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &expected, actual)
|
||||
}
|
||||
@@ -1,18 +1,34 @@
|
||||
// Copyright 2022 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.
|
||||
/*
|
||||
* Please refer to the LICENSE file in the root directory of the project.
|
||||
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package v1alpha1
|
||||
|
||||
type UpdateClusterRequest struct {
|
||||
KubeConfig []byte `json:"kubeconfig"`
|
||||
}
|
||||
|
||||
type CreateLabelRequest struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type UnbindClustersRequest struct {
|
||||
Clusters []string `json:"clusters"`
|
||||
}
|
||||
|
||||
type BindingClustersRequest struct {
|
||||
Labels []string `json:"labels"`
|
||||
Clusters []string `json:"clusters"`
|
||||
}
|
||||
|
||||
type LabelValue struct {
|
||||
Value string `json:"value"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type UpdateVisibilityRequest struct {
|
||||
Op string `json:"op"`
|
||||
Workspace string `json:"workspace"`
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 The KubeSphere Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha2
|
||||
|
||||
import "time"
|
||||
|
||||
type PageableDevOpsProject struct {
|
||||
Items []*DevOpsProject `json:"items"`
|
||||
TotalCount int `json:"total_count"`
|
||||
}
|
||||
|
||||
type DevOpsProject struct {
|
||||
ProjectId string `json:"project_id" db:"project_id" description:"ProjectId must be unique within a workspace, it is generated by kubesphere."`
|
||||
Name string `json:"name" description:"DevOps Projects's Name"`
|
||||
Description string `json:"description,omitempty" description:"DevOps Projects's Description, used to describe the DevOps Project"`
|
||||
Creator string `json:"creator" description:"Creator's username"`
|
||||
CreateTime time.Time `json:"create_time" description:"DevOps Project's Creation time"`
|
||||
Status string `json:"status" description:"DevOps project's status. e.g. active"`
|
||||
Visibility string `json:"visibility,omitempty" description:"Deprecated Field"`
|
||||
Extra string `json:"extra,omitempty" description:"Internal Use"`
|
||||
Workspace string `json:"workspace" description:"The workspace to which the devops project belongs"`
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 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 v1alpha1
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/events"
|
||||
)
|
||||
|
||||
type APIResponse struct {
|
||||
Events *events.Events `json:"query,omitempty" description:"query results"`
|
||||
Statistics *events.Statistics `json:"statistics,omitempty" description:"statistics results"`
|
||||
Histogram *events.Histogram `json:"histogram,omitempty" description:"histogram results"`
|
||||
}
|
||||
|
||||
type Query struct {
|
||||
Operation string `json:"operation,omitempty"`
|
||||
WorkspaceFilter string `json:"workspace_filter,omitempty"`
|
||||
WorkspaceSearch string `json:"workspace_search,omitempty"`
|
||||
InvolvedObjectNamespaceFilter string `json:"involved_object_namespace_filter,omitempty"`
|
||||
InvolvedObjectNamespaceSearch string `json:"involved_object_namespace_search,omitempty"`
|
||||
InvolvedObjectNameFilter string `json:"involved_object_name_filter,omitempty"`
|
||||
InvolvedObjectNameSearch string `json:"involved_object_name_search,omitempty"`
|
||||
InvolvedObjectKindFilter string `json:"involved_object_kind_filter,omitempty"`
|
||||
ReasonFilter string `json:"reason_filter,omitempty"`
|
||||
ReasonSearch string `json:"reason_search,omitempty"`
|
||||
MessageSearch string `json:"message_search,omitempty"`
|
||||
TypeFilter string `json:"type_filter,omitempty"`
|
||||
|
||||
StartTime time.Time `json:"start_time,omitempty"`
|
||||
EndTime time.Time `json:"end_time,omitempty"`
|
||||
|
||||
Interval string `json:"interval,omitempty"`
|
||||
Sort string `json:"sort,omitempty"`
|
||||
From int64 `json:"from,omitempty"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
}
|
||||
|
||||
func ParseQueryParameter(req *restful.Request) (*Query, error) {
|
||||
q := &Query{}
|
||||
|
||||
q.Operation = req.QueryParameter("operation")
|
||||
q.WorkspaceFilter = req.QueryParameter("workspace_filter")
|
||||
q.WorkspaceSearch = req.QueryParameter("workspace_search")
|
||||
q.InvolvedObjectNamespaceFilter = req.QueryParameter("involved_object_namespace_filter")
|
||||
q.InvolvedObjectNamespaceSearch = req.QueryParameter("involved_object_namespace_search")
|
||||
q.InvolvedObjectNameFilter = req.QueryParameter("involved_object_name_filter")
|
||||
q.InvolvedObjectNameSearch = req.QueryParameter("involved_object_name_search")
|
||||
q.InvolvedObjectKindFilter = req.QueryParameter("involved_object_kind_filter")
|
||||
q.ReasonFilter = req.QueryParameter("reason_filter")
|
||||
q.ReasonSearch = req.QueryParameter("reason_search")
|
||||
q.MessageSearch = req.QueryParameter("message_search")
|
||||
q.TypeFilter = req.QueryParameter("type_filter")
|
||||
|
||||
if tstr := req.QueryParameter("start_time"); tstr != "" {
|
||||
sec, err := strconv.ParseInt(tstr, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t := time.Unix(sec, 0)
|
||||
q.StartTime = t
|
||||
}
|
||||
if tstr := req.QueryParameter("end_time"); tstr != "" {
|
||||
sec, err := strconv.ParseInt(tstr, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t := time.Unix(sec, 0)
|
||||
q.EndTime = t
|
||||
}
|
||||
if q.Interval = req.QueryParameter("interval"); q.Interval == "" {
|
||||
q.Interval = "15m"
|
||||
}
|
||||
q.From, _ = strconv.ParseInt(req.QueryParameter("from"), 10, 64)
|
||||
size, err := strconv.ParseInt(req.QueryParameter("size"), 10, 64)
|
||||
if err != nil {
|
||||
size = 10
|
||||
}
|
||||
q.Size = size
|
||||
if q.Sort = req.QueryParameter("sort"); q.Sort != "asc" {
|
||||
q.Sort = "desc"
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseQueryParameter(t *testing.T) {
|
||||
queryParam := "operation=query&workspace_filter=my-ws,demo-ws&workspace_search=my,demo&involved_object_namespace_filter=my-ns,my-test&involved_object_namespace_search=my&involved_object_name_filter=my-involvedObject,demo-involvedObject&involved_object_name_search=involvedObject&involved_object_kind_filter=involvedObject.kind&reason_filter=reason.filter&reason_search=reason&message_search=message&type_filter=Normal&interval=15m&start_time=1136214245&end_time=1136214245&from=0&sort=desc"
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost/tenant.kubesphere.io/v2alpha1/events?%s", queryParam), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := Query{
|
||||
Operation: "query",
|
||||
WorkspaceFilter: "my-ws,demo-ws",
|
||||
WorkspaceSearch: "my,demo",
|
||||
InvolvedObjectNamespaceFilter: "my-ns,my-test",
|
||||
InvolvedObjectNamespaceSearch: "my",
|
||||
InvolvedObjectNameFilter: "my-involvedObject,demo-involvedObject",
|
||||
InvolvedObjectNameSearch: "involvedObject",
|
||||
InvolvedObjectKindFilter: "involvedObject.kind",
|
||||
ReasonFilter: "reason.filter",
|
||||
ReasonSearch: "reason",
|
||||
MessageSearch: "message",
|
||||
TypeFilter: "Normal",
|
||||
|
||||
StartTime: time.Unix(1136214245, 0),
|
||||
EndTime: time.Unix(1136214245, 0),
|
||||
|
||||
Interval: "15m",
|
||||
Sort: "desc",
|
||||
From: int64(0),
|
||||
Size: int64(10),
|
||||
}
|
||||
|
||||
request := restful.NewRequest(req)
|
||||
actual, err := ParseQueryParameter(request)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &expected, actual)
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 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 (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/logging"
|
||||
)
|
||||
|
||||
const (
|
||||
OperationStatistics = "statistics"
|
||||
OperationHistogram = "histogram"
|
||||
OperationQuery = "query"
|
||||
OperationExport = "export"
|
||||
|
||||
DefaultInterval = "15m"
|
||||
DefaultSize = 10
|
||||
OrderAscending = "asc"
|
||||
OrderDescending = "desc"
|
||||
)
|
||||
|
||||
type APIResponse struct {
|
||||
Logs *logging.Logs `json:"query,omitempty" description:"query results"`
|
||||
Statistics *logging.Statistics `json:"statistics,omitempty" description:"statistics results"`
|
||||
Histogram *logging.Histogram `json:"histogram,omitempty" description:"histogram results"`
|
||||
}
|
||||
|
||||
type Query struct {
|
||||
Operation string
|
||||
NamespaceFilter string
|
||||
NamespaceSearch string
|
||||
WorkloadFilter string
|
||||
WorkloadSearch string
|
||||
PodFilter string
|
||||
PodSearch string
|
||||
ContainerFilter string
|
||||
ContainerSearch string
|
||||
LogSearch string
|
||||
StartTime time.Time
|
||||
EndTime time.Time
|
||||
Interval string
|
||||
Sort string
|
||||
From int64
|
||||
Size int64
|
||||
}
|
||||
|
||||
func ParseQueryParameter(req *restful.Request) (*Query, error) {
|
||||
var q Query
|
||||
q.Operation = req.QueryParameter("operation")
|
||||
q.NamespaceFilter = req.QueryParameter("namespaces")
|
||||
q.NamespaceSearch = req.QueryParameter("namespace_query")
|
||||
q.WorkloadFilter = req.QueryParameter("workloads")
|
||||
q.WorkloadSearch = req.QueryParameter("workload_query")
|
||||
q.PodFilter = req.QueryParameter("pods")
|
||||
q.PodSearch = req.QueryParameter("pod_query")
|
||||
q.ContainerFilter = req.QueryParameter("containers")
|
||||
q.ContainerSearch = req.QueryParameter("container_query")
|
||||
q.LogSearch = req.QueryParameter("log_query")
|
||||
|
||||
if q.Operation == "" {
|
||||
q.Operation = OperationQuery
|
||||
}
|
||||
|
||||
if tstr := req.QueryParameter("start_time"); tstr != "" {
|
||||
sec, err := strconv.ParseInt(tstr, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q.StartTime = time.Unix(sec, 0)
|
||||
}
|
||||
if tstr := req.QueryParameter("end_time"); tstr != "" {
|
||||
sec, err := strconv.ParseInt(tstr, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q.EndTime = time.Unix(sec, 0)
|
||||
}
|
||||
|
||||
switch q.Operation {
|
||||
case OperationHistogram:
|
||||
q.Interval = req.QueryParameter("interval")
|
||||
if q.Interval == "" {
|
||||
q.Interval = DefaultInterval
|
||||
}
|
||||
case OperationQuery:
|
||||
q.From, _ = strconv.ParseInt(req.QueryParameter("from"), 10, 64)
|
||||
size, err := strconv.ParseInt(req.QueryParameter("size"), 10, 64)
|
||||
if err != nil {
|
||||
size = DefaultSize
|
||||
}
|
||||
q.Size = size
|
||||
q.Sort = req.QueryParameter("sort")
|
||||
if q.Sort != OrderAscending {
|
||||
q.Sort = OrderDescending
|
||||
}
|
||||
}
|
||||
|
||||
return &q, nil
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseQueryParameter(t *testing.T) {
|
||||
// default operation -- query
|
||||
defaultParam := "namespaces=default,my-ns,my-test&namespace_query=default,my&workloads=my-wl,demo-wl&workload_query=wl&pods=my-po,demo-po&pod_query=po&containers=my-cont,demo-cont&container_query=cont&log_query=ERR&start_time=1136214245&end_time=1136214245&from=0&sort=desc"
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost/tenant.kubesphere.io/v2alpha1/logs?%s", defaultParam), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := Query{
|
||||
Operation: OperationQuery,
|
||||
NamespaceFilter: "default,my-ns,my-test",
|
||||
NamespaceSearch: "default,my",
|
||||
WorkloadFilter: "my-wl,demo-wl",
|
||||
WorkloadSearch: "wl",
|
||||
PodFilter: "my-po,demo-po",
|
||||
PodSearch: "po",
|
||||
ContainerFilter: "my-cont,demo-cont",
|
||||
ContainerSearch: "cont",
|
||||
LogSearch: "ERR",
|
||||
|
||||
StartTime: time.Unix(1136214245, 0),
|
||||
EndTime: time.Unix(1136214245, 0),
|
||||
|
||||
Sort: OrderDescending,
|
||||
From: int64(0),
|
||||
Size: int64(10),
|
||||
}
|
||||
|
||||
request := restful.NewRequest(req)
|
||||
actual, err := ParseQueryParameter(request)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &expected, actual)
|
||||
|
||||
// histogram operation
|
||||
queryParamInterval := "operation=histogram&interval=15m&" + defaultParam
|
||||
reqInterval, err := http.NewRequest("GET", fmt.Sprintf("http://localhost/tenant.kubesphere.io/v2alpha1/logs?%s", queryParamInterval), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected = Query{
|
||||
Operation: OperationHistogram,
|
||||
NamespaceFilter: "default,my-ns,my-test",
|
||||
NamespaceSearch: "default,my",
|
||||
WorkloadFilter: "my-wl,demo-wl",
|
||||
WorkloadSearch: "wl",
|
||||
PodFilter: "my-po,demo-po",
|
||||
PodSearch: "po",
|
||||
ContainerFilter: "my-cont,demo-cont",
|
||||
ContainerSearch: "cont",
|
||||
LogSearch: "ERR",
|
||||
|
||||
StartTime: time.Unix(1136214245, 0),
|
||||
EndTime: time.Unix(1136214245, 0),
|
||||
|
||||
Interval: DefaultInterval,
|
||||
From: int64(0),
|
||||
Size: int64(0),
|
||||
}
|
||||
|
||||
requestInterval := restful.NewRequest(reqInterval)
|
||||
actual, err = ParseQueryParameter(requestInterval)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &expected, actual)
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
// Copyright 2022 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 v1alpha1
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/query"
|
||||
model "kubesphere.io/kubesphere/pkg/models/monitoring"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/monitoring"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultStep = 10 * time.Minute
|
||||
DefaultFilter = ".*"
|
||||
DefaultOrder = model.OrderDescending
|
||||
DefaultPage = 1
|
||||
DefaultLimit = 5
|
||||
|
||||
ErrNoHit = "'end' or 'time' 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'."
|
||||
ErrParameterNotfound = "Parmameter [%s] not found"
|
||||
ErrResourceNotfound = "resource not found"
|
||||
ErrScopeNotAllowed = "scope [%s] not allowed"
|
||||
)
|
||||
|
||||
type Query struct {
|
||||
Level monitoring.Level
|
||||
Operation string
|
||||
LabelSelector string
|
||||
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
|
||||
Applications string
|
||||
Services string
|
||||
StorageClassName string
|
||||
PVCFilter string
|
||||
Cluster string
|
||||
}
|
||||
|
||||
func ParseQueryParameter(req *restful.Request) *Query {
|
||||
var q Query
|
||||
|
||||
q.LabelSelector = req.QueryParameter(query.ParameterLabelSelector)
|
||||
|
||||
q.Level = monitoring.Level(monitoring.MeteringLevelMap[req.QueryParameter("level")])
|
||||
q.Operation = req.QueryParameter("operation")
|
||||
q.Time = req.QueryParameter("time")
|
||||
q.Start = req.QueryParameter("start")
|
||||
q.End = req.QueryParameter("end")
|
||||
q.Step = req.QueryParameter("step")
|
||||
q.Target = req.QueryParameter("sort_metric")
|
||||
q.Order = req.QueryParameter("sort_type")
|
||||
q.Page = req.QueryParameter("page")
|
||||
q.Limit = req.QueryParameter("limit")
|
||||
q.MetricFilter = req.QueryParameter("metrics_filter")
|
||||
q.ResourceFilter = req.QueryParameter("resources_filter")
|
||||
q.WorkspaceName = req.QueryParameter("workspace")
|
||||
|
||||
q.NamespaceName = req.QueryParameter("namespace")
|
||||
if q.NamespaceName == "" {
|
||||
q.NamespaceName = req.PathParameter("namespace")
|
||||
}
|
||||
|
||||
q.NodeName = req.QueryParameter("node")
|
||||
q.WorkloadKind = req.QueryParameter("kind")
|
||||
q.WorkloadName = req.QueryParameter("workload")
|
||||
q.PodName = req.QueryParameter("pod")
|
||||
q.Applications = req.QueryParameter("applications")
|
||||
q.Services = req.QueryParameter("services")
|
||||
q.StorageClassName = req.QueryParameter("storageclass")
|
||||
q.PVCFilter = req.QueryParameter("pvc_filter")
|
||||
q.Cluster = req.QueryParameter("cluster")
|
||||
|
||||
return &q
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/monitoring"
|
||||
)
|
||||
|
||||
func TestParseQueryParameter(t *testing.T) {
|
||||
queryParam := "level=LevelCluster&operation=query&time=1559347600&start=1559347200&end=1561939200&step=10m&sort_metric=meter_workspace_cpu_usage&sort_type=desc&page=1&limit=5&metrics_filter=meter_workspace_cpu_usage|meter_workspace_memory_usage&resources_filter=cpu&workspace=my-ws&namespace=my-ns&node=my-node&kind=deployment&workload=my-wl&pod=my-pod&applications=nignx&services=my-svc&storageclass=nfs&pvc_filter=my-pvc&cluster=my-cluster"
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost/tenant.kubesphere.io/v2alpha1/metering?%s", queryParam), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := Query{
|
||||
Level: monitoring.Level(1),
|
||||
Operation: "query",
|
||||
LabelSelector: "",
|
||||
Time: "1559347600",
|
||||
Start: "1559347200",
|
||||
End: "1561939200",
|
||||
Step: "10m",
|
||||
Target: "meter_workspace_cpu_usage",
|
||||
Order: "desc",
|
||||
Page: "1",
|
||||
Limit: "5",
|
||||
MetricFilter: "meter_workspace_cpu_usage|meter_workspace_memory_usage",
|
||||
ResourceFilter: "cpu",
|
||||
NodeName: "my-node",
|
||||
WorkspaceName: "my-ws",
|
||||
NamespaceName: "my-ns",
|
||||
WorkloadKind: "deployment",
|
||||
WorkloadName: "my-wl",
|
||||
PodName: "my-pod",
|
||||
Applications: "nignx",
|
||||
Services: "my-svc",
|
||||
StorageClassName: "nfs",
|
||||
PVCFilter: "my-pvc",
|
||||
Cluster: "my-cluster",
|
||||
}
|
||||
|
||||
request := restful.NewRequest(req)
|
||||
actual := ParseQueryParameter(request)
|
||||
assert.Equal(t, &expected, actual)
|
||||
}
|
||||
@@ -1,18 +1,7 @@
|
||||
/*
|
||||
Copyright 2020 The KubeSphere Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
* Please refer to the LICENSE file in the root directory of the project.
|
||||
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package v1alpha2
|
||||
|
||||
|
||||
152
pkg/api/types.go
152
pkg/api/types.go
@@ -1,29 +1,18 @@
|
||||
/*
|
||||
Copyright 2020 The KubeSphere Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
* Please refer to the LICENSE file in the root directory of the project.
|
||||
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
type ListResult struct {
|
||||
Items []interface{} `json:"items"`
|
||||
TotalItems int `json:"totalItems"`
|
||||
Items []runtime.Object `json:"items"`
|
||||
TotalItems int `json:"totalItems"`
|
||||
}
|
||||
|
||||
type ResourceQuota struct {
|
||||
@@ -65,114 +54,25 @@ type Workloads struct {
|
||||
Items map[string]interface{} `json:"items,omitempty" description:"unhealthy workloads"`
|
||||
}
|
||||
|
||||
type ClientType string
|
||||
|
||||
const (
|
||||
ClientKubernetes ClientType = "Kubernetes"
|
||||
ClientKubeSphere ClientType = "Kubesphere"
|
||||
ClientIstio ClientType = "Istio"
|
||||
ClientS2i ClientType = "S2i"
|
||||
ClientApplication ClientType = "Application"
|
||||
|
||||
StatusOK = "ok"
|
||||
)
|
||||
|
||||
var SupportedGroupVersionResources = map[ClientType][]schema.GroupVersionResource{
|
||||
// all supported kubernetes api objects
|
||||
ClientKubernetes: {
|
||||
{Group: "", Version: "v1", Resource: "namespaces"},
|
||||
{Group: "", Version: "v1", Resource: "nodes"},
|
||||
{Group: "", Version: "v1", Resource: "resourcequotas"},
|
||||
{Group: "", Version: "v1", Resource: "pods"},
|
||||
{Group: "", Version: "v1", Resource: "services"},
|
||||
{Group: "", Version: "v1", Resource: "persistentvolumeclaims"},
|
||||
{Group: "", Version: "v1", Resource: "secrets"},
|
||||
{Group: "", Version: "v1", Resource: "configmaps"},
|
||||
{Group: "", Version: "v1", Resource: "serviceaccounts"},
|
||||
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "roles"},
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "rolebindings"},
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterroles"},
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterrolebindings"},
|
||||
|
||||
{Group: "apps", Version: "v1", Resource: "deployments"},
|
||||
{Group: "apps", Version: "v1", Resource: "daemonsets"},
|
||||
{Group: "apps", Version: "v1", Resource: "replicasets"},
|
||||
{Group: "apps", Version: "v1", Resource: "statefulsets"},
|
||||
{Group: "apps", Version: "v1", Resource: "controllerrevisions"},
|
||||
|
||||
{Group: "storage.k8s.io", Version: "v1", Resource: "storageclasses"},
|
||||
|
||||
{Group: "batch", Version: "v1", Resource: "jobs"},
|
||||
{Group: "batch", Version: "v1", Resource: "cronjobs"},
|
||||
|
||||
{Group: "networking.k8s.io", Version: "v1", Resource: "ingresses"},
|
||||
|
||||
{Group: "autoscaling", Version: "v2", Resource: "horizontalpodautoscalers"},
|
||||
},
|
||||
|
||||
// all supported kubesphere api objects
|
||||
ClientKubeSphere: {
|
||||
{Group: "tenant.kubesphere.io", Version: "v1alpha1", Resource: "workspaces"},
|
||||
{Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2ibinaries"},
|
||||
|
||||
{Group: "servicemesh.kubesphere.io", Version: "v1alpha2", Resource: "strategies"},
|
||||
{Group: "servicemesh.kubesphere.io", Version: "v1alpha2", Resource: "servicepolicies"},
|
||||
},
|
||||
|
||||
// all supported istio api objects
|
||||
ClientIstio: {},
|
||||
|
||||
// all supported s2i api objects
|
||||
// TODO: move s2i clientset into kubesphere
|
||||
ClientS2i: {
|
||||
{Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2ibuildertemplates"},
|
||||
{Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2iruns"},
|
||||
{Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2ibuilders"},
|
||||
},
|
||||
|
||||
// kubernetes-sigs application api objects
|
||||
ClientApplication: {
|
||||
{Group: "app.k8s.io", Version: "v1beta1", Resource: "applications"},
|
||||
},
|
||||
}
|
||||
|
||||
// List of all resource kinds supported by the UI.
|
||||
const (
|
||||
ResourceKindConfigMap = "configmaps"
|
||||
ResourceKindDaemonSet = "daemonsets"
|
||||
ResourceKindDeployment = "deployments"
|
||||
ResourceKindEvent = "events"
|
||||
ResourceKindHorizontalPodAutoscaler = "horizontalpodautoscalers"
|
||||
ResourceKindIngress = "ingresses"
|
||||
ResourceKindJob = "jobs"
|
||||
ResourceKindCronJob = "cronjobs"
|
||||
ResourceKindLimitRange = "limitranges"
|
||||
ResourceKindNamespace = "namespaces"
|
||||
ResourceKindNode = "nodes"
|
||||
ResourceKindPersistentVolumeClaim = "persistentvolumeclaims"
|
||||
ResourceKindPersistentVolume = "persistentvolumes"
|
||||
ResourceKindCustomResourceDefinition = "customresourcedefinitions"
|
||||
ResourceKindPod = "pods"
|
||||
ResourceKindReplicaSet = "replicasets"
|
||||
ResourceKindResourceQuota = "resourcequota"
|
||||
ResourceKindSecret = "secrets"
|
||||
ResourceKindService = "services"
|
||||
ResourceKindStatefulSet = "statefulsets"
|
||||
ResourceKindStorageClass = "storageclasses"
|
||||
ResourceKindClusterRole = "clusterroles"
|
||||
ResourceKindClusterRoleBinding = "clusterrolebindings"
|
||||
ResourceKindRole = "roles"
|
||||
ResourceKindRoleBinding = "rolebindings"
|
||||
ResourceKindWorkspace = "workspaces"
|
||||
ResourceKindS2iBinary = "s2ibinaries"
|
||||
ResourceKindStrategy = "strategy"
|
||||
ResourceKindServicePolicy = "servicepolicies"
|
||||
ResourceKindS2iBuilderTemplate = "s2ibuildertemplates"
|
||||
ResourceKindeS2iRun = "s2iruns"
|
||||
ResourceKindS2iBuilder = "s2ibuilders"
|
||||
ResourceKindApplication = "applications"
|
||||
|
||||
WorkspaceNone = ""
|
||||
ClusterNone = ""
|
||||
ResourceKindDaemonSet = "daemonsets"
|
||||
ResourceKindDeployment = "deployments"
|
||||
ResourceKindJob = "jobs"
|
||||
ResourceKindPersistentVolumeClaim = "persistentvolumeclaims"
|
||||
ResourceKindStatefulSet = "statefulsets"
|
||||
StatusOK = "ok"
|
||||
WorkspaceNone = ""
|
||||
ClusterNone = ""
|
||||
TagNonResourceAPI = "NonResource APIs"
|
||||
TagAuthentication = "Authentication"
|
||||
TagMultiCluster = "Multi-cluster"
|
||||
TagIdentityManagement = "Identity Management"
|
||||
TagAccessManagement = "Access Management"
|
||||
TagAdvancedOperations = "Advanced Operations"
|
||||
TagTerminal = "Web Terminal"
|
||||
TagNamespacedResources = "Namespaced Resources"
|
||||
TagClusterResources = "Cluster Resources"
|
||||
TagComponentStatus = "Component Status"
|
||||
TagUserRelatedResources = "User Related Resources"
|
||||
TagPlatformConfigurations = "Platform Configurations"
|
||||
)
|
||||
|
||||
@@ -1,18 +1,7 @@
|
||||
/*
|
||||
Copyright 2020 The KubeSphere Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
* Please refer to the LICENSE file in the root directory of the project.
|
||||
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package api
|
||||
|
||||
|
||||
Reference in New Issue
Block a user