Merge pull request #2069 from junotx/master

add events search apis
This commit is contained in:
KubeSphere CI Bot
2020-05-15 19:51:31 +08:00
committed by GitHub
19 changed files with 1756 additions and 327 deletions

View File

@@ -0,0 +1,64 @@
package events
import (
eventsv1alpha1 "kubesphere.io/kubesphere/pkg/api/events/v1alpha1"
"kubesphere.io/kubesphere/pkg/simple/client/events"
"kubesphere.io/kubesphere/pkg/utils/stringutils"
)
type Interface interface {
Events(queryParam *eventsv1alpha1.Query, MutateFilterFunc func(*events.Filter)) (*eventsv1alpha1.APIResponse, error)
}
type eventsOperator struct {
client events.Client
}
func NewEventsOperator(client events.Client) Interface {
return &eventsOperator{client}
}
func (eo *eventsOperator) Events(queryParam *eventsv1alpha1.Query,
MutateFilterFunc func(*events.Filter)) (*eventsv1alpha1.APIResponse, error) {
filter := &events.Filter{
InvolvedObjectNames: stringutils.Split(queryParam.InvolvedObjectNameFilter, ","),
InvolvedObjectNameFuzzy: stringutils.Split(queryParam.InvolvedObjectNameSearch, ","),
InvolvedObjectkinds: stringutils.Split(queryParam.InvolvedObjectKindFilter, ","),
Reasons: stringutils.Split(queryParam.ReasonFilter, ","),
ReasonFuzzy: stringutils.Split(queryParam.ReasonSearch, ","),
MessageFuzzy: stringutils.Split(queryParam.MessageSearch, ","),
Type: queryParam.TypeFilter,
StartTime: queryParam.StartTime,
EndTime: queryParam.EndTime,
}
if MutateFilterFunc != nil {
MutateFilterFunc(filter)
}
var ar eventsv1alpha1.APIResponse
var err error
switch queryParam.Operation {
case "histogram":
if len(filter.InvolvedObjectNamespaceMap) == 0 {
ar.Histogram = &events.Histogram{}
} else {
ar.Histogram, err = eo.client.CountOverTime(filter, queryParam.Interval)
}
case "statistics":
if len(filter.InvolvedObjectNamespaceMap) == 0 {
ar.Statistics = &events.Statistics{}
} else {
ar.Statistics, err = eo.client.StatisticsOnResources(filter)
}
default:
if len(filter.InvolvedObjectNamespaceMap) == 0 {
ar.Events = &events.Events{}
} else {
ar.Events, err = eo.client.SearchEvents(filter, queryParam.From, queryParam.Size, queryParam.Sort)
}
}
if err != nil {
return nil, err
}
return &ar, nil
}

View File

