diff --git a/pkg/apiserver/logging/logging.go b/pkg/apiserver/logging/logging.go index fd99e5ad6..461df0850 100644 --- a/pkg/apiserver/logging/logging.go +++ b/pkg/apiserver/logging/logging.go @@ -25,13 +25,14 @@ import ( "kubesphere.io/kubesphere/pkg/models/log" es "kubesphere.io/kubesphere/pkg/simple/client/elasticsearch" fb "kubesphere.io/kubesphere/pkg/simple/client/fluentbit" + "kubesphere.io/kubesphere/pkg/utils/stringutils" "net/http" "strconv" + "strings" ) func LoggingQueryCluster(request *restful.Request, response *restful.Response) { res := logQuery(log.QueryLevelCluster, request) - if res.Status != http.StatusOK { response.WriteHeaderAndEntity(res.Status, errors.New(res.Error)) return @@ -42,7 +43,6 @@ func LoggingQueryCluster(request *restful.Request, response *restful.Response) { func LoggingQueryWorkspace(request *restful.Request, response *restful.Response) { res := logQuery(log.QueryLevelWorkspace, request) - if res.Status != http.StatusOK { response.WriteHeaderAndEntity(res.Status, errors.New(res.Error)) return @@ -53,7 +53,6 @@ func LoggingQueryWorkspace(request *restful.Request, response *restful.Response) func LoggingQueryNamespace(request *restful.Request, response *restful.Response) { res := logQuery(log.QueryLevelNamespace, request) - if res.Status != http.StatusOK { response.WriteHeaderAndEntity(res.Status, errors.New(res.Error)) return @@ -160,79 +159,66 @@ func LoggingDeleteFluentbitOutput(request *restful.Request, response *restful.Re } func logQuery(level log.LogQueryLevel, request *restful.Request) *es.QueryResult { - var param es.QueryParameters - param.Operation = request.QueryParameter("operation") + var param es.QueryParameters switch level { case log.QueryLevelCluster: - { - param.NamespaceFilled, param.Namespaces = log.QueryWorkspace(request.QueryParameter("workspaces"), request.QueryParameter("workspace_query")) - param.NamespaceFilled, param.Namespaces = log.MatchNamespace(request.QueryParameter("namespaces"), param.NamespaceFilled, param.Namespaces) - param.NamespaceFilled, param.NamespaceWithCreationTime = log.GetNamespaceCreationTimeMap(param.Namespaces) - param.NamespaceQuery = request.QueryParameter("namespace_query") - param.PodFilled, param.Pods = log.QueryWorkload(request.QueryParameter("workloads"), request.QueryParameter("workload_query"), param.Namespaces) - param.PodFilled, param.Pods = log.MatchPod(request.QueryParameter("pods"), param.PodFilled, param.Pods) - param.PodQuery = request.QueryParameter("pod_query") - param.ContainerFilled, param.Containers = log.MatchContainer(request.QueryParameter("containers")) - param.ContainerQuery = request.QueryParameter("container_query") - } + var namespaces []string + param.NamespaceNotFound, namespaces = log.MatchNamespace(stringutils.Split(request.QueryParameter("namespaces"), ","), + stringutils.Split(strings.ToLower(request.QueryParameter("namespace_query")), ","), // case-insensitive + stringutils.Split(request.QueryParameter("workspaces"), ","), + stringutils.Split(strings.ToLower(request.QueryParameter("workspace_query")), ",")) // case-insensitive + param.NamespaceWithCreationTime = log.MakeNamespaceCreationTimeMap(namespaces) + param.WorkloadNotFound, param.WorkloadFilter = log.MatchWorkload(request.QueryParameter("workloads"), request.QueryParameter("workload_query"), namespaces) + param.PodFilter = stringutils.Split(request.QueryParameter("pods"), ",") + param.PodQuery = stringutils.Split(request.QueryParameter("pod_query"), ",") + param.ContainerFilter = stringutils.Split(request.QueryParameter("containers"), ",") + param.ContainerQuery = stringutils.Split(request.QueryParameter("container_query"), ",") case log.QueryLevelWorkspace: - { - param.NamespaceFilled, param.Namespaces = log.QueryWorkspace(request.PathParameter("workspace"), "") - param.NamespaceFilled, param.Namespaces = log.MatchNamespace(request.QueryParameter("namespaces"), param.NamespaceFilled, param.Namespaces) - param.NamespaceFilled, param.NamespaceWithCreationTime = log.GetNamespaceCreationTimeMap(param.Namespaces) - param.NamespaceQuery = request.QueryParameter("namespace_query") - param.PodFilled, param.Pods = log.QueryWorkload(request.QueryParameter("workloads"), request.QueryParameter("workload_query"), param.Namespaces) - param.PodFilled, param.Pods = log.MatchPod(request.QueryParameter("pods"), param.PodFilled, param.Pods) - param.PodQuery = request.QueryParameter("pod_query") - param.ContainerFilled, param.Containers = log.MatchContainer(request.QueryParameter("containers")) - param.ContainerQuery = request.QueryParameter("container_query") - } + var namespaces []string + param.NamespaceNotFound, namespaces = log.MatchNamespace(stringutils.Split(request.QueryParameter("namespaces"), ","), + stringutils.Split(strings.ToLower(request.QueryParameter("namespace_query")), ","), // case-insensitive + stringutils.Split(request.PathParameter("workspace"), ","), nil) // case-insensitive + param.NamespaceWithCreationTime = log.MakeNamespaceCreationTimeMap(namespaces) + param.WorkloadNotFound, param.WorkloadFilter = log.MatchWorkload(request.QueryParameter("workloads"), request.QueryParameter("workload_query"), namespaces) + param.PodFilter = stringutils.Split(request.QueryParameter("pods"), ",") + param.PodQuery = stringutils.Split(request.QueryParameter("pod_query"), ",") + param.ContainerFilter = stringutils.Split(request.QueryParameter("containers"), ",") + param.ContainerQuery = stringutils.Split(request.QueryParameter("container_query"), ",") case log.QueryLevelNamespace: - { - param.NamespaceFilled, param.Namespaces = log.MatchNamespace(request.PathParameter("namespace"), false, nil) - param.NamespaceFilled, param.NamespaceWithCreationTime = log.GetNamespaceCreationTimeMap(param.Namespaces) - param.PodFilled, param.Pods = log.QueryWorkload(request.QueryParameter("workloads"), request.QueryParameter("workload_query"), param.Namespaces) - param.PodFilled, param.Pods = log.MatchPod(request.QueryParameter("pods"), param.PodFilled, param.Pods) - param.PodQuery = request.QueryParameter("pod_query") - param.ContainerFilled, param.Containers = log.MatchContainer(request.QueryParameter("containers")) - param.ContainerQuery = request.QueryParameter("container_query") - } + namespaces := []string{request.PathParameter("namespace")} + param.NamespaceWithCreationTime = log.MakeNamespaceCreationTimeMap(namespaces) + param.WorkloadNotFound, param.WorkloadFilter = log.MatchWorkload(request.QueryParameter("workloads"), request.QueryParameter("workload_query"), namespaces) + param.PodFilter = stringutils.Split(request.QueryParameter("pods"), ",") + param.PodQuery = stringutils.Split(request.QueryParameter("pod_query"), ",") + param.ContainerFilter = stringutils.Split(request.QueryParameter("containers"), ",") + param.ContainerQuery = stringutils.Split(request.QueryParameter("container_query"), ",") case log.QueryLevelWorkload: - { - param.NamespaceFilled, param.Namespaces = log.MatchNamespace(request.PathParameter("namespace"), false, nil) - param.NamespaceFilled, param.NamespaceWithCreationTime = log.GetNamespaceCreationTimeMap(param.Namespaces) - param.PodFilled, param.Pods = log.QueryWorkload(request.PathParameter("workload"), "", param.Namespaces) - param.PodFilled, param.Pods = log.MatchPod(request.QueryParameter("pods"), param.PodFilled, param.Pods) - param.PodQuery = request.QueryParameter("pod_query") - param.ContainerFilled, param.Containers = log.MatchContainer(request.QueryParameter("containers")) - param.ContainerQuery = request.QueryParameter("container_query") - } + namespaces := []string{request.PathParameter("namespace")} + param.NamespaceWithCreationTime = log.MakeNamespaceCreationTimeMap(namespaces) + param.WorkloadNotFound, param.WorkloadFilter = log.MatchWorkload(request.PathParameter("workload"), "", namespaces) + param.PodFilter = stringutils.Split(request.QueryParameter("pods"), ",") + param.PodQuery = stringutils.Split(request.QueryParameter("pod_query"), ",") + param.ContainerFilter = stringutils.Split(request.QueryParameter("containers"), ",") + param.ContainerQuery = stringutils.Split(request.QueryParameter("container_query"), ",") case log.QueryLevelPod: - { - param.NamespaceFilled, param.Namespaces = log.MatchNamespace(request.PathParameter("namespace"), false, nil) - param.NamespaceFilled, param.NamespaceWithCreationTime = log.GetNamespaceCreationTimeMap(param.Namespaces) - param.PodFilled, param.Pods = log.MatchPod(request.PathParameter("pod"), false, nil) - param.ContainerFilled, param.Containers = log.MatchContainer(request.QueryParameter("containers")) - param.ContainerQuery = request.QueryParameter("container_query") - } + namespaces := []string{request.PathParameter("namespace")} + param.NamespaceWithCreationTime = log.MakeNamespaceCreationTimeMap(namespaces) + param.PodFilter = []string{request.PathParameter("pod")} + param.ContainerFilter = stringutils.Split(request.QueryParameter("containers"), ",") + param.ContainerQuery = stringutils.Split(request.QueryParameter("container_query"), ",") case log.QueryLevelContainer: - { - param.NamespaceFilled, param.Namespaces = log.MatchNamespace(request.PathParameter("namespace"), false, nil) - param.NamespaceFilled, param.NamespaceWithCreationTime = log.GetNamespaceCreationTimeMap(param.Namespaces) - param.PodFilled, param.Pods = log.MatchPod(request.PathParameter("pod"), false, nil) - param.ContainerFilled, param.Containers = log.MatchContainer(request.PathParameter("container")) - } + namespaces := []string{request.PathParameter("namespace")} + param.NamespaceWithCreationTime = log.MakeNamespaceCreationTimeMap(namespaces) + param.PodFilter = []string{request.PathParameter("pod")} + param.ContainerFilter = []string{request.PathParameter("container")} } - if len(param.Namespaces) == 1 { - param.Workspace = log.GetWorkspaceOfNamesapce(param.Namespaces[0]) - } + param.LogQuery = stringutils.Split(request.QueryParameter("log_query"), ",") + param.Operation = request.QueryParameter("operation") param.Interval = request.QueryParameter("interval") - - param.LogQuery = request.QueryParameter("log_query") param.StartTime = request.QueryParameter("start_time") param.EndTime = request.QueryParameter("end_time") param.Sort = request.QueryParameter("sort") diff --git a/pkg/models/log/logcollector.go b/pkg/models/log/logcollector.go index 42253683c..78f159cbd 100644 --- a/pkg/models/log/logcollector.go +++ b/pkg/models/log/logcollector.go @@ -23,40 +23,13 @@ import ( "k8s.io/apimachinery/pkg/labels" "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/utils/stringutils" "reflect" "strconv" "strings" "time" ) -func intersection(s1, s2 []string) (inter []string) { - hash := make(map[string]bool) - for _, e := range s1 { - hash[e] = true - } - for _, e := range s2 { - // If elements present in the hashmap then append intersection list. - if hash[e] { - inter = append(inter, e) - } - } - //Remove dups from slice. - inter = removeDups(inter) - return -} - -//Remove dups from slice. -func removeDups(elements []string) (nodups []string) { - encountered := make(map[string]bool) - for _, element := range elements { - if !encountered[element] { - nodups = append(nodups, element) - encountered[element] = true - } - } - return -} - func in(value interface{}, container interface{}) int { if container == nil { return -1 @@ -116,105 +89,68 @@ func queryLabel(label string, labelsQuery []string) bool { return result } -func QueryWorkspace(workspaceMatch string, workspaceQuery string) (bool, []string) { - if workspaceMatch == "" && workspaceQuery == "" { - return false, nil - } +// list namespaces that match search conditions +func MatchNamespace(nsFilter []string, nsQuery []string, wsFilter []string, wsQuery []string) (bool, []string) { nsLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister() nsList, err := nsLister.List(labels.Everything()) if err != nil { - glog.Error("failed to list namespace, error: ", err) + glog.Errorf("failed to list namespace, error: %s", err) return true, nil } var namespaces []string - var hasMatch = false - var workspacesMatch []string - if workspaceMatch != "" { - workspacesMatch = strings.Split(strings.Replace(workspaceMatch, ",", " ", -1), " ") - hasMatch = true - } - - var hasQuery = false - var workspacesQuery []string - if workspaceQuery != "" { - workspacesQuery = strings.Split(strings.ToLower(strings.Replace(workspaceQuery, ",", " ", -1)), " ") - hasQuery = true + // if no search condition is set on both namespace and workspace, + // then return all namespaces + if nsQuery == nil && nsFilter == nil && wsQuery == nil && wsFilter == nil { + for _, ns := range nsList { + namespaces = append(namespaces, ns.Name) + } + return false, namespaces } for _, ns := range nsList { - labels := ns.GetLabels() - _, ok := labels[constants.WorkspaceLabelKey] - if ok { - var namespaceCanAppend = true - if hasMatch { - if !matchLabel(labels[constants.WorkspaceLabelKey], workspacesMatch) { - namespaceCanAppend = false - } - } - if hasQuery { - if !queryLabel(strings.ToLower(labels[constants.WorkspaceLabelKey]), workspacesQuery) { - namespaceCanAppend = false - } - } - - if namespaceCanAppend { - namespaces = append(namespaces, ns.GetName()) - } - } - } - - return true, namespaces -} - -func MatchNamespace(namespaceMatch string, namespaceFilled bool, namespaces []string) (bool, []string) { - if namespaceMatch == "" { - return namespaceFilled, namespaces - } - - namespacesMatch := strings.Split(strings.Replace(namespaceMatch, ",", " ", -1), " ") - - if namespaceFilled { - return true, intersection(namespacesMatch, namespaces) - } - - return true, namespacesMatch -} - -func GetNamespaceCreationTimeMap(namespaces []string) (bool, map[string]string) { - - namespaceWithCreationTime := make(map[string]string) - - nsLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister() - - if len(namespaces) == 0 { - nsList, err := nsLister.List(labels.Everything()) - if err != nil { - glog.Error("failed to list namespace, error: ", err) - return true, namespaceWithCreationTime - } - - for _, ns := range nsList { + if stringutils.StringIn(ns.Name, nsFilter) || + stringutils.StringIn(ns.Annotations[constants.WorkspaceLabelKey], wsFilter) || + containsIn(ns.Name, nsQuery) || + containsIn(ns.Annotations[constants.WorkspaceLabelKey], wsQuery) { namespaces = append(namespaces, ns.Name) } } + // if namespaces is equal to nil, indicates no namespace matched + // it causes the query to return no result + return namespaces == nil, namespaces +} + +func containsIn(str string, subStrs []string) bool { + for _, sub := range subStrs { + if strings.Contains(str, sub) { + return true + } + } + return false +} + +func MakeNamespaceCreationTimeMap(namespaces []string) map[string]string { + + namespaceWithCreationTime := make(map[string]string) + + nsLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister() for _, item := range namespaces { ns, err := nsLister.Get(item) if err != nil { - glog.Error("failed to get namespace, error: ", err) + // the ns doesn't exist continue } - namespaceWithCreationTime[ns.Name] = strconv.FormatInt(ns.CreationTimestamp.UnixNano()/int64(time.Millisecond), 10) } - return true, namespaceWithCreationTime + return namespaceWithCreationTime } -func QueryWorkload(workloadMatch string, workloadQuery string, namespaces []string) (bool, []string) { +func MatchWorkload(workloadMatch string, workloadQuery string, namespaces []string) (bool, []string) { if workloadMatch == "" && workloadQuery == "" { return false, nil } @@ -292,51 +228,7 @@ func QueryWorkload(workloadMatch string, workloadQuery string, namespaces []stri } } - return true, pods -} - -func MatchPod(podMatch string, podFilled bool, pods []string) (bool, []string) { - if podMatch == "" { - return podFilled, pods - } - - podsMatch := strings.Split(strings.Replace(podMatch, ",", " ", -1), " ") - - if podFilled { - return true, intersection(podsMatch, pods) - } - - return true, podsMatch -} - -func MatchContainer(containerMatch string) (bool, []string) { - if containerMatch == "" { - return false, nil - } - - return true, strings.Split(strings.Replace(containerMatch, ",", " ", -1), " ") -} - -func GetWorkspaceOfNamesapce(namespace string) string { - var workspace string - workspace = "" - - nsLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister() - nsList, err := nsLister.List(labels.Everything()) - if err != nil { - glog.Error("failed to list namespace, error: ", err) - return workspace - } - - for _, ns := range nsList { - if ns.GetName() == namespace { - labels := ns.GetLabels() - _, ok := labels[constants.WorkspaceLabelKey] - if ok { - workspace = labels[constants.WorkspaceLabelKey] - } - } - } - - return workspace + // if workloads is equal to nil, indicates no workload matched + // it causes the query to return no result + return pods == nil, pods } diff --git a/pkg/simple/client/elasticsearch/esclient.go b/pkg/simple/client/elasticsearch/esclient.go index 72a5f1d6d..c68579dc8 100644 --- a/pkg/simple/client/elasticsearch/esclient.go +++ b/pkg/simple/client/elasticsearch/esclient.go @@ -25,6 +25,16 @@ import ( "github.com/json-iterator/go" ) +const ( + matchPhrase = iota + matchPhrasePrefix + + fieldNamespaceName = "kubernetes.namespace_name" + fieldPodName = "kubernetes.pod_name" + fieldContainerName = "kubernetes.container_name" + fieldLog = "log" +) + var jsonIter = jsoniter.ConfigCompatibleWithStandardLibrary var ( @@ -73,11 +83,20 @@ type Order struct { } type BoolQuery struct { - BoolMusts BoolMusts `json:"bool"` + Bool interface{} `json:"bool"` } -type BoolMusts struct { - Musts []interface{} `json:"must"` +type FilterContext struct { + Filter []interface{} `json:"must"` +} + +type BoolMust struct { + Must []interface{} `json:"must"` +} + +type BoolShould struct { + Should []interface{} `json:"should"` + MinimumShouldMatch int64 `json:"minimum_should_match"` } type RangeQuery struct { @@ -93,25 +112,12 @@ type TimeRange struct { Lte string `json:"lte,omitempty"` } -type BoolShouldMatchPhrase struct { - ShouldMatchPhrase ShouldMatchPhrase `json:"bool"` -} - -type ShouldMatchPhrase struct { - Shoulds []interface{} `json:"should"` - MinimumShouldMatch int64 `json:"minimum_should_match"` -} - type MatchPhrase struct { - MatchPhrase interface{} `json:"match_phrase"` + MatchPhrase map[string]string `json:"match_phrase"` } -type Match struct { - Match interface{} `json:"match"` -} - -type QueryWord struct { - Word string `json:"query"` +type MatchPhrasePrefix struct { + MatchPhrasePrefix interface{} `json:"match_phrase_prefix"` } type MainHighLight struct { @@ -166,78 +172,52 @@ type DateHistogram struct { func createQueryRequest(param QueryParameters) (int, []byte, error) { var request Request - var mainBoolQuery BoolMusts + var mainBoolQuery FilterContext - if param.NamespaceFilled { - var shouldMatchPhrase ShouldMatchPhrase - if len(param.NamespaceWithCreationTime) == 0 { - matchPhrase := MatchPhrase{map[string]interface{}{"kubernetes.namespace_name.key_word": QueryWord{""}}} - shouldMatchPhrase.Shoulds = append(shouldMatchPhrase.Shoulds, matchPhrase) - } else { - for namespace, creationTime := range param.NamespaceWithCreationTime { - var boolQuery BoolQuery + if len(param.NamespaceWithCreationTime) != 0 { + var boolShoulds BoolShould + for namespace, creationTime := range param.NamespaceWithCreationTime { + var boolMusts BoolMust - matchPhrase := MatchPhrase{map[string]interface{}{"kubernetes.namespace_name.keyword": QueryWord{namespace}}} - rangeQuery := RangeQuery{RangeSpec{TimeRange{creationTime, ""}}} + matchPhrase := MatchPhrase{MatchPhrase: map[string]string{fieldNamespaceName: namespace}} + rangeQuery := RangeQuery{RangeSpec{TimeRange{creationTime, ""}}} - boolQuery.BoolMusts.Musts = append(boolQuery.BoolMusts.Musts, matchPhrase) - boolQuery.BoolMusts.Musts = append(boolQuery.BoolMusts.Musts, rangeQuery) + boolMusts.Must = append(boolMusts.Must, matchPhrase) + boolMusts.Must = append(boolMusts.Must, rangeQuery) - shouldMatchPhrase.Shoulds = append(shouldMatchPhrase.Shoulds, boolQuery) - } + boolShoulds.Should = append(boolShoulds.Should, BoolQuery{Bool: boolMusts}) } - shouldMatchPhrase.MinimumShouldMatch = 1 - mainBoolQuery.Musts = append(mainBoolQuery.Musts, BoolShouldMatchPhrase{shouldMatchPhrase}) + boolShoulds.MinimumShouldMatch = 1 + mainBoolQuery.Filter = append(mainBoolQuery.Filter, BoolQuery{Bool: boolShoulds}) } - if param.PodFilled { - var shouldMatchPhrase ShouldMatchPhrase - if len(param.Pods) == 0 { - matchPhrase := MatchPhrase{map[string]interface{}{"kubernetes.pod_name.key_word": QueryWord{""}}} - shouldMatchPhrase.Shoulds = append(shouldMatchPhrase.Shoulds, matchPhrase) - } else { - for _, pod := range param.Pods { - matchPhrase := MatchPhrase{map[string]interface{}{"kubernetes.pod_name.keyword": QueryWord{pod}}} - shouldMatchPhrase.Shoulds = append(shouldMatchPhrase.Shoulds, matchPhrase) - } - } - shouldMatchPhrase.MinimumShouldMatch = 1 - mainBoolQuery.Musts = append(mainBoolQuery.Musts, BoolShouldMatchPhrase{shouldMatchPhrase}) + if param.WorkloadFilter != nil { + boolQuery := makeBoolShould(matchPhrase, fieldPodName, param.WorkloadFilter) + mainBoolQuery.Filter = append(mainBoolQuery.Filter, boolQuery) } - if param.ContainerFilled { - var shouldMatchPhrase ShouldMatchPhrase - if len(param.Containers) == 0 { - matchPhrase := MatchPhrase{map[string]interface{}{"kubernetes.container_name.key_word": QueryWord{""}}} - shouldMatchPhrase.Shoulds = append(shouldMatchPhrase.Shoulds, matchPhrase) - } else { - for _, container := range param.Containers { - matchPhrase := MatchPhrase{map[string]interface{}{"kubernetes.container_name.keyword": QueryWord{container}}} - shouldMatchPhrase.Shoulds = append(shouldMatchPhrase.Shoulds, matchPhrase) - } - } - shouldMatchPhrase.MinimumShouldMatch = 1 - mainBoolQuery.Musts = append(mainBoolQuery.Musts, BoolShouldMatchPhrase{shouldMatchPhrase}) + if param.PodFilter != nil { + boolQuery := makeBoolShould(matchPhrase, fieldPodName, param.PodFilter) + mainBoolQuery.Filter = append(mainBoolQuery.Filter, boolQuery) + } + if param.ContainerFilter != nil { + boolQuery := makeBoolShould(matchPhrase, fieldContainerName, param.ContainerFilter) + mainBoolQuery.Filter = append(mainBoolQuery.Filter, boolQuery) } - if param.NamespaceQuery != "" { - match := Match{map[string]interface{}{"kubernetes.namespace_name": QueryWord{param.NamespaceQuery}}} - mainBoolQuery.Musts = append(mainBoolQuery.Musts, match) + if param.PodQuery != nil { + boolQuery := makeBoolShould(matchPhrasePrefix, fieldPodName, param.PodQuery) + mainBoolQuery.Filter = append(mainBoolQuery.Filter, boolQuery) } - if param.PodQuery != "" { - match := Match{map[string]interface{}{"kubernetes.pod_name": QueryWord{param.PodQuery}}} - mainBoolQuery.Musts = append(mainBoolQuery.Musts, match) + if param.ContainerQuery != nil { + boolQuery := makeBoolShould(matchPhrasePrefix, fieldContainerName, param.ContainerQuery) + mainBoolQuery.Filter = append(mainBoolQuery.Filter, boolQuery) } - if param.ContainerQuery != "" { - match := Match{map[string]interface{}{"kubernetes.container_name": QueryWord{param.ContainerQuery}}} - mainBoolQuery.Musts = append(mainBoolQuery.Musts, match) - } - - if param.LogQuery != "" { - match := Match{map[string]interface{}{"log": QueryWord{param.LogQuery}}} - mainBoolQuery.Musts = append(mainBoolQuery.Musts, match) + if param.LogQuery != nil { + boolQuery := makeBoolShould(matchPhrasePrefix, fieldLog, param.LogQuery) + mainBoolQuery.Filter = append(mainBoolQuery.Filter, boolQuery) } rangeQuery := RangeQuery{RangeSpec{TimeRange{param.StartTime, param.EndTime}}} - mainBoolQuery.Musts = append(mainBoolQuery.Musts, rangeQuery) + mainBoolQuery.Filter = append(mainBoolQuery.Filter, rangeQuery) var operation int @@ -286,6 +266,30 @@ func createQueryRequest(param QueryParameters) (int, []byte, error) { return operation, queryRequest, err } +func makeBoolShould(queryType int, field string, list []string) BoolQuery { + var should []interface{} + for _, phrase := range list { + + var q interface{} + + switch queryType { + case matchPhrase: + q = MatchPhrase{MatchPhrase: map[string]string{field: phrase}} + case matchPhrasePrefix: + q = MatchPhrasePrefix{MatchPhrasePrefix: map[string]string{field: phrase}} + } + + should = append(should, q) + } + + return BoolQuery{ + Bool: BoolShould{ + Should: should, + MinimumShouldMatch: 1, + }, + } +} + // Fore more info, refer to https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started-search-API.html // Response from the elasticsearch engine type Response struct { @@ -396,7 +400,6 @@ type HistogramResult struct { type QueryResult struct { Status int `json:"status,omitempty" description:"query status"` Error string `json:"error,omitempty" description:"debugging information"` - Workspace string `json:"workspace,omitempty" description:"the name of the workspace where logs come from"` Read *ReadResult `json:"query,omitempty" description:"query results"` Statistics *StatisticsResult `json:"statistics,omitempty" description:"statistics results"` Histogram *HistogramResult `json:"histogram,omitempty" description:"histogram results"` @@ -513,28 +516,29 @@ func parseQueryResult(operation int, param QueryParameters, body []byte) *QueryR } queryResult.Status = http.StatusOK - queryResult.Workspace = param.Workspace return &queryResult } type QueryParameters struct { - NamespaceFilled bool - Namespaces []string + // when true, indicates the provided `namespaces` or `namespace_query` doesn't match any namespace + NamespaceNotFound bool + // a map of namespace with creation time NamespaceWithCreationTime map[string]string - PodFilled bool - Pods []string - ContainerFilled bool - Containers []string - NamespaceQuery string - PodQuery string - ContainerQuery string + // when true, indicates the provided `workloads` or `workload_query` doesn't match any workload + WorkloadNotFound bool + WorkloadFilter []string - Workspace string + PodFilter []string + PodQuery []string + + ContainerFilter []string + ContainerQuery []string + + LogQuery []string Operation string - LogQuery string Interval string StartTime string EndTime string @@ -547,6 +551,23 @@ func Query(param QueryParameters) *QueryResult { var queryResult = new(QueryResult) + if param.NamespaceNotFound || param.WorkloadNotFound { + queryResult = new(QueryResult) + queryResult.Status = http.StatusOK + switch param.Operation { + case "statistics": + queryResult.Statistics = new(StatisticsResult) + case "histogram": + queryResult.Histogram = &HistogramResult{ + StartTime: calcTimestamp(param.StartTime), + EndTime: calcTimestamp(param.EndTime), + Interval: param.Interval} + default: + queryResult.Read = new(ReadResult) + } + return queryResult + } + if client == nil { queryResult.Status = http.StatusBadRequest queryResult.Error = fmt.Sprintf("Invalid elasticsearch address: host=%s, port=%s", config.Host, config.Port) diff --git a/pkg/utils/stringutils/string.go b/pkg/utils/stringutils/string.go index ff391b70a..ef68b33bd 100644 --- a/pkg/utils/stringutils/string.go +++ b/pkg/utils/stringutils/string.go @@ -14,6 +14,7 @@ limitations under the License. package stringutils import ( + "strings" "unicode/utf8" "github.com/asaskevich/govalidator" @@ -75,3 +76,10 @@ func Reverse(s string) string { } return string(buf) } + +func Split(str string, sep string) []string { + if str == "" { + return nil + } + return strings.Split(str, sep) +}