package elasticsearch import ( "fmt" "github.com/json-iterator/go" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/simple/client/logging" ) const ( podNameMaxLength = 63 podNameSuffixLength = 6 // 5 characters + 1 hyphen replicaSetSuffixMaxLength = 11 // max 10 characters + 1 hyphen ) // TODO: elastic/go-elasticsearch is working on Query DSL support. // See https://github.com/elastic/go-elasticsearch/issues/42. // We need refactor our query body builder when that is ready. type bodyBuilder struct { Body } func newBodyBuilder() *bodyBuilder { return &bodyBuilder{} } func (bb *bodyBuilder) bytes() ([]byte, error) { return jsoniter.Marshal(bb.Body) } func (bb *bodyBuilder) mainBool(sf logging.SearchFilter) *bodyBuilder { var ms []Match // literal matching if len(sf.NamespaceFilter) != 0 { var b Bool for ns := range sf.NamespaceFilter { var match Match if ct := sf.NamespaceFilter[ns]; ct != nil { match = Match{ Bool: &Bool{ Filter: []Match{ { MatchPhrase: map[string]string{ "kubernetes.namespace_name.keyword": ns, }, }, { Range: &Range{ Time: &Time{ Gte: ct, }, }, }, }, }, } } else { match = Match{ Bool: &Bool{ Filter: []Match{ { MatchPhrase: map[string]string{ "kubernetes.namespace_name.keyword": ns, }, }, }, }, } } b.Should = append(b.Should, match) } b.MinimumShouldMatch = 1 ms = append(ms, Match{Bool: &b}) } if sf.WorkloadFilter != nil { var b Bool for _, wk := range sf.WorkloadFilter { b.Should = append(b.Should, Match{Regexp: map[string]string{"kubernetes.pod_name.keyword": podNameRegexp(wk)}}) } b.MinimumShouldMatch = 1 ms = append(ms, Match{Bool: &b}) } if sf.PodFilter != nil { var b Bool for _, po := range sf.PodFilter { b.Should = append(b.Should, Match{MatchPhrase: map[string]string{"kubernetes.pod_name.keyword": po}}) } b.MinimumShouldMatch = 1 ms = append(ms, Match{Bool: &b}) } if sf.ContainerFilter != nil { var b Bool for _, c := range sf.ContainerFilter { b.Should = append(b.Should, Match{MatchPhrase: map[string]string{"kubernetes.container_name.keyword": c}}) } b.MinimumShouldMatch = 1 ms = append(ms, Match{Bool: &b}) } // fuzzy matching if sf.WorkloadSearch != nil { var b Bool for _, wk := range sf.WorkloadSearch { b.Should = append(b.Should, Match{MatchPhrasePrefix: map[string]string{"kubernetes.pod_name": wk}}) } b.MinimumShouldMatch = 1 ms = append(ms, Match{Bool: &b}) } if sf.PodSearch != nil { var b Bool for _, po := range sf.PodSearch { b.Should = append(b.Should, Match{MatchPhrasePrefix: map[string]string{"kubernetes.pod_name": po}}) } b.MinimumShouldMatch = 1 ms = append(ms, Match{Bool: &b}) } if sf.ContainerSearch != nil { var b Bool for _, c := range sf.ContainerSearch { b.Should = append(b.Should, Match{MatchPhrasePrefix: map[string]string{"kubernetes.container_name": c}}) } b.MinimumShouldMatch = 1 ms = append(ms, Match{Bool: &b}) } if sf.LogSearch != nil { var b Bool for _, l := range sf.LogSearch { b.Should = append(b.Should, Match{MatchPhrasePrefix: map[string]string{"log": l}}) } b.MinimumShouldMatch = 1 ms = append(ms, Match{Bool: &b}) } r := &Range{Time: &Time{}} if !sf.Starttime.IsZero() { r.Gte = &sf.Starttime } if !sf.Endtime.IsZero() { r.Lte = &sf.Endtime } if r.Lte != nil || r.Gte != nil { ms = append(ms, Match{Range: r}) } bb.Body.Query = &Query{Bool{Filter: ms}} return bb } func (bb *bodyBuilder) cardinalityAggregation() *bodyBuilder { bb.Body.Aggs = &Aggs{ CardinalityAggregation: &CardinalityAggregation{ &Cardinality{ Field: "kubernetes.docker_id.keyword", }, }, } return bb } func (bb *bodyBuilder) dateHistogramAggregation(interval string) *bodyBuilder { bb.Body.Aggs = &Aggs{ DateHistogramAggregation: &DateHistogramAggregation{ &DateHistogram{ Field: "time", Interval: interval, }, }, } return bb } func (bb *bodyBuilder) from(n int64) *bodyBuilder { bb.From = n return bb } func (bb *bodyBuilder) size(n int64) *bodyBuilder { bb.Size = n return bb } func (bb *bodyBuilder) sort(order string) *bodyBuilder { bb.Sorts = []map[string]string{{"time": order}} return bb } func podNameRegexp(workloadName string) string { var regexp string if len(workloadName) <= podNameMaxLength-replicaSetSuffixMaxLength-podNameSuffixLength { // match deployment pods, eg. -579dfbcddd-24znw // replicaset rand string is limited to vowels // https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83 regexp += workloadName + "-[bcdfghjklmnpqrstvwxz2456789]{1,10}-[a-z0-9]{5}|" // match statefulset pods, eg. -0 regexp += workloadName + "-[0-9]+|" // match pods of daemonset or job, eg. -29tdk, -5xqvl regexp += workloadName + "-[a-z0-9]{5}" } else if len(workloadName) <= podNameMaxLength-podNameSuffixLength { replicaSetSuffixLength := podNameMaxLength - podNameSuffixLength - len(workloadName) regexp += fmt.Sprintf("%s%d%s", workloadName+"-[bcdfghjklmnpqrstvwxz2456789]{", replicaSetSuffixLength, "}[a-z0-9]{5}|") regexp += workloadName + "-[0-9]+|" regexp += workloadName + "-[a-z0-9]{5}" } else { // Rand suffix may overwrites the workload name if the name is too long // This won't happen for StatefulSet because long name will cause ReplicaSet fails during StatefulSet creation. regexp += workloadName[:podNameMaxLength-podNameSuffixLength+1] + "[a-z0-9]{5}|" regexp += workloadName + "-[0-9]+" } return regexp } func parseResponse(body []byte) (Response, error) { var res Response err := jsoniter.Unmarshal(body, &res) if err != nil { klog.Error(err) return Response{}, err } return res, nil }