@@ -25,29 +25,37 @@ import (
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api"
eventsv1alpha1 "kubesphere.io/kubesphere/pkg/api/events/v1alpha1"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizerfactory"
unionauthorizer "kubesphere.io/kubesphere/pkg/apiserver/authorization/union"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models/events"
"kubesphere.io/kubesphere/pkg/models/iam/am"
resources "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
resourcesv1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource"
eventsclient "kubesphere.io/kubesphere/pkg/simple/client/events"
"kubesphere.io/kubesphere/pkg/utils/stringutils"
"strings"
"time"
)
type Interface interface {
ListWorkspaces(user user.Info, query *query.Query) (*api.ListResult, error)
ListNamespaces(user user.Info, workspace string, query *query.Query) (*api.ListResult, error)
Events(user user.Info, queryParam *eventsv1alpha1.Query) (*eventsv1alpha1.APIResponse, error)
}
type tenantOperator struct {
am am.AccessManagementInterface
authorizer authorizer.Authorizer
resourceGetter *resourcesv1alpha3.ResourceGetter
events events.Interface
}
func New(informers informers.InformerFactory) Interface {
func New(informers informers.InformerFactory, evtsClient eventsclient.Client) Interface {
amOperator := am.NewAMOperator(informers)
rbacAuthorizer := authorizerfactory.NewRBACAuthorizer(amOperator)
opaAuthorizer := authorizerfactory.NewOPAAuthorizer(amOperator)
@@ -56,6 +64,7 @@ func New(informers informers.InformerFactory) Interface {
am: amOperator,
authorizer: authorizers,
resourceGetter: resourcesv1alpha3.NewResourceGetter(informers),
events: events.NewEventsOperator(evtsClient),
}
}
@@ -200,6 +209,131 @@ func (t *tenantOperator) ListNamespaces(user user.Info, workspace string, queryP
return result, nil
}
// listIntersectedNamespaces lists the namespaces which meet all the following conditions at the same time
// 1. the namespace which belongs to user.
// 2. the namespace in workspace which is in workspaces when workspaces is not empty.
// 3. the namespace in workspace which contains one of workspaceSubstrs when workspaceSubstrs is not empty.
// 4. the namespace which is in namespaces when namespaces is not empty.
// 5. the namespace which contains one of namespaceSubstrs when namespaceSubstrs is not empty.
func (t *tenantOperator) listIntersectedNamespaces(user user.Info,
workspaces, workspaceSubstrs, namespaces, namespaceSubstrs []string) ([]*corev1.Namespace, error) {
var (
namespaceSet = stringSet(namespaces)
workspaceSet = stringSet(workspaces)
iNamespaces []*corev1.Namespace
)
// When user can list all namespaces, the namespaces which do not belong to any workspace should be considered
listNs := authorizer.AttributesRecord{
User: user,
Verb: "list",
APIGroup: "",
APIVersion: "v1",
Resource: "namespaces",
ResourceRequest: true,
}
decision, _, err := t.authorizer.Authorize(listNs)
if err != nil {
return nil, err
}
includeNsWithoutWs := len(workspaceSet) == 0 && len(workspaceSubstrs) == 0 && decision == authorizer.DecisionAllow
roleBindings, err := t.am.ListRoleBindings(user.GetName(), "")
if err != nil {
return nil, err
}
for _, rb := range roleBindings {
if len(namespaceSet) > 0 {
if _, ok := namespaceSet[rb.Namespace]; !ok {
continue
}
}
if len(namespaceSubstrs) > 0 && !stringContains(rb.Namespace, namespaceSubstrs) {
continue
}
ns, err := t.resourceGetter.Get("namespaces", "", rb.Namespace)
if err != nil {
return nil, err
}
if ns, ok := ns.(*corev1.Namespace); ok {
if ws := ns.Labels[tenantv1alpha1.WorkspaceLabel]; ws != "" {
if len(workspaceSet) > 0 {
if _, ok := workspaceSet[ws]; !ok {
continue
}
}
if len(workspaceSubstrs) > 0 && !stringContains(ws, workspaceSubstrs) {
continue
}
} else if !includeNsWithoutWs {
continue
}
iNamespaces = append(iNamespaces, ns)
}
}
return iNamespaces, nil
}
func (t *tenantOperator) Events(user user.Info, queryParam *eventsv1alpha1.Query) (*eventsv1alpha1.APIResponse, error) {
iNamespaces, err := t.listIntersectedNamespaces(user,
stringutils.Split(queryParam.WorkspaceFilter, ","),
stringutils.Split(queryParam.WorkspaceSearch, ","),
stringutils.Split(queryParam.InvolvedObjectNamespaceFilter, ","),
stringutils.Split(queryParam.InvolvedObjectNamespaceSearch, ","))
if err != nil {
klog.Error(err)
return nil, err
}
namespaceCreateTimeMap := make(map[string]time.Time)
for _, ns := range iNamespaces {
listEvts := authorizer.AttributesRecord{
User: user,
Verb: "list",
APIGroup: "",
APIVersion: "v1",
Namespace: ns.Name,
Resource: "events",
ResourceRequest: true,
}
decision, _, err := t.authorizer.Authorize(listEvts)
if err != nil {
klog.Error(err)
return nil, err
}
if decision == authorizer.DecisionAllow {
namespaceCreateTimeMap[ns.Name] = ns.CreationTimestamp.Time
}
}
// If there are no ns and ws query conditions,
// those events with empty `involvedObject.namespace` will also be listed when user can list all events
if len(queryParam.WorkspaceFilter) == 0 && len(queryParam.InvolvedObjectNamespaceFilter) == 0 &&
len(queryParam.WorkspaceSearch) == 0 && len(queryParam.InvolvedObjectNamespaceSearch) == 0 {
listEvts := authorizer.AttributesRecord{
User: user,
Verb: "list",
APIGroup: "",
APIVersion: "v1",
Resource: "events",
ResourceRequest: true,
}
decision, _, err := t.authorizer.Authorize(listEvts)
if err != nil {
klog.Error(err)
return nil, err
}
if decision == authorizer.DecisionAllow {
namespaceCreateTimeMap[""] = time.Time{}
}
}
return t.events.Events(queryParam, func(filter *eventsclient.Filter) {
filter.InvolvedObjectNamespaceMap = namespaceCreateTimeMap
})
}
func contains(objects []runtime.Object, object runtime.Object) bool {
for _, item := range objects {
if item == object {
@@ -208,3 +342,20 @@ func contains(objects []runtime.Object, object runtime.Object) bool {
}
return false
}
func stringSet(strs []string) map[string]struct{} {
m := make(map[string]struct{})
for _, str := range strs {
m[str] = struct{}{}
}
return m
}
func stringContains(str string, subStrs []string) bool {
for _, sub := range subStrs {
if strings.Contains(str, sub) {
return true
}
}
return false
}

View File

@@ -332,5 +332,5 @@ func prepare() Interface {
RoleBindings().Informer().GetIndexer().Add(roleBinding)
}
return New(fakeInformerFactory)
return New(fakeInformerFactory, nil)
}