diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..688152a --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,110 @@ +version: "2" + +linters: + enable: + - errcheck + - govet + - ineffassign + - staticcheck + - unused + - arangolint + - asasalint + - asciicheck + - bidichk + - bodyclose + - canonicalheader + - clickhouselint + - containedctx + #- contextcheck + - copyloopvar + #- cyclop + - decorder + - dogsled + - dupword + - durationcheck + - embeddedstructfieldcheck + - errchkjson + - errname + #- errorlint + - exhaustive + - exptostd + - fatcontext + - funcorder + #- funlen + - ginkgolinter + - gocheckcompilerdirectives + - gochecknoinits + - gochecksumtype + #- gocognit + - gocritic + #- gocyclo + #- godoclint + - goheader + - gomoddirectives + - gomodguard_v2 + - goprintffuncname + #- gosec + - gosmopolitan + - grouper + - iface + - importas + - inamedparam + - interfacebloat + - intrange + - iotamixing + - lll + - loggercheck + - makezero + - misspell + - modernize + - nakedret + #- nestif + - nilerr + - nilnesserr + - nlreturn + - nonamedreturns + - nosprintfhostport + - paralleltest + - perfsprint + - prealloc + #- predeclared + - promlinter + - protogetter + - reassign + - recvcheck + #- revive + - rowserrcheck + - sloglint + - spancheck + - sqlclosecheck + - tagalign + - testableexamples + - testifylint + - testpackage + - thelper + - tparallel + - unconvert + - unparam + - unqueryvet + - usestdlibvars + - usetesting + - varnamelen + - wastedassign + - whitespace + - wsl_v5 + - zerologlint + + settings: + varnamelen: + ignore-names: + - err + - wg + - mu + - ts + - to + - es + - op + - fd + - id + - fn + diff --git a/Makefile b/Makefile index 66ac406..23e2825 100644 --- a/Makefile +++ b/Makefile @@ -68,10 +68,10 @@ test: clean buildlocal testlint: test lint lint: - golangci-lint run + golangci-lint run --enable-only errcheck,govet,ineffassign,staticcheck,unused lint-full: - golangci-lint run --enable-all --exclude-use-default --disable exhaustivestruct,exhaustruct,depguard,interfacer,deadcode,golint,structcheck,scopelint,varcheck,ifshort,maligned,nosnakecase,godot,funlen,gofumpt,cyclop,noctx,gochecknoglobals,paralleltest + golangci-lint run --show-stats=false testfuzzy: clean go test -fuzz ./... $(ARGS) diff --git a/cmd/cluster_reroute.go b/cmd/cluster_reroute.go index 935de1a..3518fb8 100644 --- a/cmd/cluster_reroute.go +++ b/cmd/cluster_reroute.go @@ -177,7 +177,6 @@ func ClusterRerouteAllocatePrimary(conf *cfg.Config, stale bool) *cli.Command { if stale { name = "allocate-stale-primary" usage = "allocate a stale primary shard to a node" - } return &cli.Command{ diff --git a/cmd/completion.go b/cmd/completion.go index fdec91c..3862765 100644 --- a/cmd/completion.go +++ b/cmd/completion.go @@ -48,8 +48,10 @@ func complete(cmd *cli.Command, what int) { return } - var list []string - var err error + var ( + list []string + err error + ) switch what { case Cindex: diff --git a/cmd/ilm_forecast.go b/cmd/ilm_forecast.go index 968172d..1408545 100644 --- a/cmd/ilm_forecast.go +++ b/cmd/ilm_forecast.go @@ -71,7 +71,7 @@ func IlmForecastList(conf *cfg.Config) *cli.Command { }, &cli.StringFlag{ Name: "within", - Usage: " withing which to forecast", + Usage: " within which to forecast", Destination: &conf.Ilm.Within, Aliases: []string{"w"}, }, @@ -121,7 +121,7 @@ func IlmForecastShow(conf *cfg.Config) *cli.Command { }, &cli.StringFlag{ Name: "within", - Usage: "duration withing which to forecast", + Usage: "duration within which to forecast", Destination: &conf.Ilm.Within, Aliases: []string{"w"}, }, diff --git a/cmd/index.go b/cmd/index.go index 53d947c..943ec0b 100644 --- a/cmd/index.go +++ b/cmd/index.go @@ -19,7 +19,6 @@ package cmd import ( "context" "errors" - "fmt" "codeberg.org/scip/esctl/pkg/cfg" "codeberg.org/scip/esctl/pkg/es" @@ -163,7 +162,7 @@ Valid field mapping types: integer, text, date, keyword`, Action: func(ctx context.Context, cmd *cli.Command) error { args := cmd.Args() if args.Len() == 0 { - return fmt.Errorf("no index specified") + return errors.New("no index specified") } mappings := args.Slice()[1:] diff --git a/cmd/index_alias.go b/cmd/index_alias.go index 5f4098f..b490f60 100644 --- a/cmd/index_alias.go +++ b/cmd/index_alias.go @@ -38,7 +38,8 @@ func IndexAlias(conf *cfg.Config) *cli.Command { IndexAliasDelete(conf), IndexAliasRollover(conf), // FIXME: implement IndexAliasAdd - //IndexAliasAdd(conf), // see https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-indices-update-aliases + // see https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-indices-update-aliases + // IndexAliasAdd(conf), }, } } diff --git a/cmd/index_template.go b/cmd/index_template.go index b00bd72..ae66d6e 100644 --- a/cmd/index_template.go +++ b/cmd/index_template.go @@ -19,7 +19,6 @@ package cmd import ( "context" "errors" - "fmt" "slices" "codeberg.org/scip/esctl/pkg/cfg" @@ -180,7 +179,7 @@ https://www.elastic.co/docs/reference/elasticsearch/index-settings Action: func(ctx context.Context, cmd *cli.Command) error { args := cmd.Args() if args.Len() == 0 { - return fmt.Errorf("no name specified") + return errors.New("no name specified") } if conf.Mode != "" { diff --git a/cmd/root.go b/cmd/root.go index 2eac0a1..1ad6e3f 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -31,9 +31,60 @@ import ( "github.com/urfave/cli/v3" ) +const ( + versionFmt = `esctl version: %s + build: %s + branch: %s + commit: %s + go version: %s + API Version: %s` + + jsonPathUsage = `jsonPath usage: + +name.last >> "Anderson" +age >> 37 +children >> ["Sara","Alex","Jack"] +children.# >> 3 +children.1 >> "Alex" +child*.2 >> "Jack" +c?ildren.0 >> "Sara" +fav\.movie >> "Deer Hunter" +friends.#.first >> ["Dale","Roger","Jane"] +friends.1.last >> "Craig" + +You can also query an array for the first match by using #(...), or +find all matches with #(...)#. Queries support the ==, !=, <, <=, >, +>= comparison operators and the simple pattern matching % (like) and +!% (not like) operators. Eg: + +friends.#(last=="Murphy").first >> "Dale" +friends.#(last=="Murphy")#.first >> ["Dale","Jane"] +friends.#(age>45)#.last >> ["Craig","Murphy"] +friends.#(first%"D*").last >> "Murphy" +friends.#(first!%"D*").last >> "Craig" +friends.#(nets.#(=="fb"))#.first >> ["Dale","Roger"] + +To extract more than one field, use: + +{"ns":source.namespace_name,"time":source.@timestamp} >> + +{ + "ns": "3f80316965c64405-275a75782e984bcc8d82", + "time": "2026-06-10T04:23:38.914080361+00:00" +} +{ + "ns": "3f80316965c64405-f0e1dcef668e45bb9afd", + "time": "2026-06-10T09:39:00.554892542+00:00" +} + + +Documentation: https://github.com/tidwall/gjson/blob/master/SYNTAX.md` +) + func Finish(err error) int { if err != nil { fmt.Fprintln(os.Stderr, "Error: ", err.Error()) + return 1 } @@ -127,6 +178,7 @@ func Main() int { return nil, err } else { fmt.Println(cmd.UsageText) + return nil, nil } } @@ -161,53 +213,13 @@ func Main() int { } func HelpJsonPath(conf *cfg.Config) *cli.Command { - msg := `jsonPath usage: - -name.last >> "Anderson" -age >> 37 -children >> ["Sara","Alex","Jack"] -children.# >> 3 -children.1 >> "Alex" -child*.2 >> "Jack" -c?ildren.0 >> "Sara" -fav\.movie >> "Deer Hunter" -friends.#.first >> ["Dale","Roger","Jane"] -friends.1.last >> "Craig" - -You can also query an array for the first match by using #(...), or -find all matches with #(...)#. Queries support the ==, !=, <, <=, >, ->= comparison operators and the simple pattern matching % (like) and -!% (not like) operators. Eg: - -friends.#(last=="Murphy").first >> "Dale" -friends.#(last=="Murphy")#.first >> ["Dale","Jane"] -friends.#(age>45)#.last >> ["Craig","Murphy"] -friends.#(first%"D*").last >> "Murphy" -friends.#(first!%"D*").last >> "Craig" -friends.#(nets.#(=="fb"))#.first >> ["Dale","Roger"] - -To extract more than one field, use: - -{"ns":source.namespace_name,"time":source.@timestamp} >> - -{ - "ns": "3f80316965c64405-275a75782e984bcc8d82", - "time": "2026-06-10T04:23:38.914080361+00:00" -} -{ - "ns": "3f80316965c64405-f0e1dcef668e45bb9afd", - "time": "2026-06-10T09:39:00.554892542+00:00" -} - - -Documentation: https://github.com/tidwall/gjson/blob/master/SYNTAX.md` - return &cli.Command{ Name: "help-jsonpath", Usage: "show jsonpath help", Action: func(ctx context.Context, cmd *cli.Command) error { - _, err := fmt.Println(msg) + _, err := fmt.Println(jsonPathUsage) + return err }, } @@ -219,8 +231,9 @@ func Version(conf *cfg.Config) *cli.Command { Usage: "show esctl version information", Action: func(ctx context.Context, cmd *cli.Command) error { - _, err := fmt.Printf("esctl version: %s\n build: %s\n branch: %s\n commit: %s\n go version: %s\n API Version: %s\n", + _, err := fmt.Printf(versionFmt, cfg.Version, cfg.BUILD, cfg.BRANCH, cfg.COMMIT, cfg.GOVERSION, cfg.APIVERSION) + return err }, } @@ -327,7 +340,6 @@ func HelpUsage(conf *cfg.Config) *cli.Command { }) }, } - } // copy of cmd.Walk() with the exception to skip hidden commands and its siblings diff --git a/cmd/utilities.go b/cmd/utilities.go index c8efb97..a4715a7 100644 --- a/cmd/utilities.go +++ b/cmd/utilities.go @@ -29,6 +29,7 @@ func addReference(ref string) string { for line := range strings.SplitSeq(ref, "\n") { indentedRef = append(indentedRef, " "+line) } + return fmt.Sprintf("%s\nREFERENCE:\n%s\n", cli.SubcommandHelpTemplate, strings.Join(indentedRef, "\n")) diff --git a/pkg/cfg/cluster.go b/pkg/cfg/cluster.go index 7d2a28a..0a3d3fd 100644 --- a/pkg/cfg/cluster.go +++ b/pkg/cfg/cluster.go @@ -55,37 +55,6 @@ func (cluster *Cluster) SetClient(client *elasticsearch.TypedClient) { cluster.client = client } -func (cluster *Cluster) getTransport() elastictransport.Option { - transport := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - - if cluster.DebugHTTP { - return elastictransport.WithTransport( - &DebugTransport{Transport: transport}, - ) - } - - return elastictransport.WithTransport(transport) -} - -func (cluster *Cluster) getDefaultOptions() []elasticsearch.Option { - // These headers are not needed with ES 9, but with ES 8, we set - // them here so every API call uses it. The only exception being - // the api repl, which does it on its own. - headers := http.Header{} - headers.Add("content-type", "application/json") - headers.Add("Accept", "application/json") - - return []elasticsearch.Option{ - elasticsearch.WithAddresses(cluster.Uri), - elasticsearch.WithTransportOptions( - cluster.getTransport(), - elastictransport.WithHeader(headers), - ), - } -} - // return the go-elasticsearch client object but before doing that, // check if we need to tune auth func (cluster *Cluster) ES() *elasticsearch.TypedClient { @@ -114,7 +83,8 @@ func (cluster *Cluster) CheckAuth() error { } else { // k, try interactively fmt.Fprintf(os.Stderr, "Enter password for elasticsearch user %s@%s: ", cluster.User, cluster.Name) - pass, err := term.ReadPassword(int(syscall.Stdin)) + + pass, err := term.ReadPassword(syscall.Stdin) if err != nil { return err } @@ -125,6 +95,7 @@ func (cluster *Cluster) CheckAuth() error { } cluster.Pass = string(pass) + fmt.Println() } @@ -132,7 +103,6 @@ func (cluster *Cluster) CheckAuth() error { opts = append(opts, elasticsearch.WithBasicAuth(cluster.User, cluster.Pass)) es, err := elasticsearch.NewTyped(opts...) - if err != nil { return fmt.Errorf("failed to setup elasticsearch connection: %w", err) } @@ -205,6 +175,37 @@ func (cluster *Cluster) IsReachable() (bool, error) { return true, conn.Close() } +func (cluster *Cluster) getDefaultOptions() []elasticsearch.Option { + // These headers are not needed with ES 9, but with ES 8, we set + // them here so every API call uses it. The only exception being + // the api repl, which does it on its own. + headers := http.Header{} + headers.Add("Content-Type", "application/json") + headers.Add("Accept", "application/json") + + return []elasticsearch.Option{ + elasticsearch.WithAddresses(cluster.Uri), + elasticsearch.WithTransportOptions( + cluster.getTransport(), + elastictransport.WithHeader(headers), + ), + } +} + +func (cluster *Cluster) getTransport() elastictransport.Option { + transport := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + + if cluster.DebugHTTP { + return elastictransport.WithTransport( + &DebugTransport{Transport: transport}, + ) + } + + return elastictransport.WithTransport(transport) +} + func (conf *Config) SetupES() error { for name, cluster := range conf.Clusters { cluster.Name = name @@ -220,7 +221,6 @@ func (conf *Config) SetupES() error { } es, err := elasticsearch.NewTyped(opts...) - if err != nil { return fmt.Errorf("failed to setup elasticsearch connection: %w", err) } diff --git a/pkg/cfg/config.go b/pkg/cfg/config.go index dc3505a..a1588be 100644 --- a/pkg/cfg/config.go +++ b/pkg/cfg/config.go @@ -151,45 +151,6 @@ func (conf *Config) Init() error { return nil } -func (conf *Config) determineDefaultCluster() error { - if conf.CurrentCluster != "" { - // -C specified, set current cluster explicitly, no matter what the config says - current, exists := conf.Clusters[conf.CurrentCluster] - if !exists { - return fmt.Errorf("no cluster with alias %s configured", conf.CurrentCluster) - } else { - conf.DefaultCluster = current - - // disable all others - for _, cluster := range conf.Clusters { - cluster.Default = false - } - - conf.DefaultCluster.Default = true - } - } else { - // we need to determine ourselfes - if len(conf.Clusters) == 1 { - // ok, just one cluster configured, use this, of course - for name, cluster := range conf.Clusters { - conf.DefaultCluster = cluster - conf.CurrentCluster = name - conf.DefaultCluster.Default = true - } - } else { - // multiple ones exists, look if one is set as default - for name, cluster := range conf.Clusters { - if cluster.Default { - conf.DefaultCluster = cluster - conf.CurrentCluster = name - } - } - } - } - - return nil -} - // we are using reflect to clone a config obj w/o the ES stuff for // shorter repr.Println() output (the ES structure is just too large) func (conf *Config) Clone() Config { @@ -198,14 +159,14 @@ func (conf *Config) Clone() Config { ref := reflect.ValueOf(*conf) typeOfS := ref.Type() - for i := 0; i < ref.NumField(); i++ { - field := typeOfS.Field(i).Name + for idx := range ref.NumField() { + field := typeOfS.Field(idx).Name - if field == "Clusters" || field == "DefaultCluster" || !ref.Field(i).CanInterface() { + if field == "Clusters" || field == "DefaultCluster" || !ref.Field(idx).CanInterface() { continue } - reflect.ValueOf(&clone).Elem().FieldByName(field).Set(reflect.ValueOf(ref.Field(i).Interface())) + reflect.ValueOf(&clone).Elem().FieldByName(field).Set(reflect.ValueOf(ref.Field(idx).Interface())) } return clone @@ -266,6 +227,7 @@ func (conf *Config) LoadConfig() error { for _, cluster := range conf.Clusters { if cluster.Default { conf.DefaultCluster = cluster + break } } @@ -278,9 +240,47 @@ func (conf *Config) LoadConfig() error { return nil } +func (conf *Config) determineDefaultCluster() error { + if conf.CurrentCluster != "" { + // -C specified, set current cluster explicitly, no matter what the config says + current, exists := conf.Clusters[conf.CurrentCluster] + if !exists { + return fmt.Errorf("no cluster with alias %s configured", conf.CurrentCluster) + } else { + conf.DefaultCluster = current + + // disable all others + for _, cluster := range conf.Clusters { + cluster.Default = false + } + + conf.DefaultCluster.Default = true + } + } else { + // we need to determine ourselfes + if len(conf.Clusters) == 1 { + // ok, just one cluster configured, use this, of course + for name, cluster := range conf.Clusters { + conf.DefaultCluster = cluster + conf.CurrentCluster = name + conf.DefaultCluster.Default = true + } + } else { + // multiple ones exists, look if one is set as default + for name, cluster := range conf.Clusters { + if cluster.Default { + conf.DefaultCluster = cluster + conf.CurrentCluster = name + } + } + } + } + + return nil +} + func fileExists(filename string) bool { info, err := os.Stat(filename) - if err != nil { // return false on any error return false diff --git a/pkg/cfg/jq.go b/pkg/cfg/jq.go index 6b328bc..5eeb5cc 100644 --- a/pkg/cfg/jq.go +++ b/pkg/cfg/jq.go @@ -25,6 +25,7 @@ import ( func isJQinstalled() bool { cmd := exec.CommandContext(context.Background(), "jq", "-h") + out, err := cmd.Output() if err != nil { return false diff --git a/pkg/cfg/transport.go b/pkg/cfg/transport.go index f4c8015..070ef29 100644 --- a/pkg/cfg/transport.go +++ b/pkg/cfg/transport.go @@ -43,6 +43,7 @@ func (t *DebugTransport) RoundTrip(req *http.Request) (*http.Response, error) { } var pretty bytes.Buffer + err = json.Indent(&pretty, buf.Bytes(), "", "\t") if err != nil { return nil, fmt.Errorf("json parse error: %w", err) diff --git a/pkg/es/api.go b/pkg/es/api.go index fdb2fcc..c8e71b2 100644 --- a/pkg/es/api.go +++ b/pkg/es/api.go @@ -26,6 +26,7 @@ import ( "errors" "fmt" "io" + "log" "log/slog" "net/http" "os" @@ -92,7 +93,6 @@ func ApiRepl(conf *cfg.Config) error { EOFPrompt: "exit", HistorySearchFold: true, }) - if err != nil { return fmt.Errorf("failed to initialize readline lib: %w", err) } @@ -112,11 +112,13 @@ func ApiRepl(conf *cfg.Config) error { parts := strings.SplitN(strings.TrimSpace(text), " ", 3) if len(parts) < 2 { fmt.Println("error: you need to input a verb, uri [and post data]") + continue } if !slices.Contains(verbs, strings.ToLower(parts[0])) { fmt.Println("error: verb must be one of " + strings.Join(verbs, ",")) + continue } @@ -144,6 +146,7 @@ func ApiRepl(conf *cfg.Config) error { pageJsonOutput(conf, raw) } + //nolint:nilerr return nil } @@ -175,7 +178,6 @@ func pageJsonOutput(conf *cfg.Config, raw []byte) { pager.Stderr = os.Stderr err := pager.Run() - if err != nil { fmt.Printf("failed to execute pager '%s': %s", conf.Pager, err) } @@ -197,16 +199,16 @@ func CallAPI(conf *cfg.Config, verb, path, data string) ([]byte, error) { verb = strings.ToUpper(verb) // we're using port-forwards anyway - tr := &http.Transport{ + noVerifyTransport := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } - client := &http.Client{Transport: tr} + client := &http.Client{Transport: noVerifyTransport} if conf.DebugHTTP { client = &http.Client{ Transport: &cfg.DebugTransport{ - Transport: tr}} + Transport: noVerifyTransport}} } req, err := http.NewRequest(verb, conf.DefaultCluster.Uri+path, bytes.NewBuffer([]byte(data))) @@ -215,7 +217,7 @@ func CallAPI(conf *cfg.Config, verb, path, data string) ([]byte, error) { } req.Header.Add("Content-Type", "application/json") - req.Header.Add("accept", "application/json") + req.Header.Add("Accept", "application/json") // make sure we have got all we need if err := conf.DefaultCluster.CheckAuth(); err != nil { @@ -233,6 +235,11 @@ func CallAPI(conf *cfg.Config, verb, path, data string) ([]byte, error) { if err != nil { return nil, err } + defer func() { + if err := resp.Body.Close(); err != nil { + log.Fatal(err) + } + }() // Read and print response body, err := io.ReadAll(resp.Body) @@ -249,6 +256,7 @@ func prettyfiJson(conf *cfg.Config, raw []byte) (string, error) { cmd.Stdin = bytes.NewReader(raw) var out bytes.Buffer + cmd.Stdout = &out err := cmd.Run() @@ -258,7 +266,9 @@ func prettyfiJson(conf *cfg.Config, raw []byte) (string, error) { return out.String(), nil } + var pretty bytes.Buffer + err := json.Indent(&pretty, raw, "", "\t") if err != nil { return "", fmt.Errorf("json parse error: %w", err) @@ -270,10 +280,10 @@ func prettyfiJson(conf *cfg.Config, raw []byte) (string, error) { // interactively read arbitrary JSON data from STDIN, which is // virtually a repl inside the primary repl func readJSON(input string) (string, error) { - data := "" + data := strings.Builder{} if input != "" { - data = input + data.WriteString(input) } else { scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { @@ -283,23 +293,23 @@ func readJSON(input string) (string, error) { break } - data += line + data.WriteString(line) } } - if data == "" { - return data, nil + if data.Len() == 0 { + return "", nil } // validate check := map[string]any{} - err := json.Unmarshal([]byte(data), &check) + + err := json.Unmarshal([]byte(data.String()), &check) if err != nil { return "", fmt.Errorf("error: input data is not proper JSON: %w", err) - } - return data, nil + return data.String(), nil } func ApiList(conf *cfg.Config, pattern string) error { @@ -338,6 +348,7 @@ func ApiList(conf *cfg.Config, pattern string) error { } table.Sort() + return table.Print() } @@ -345,10 +356,13 @@ func ApiList(conf *cfg.Config, pattern string) error { func ApiPathNames() []string { assets.LoadAssetOpenApi() - paths := []string{} + paths := make([]string, len(assets.OpenAPI.Spec().Paths.Paths)) + + idx := 0 for path := range assets.OpenAPI.Spec().Paths.Paths { - paths = append(paths, path) + paths[idx] = path + idx++ } return paths @@ -356,6 +370,7 @@ func ApiPathNames() []string { func ApiShow(conf *cfg.Config, showpath, verb string) error { assets.LoadAssetOpenApi() + out := printer.Builder{} op, err := matchOperation(showpath, verb) @@ -372,18 +387,20 @@ func ApiShow(conf *cfg.Config, showpath, verb string) error { sample := getApiExample(op) description := markdown.Render(cleanMarkup.ReplaceAllString(op.Op.Description, ""), width, cfg.DefaultMargin) - var bold = lipgloss.NewStyle(). - Bold(true) - var paragraph = lipgloss.NewStyle(). - MarginBottom(1). - MarginLeft(cfg.DefaultMargin) - var boldparagraph = lipgloss.NewStyle(). - MarginBottom(1). - MarginLeft(cfg.DefaultMargin). - Bold(true) - var indentparagraph = lipgloss.NewStyle(). - MarginBottom(1). - MarginLeft(2) + var ( + bold = lipgloss.NewStyle(). + Bold(true) + paragraph = lipgloss.NewStyle(). + MarginBottom(1). + MarginLeft(cfg.DefaultMargin) + boldparagraph = lipgloss.NewStyle(). + MarginBottom(1). + MarginLeft(cfg.DefaultMargin). + Bold(true) + indentparagraph = lipgloss.NewStyle(). + MarginBottom(1). + MarginLeft(2) + ) out.WriteStringLine(bold.Render("ID: " + op.Op.ID)) out.WriteStringLine(paragraph.Render(fmt.Sprintf("%s %s", op.Verb, showpath))) @@ -399,10 +416,11 @@ func ApiShow(conf *cfg.Config, showpath, verb string) error { if len(params.Path) > 0 { out.WriteStringLine(bold.Render("Path Parameters")) + for _, param := range params.Path { out.WriteStringLine(boldparagraph.Render(param.Param)) + if param.Description != "" { - //out.WriteStringLine(indentparagraph.Render(param.Description)) out.WriteStringLine(param.Description) } } @@ -410,8 +428,10 @@ func ApiShow(conf *cfg.Config, showpath, verb string) error { if len(params.Query) > 0 { out.WriteStringLine(bold.Render("Query Parameters")) + for _, param := range params.Query { out.WriteStringLine(boldparagraph.Render(param.Param)) + if param.Description != "" { out.WriteStringLine(indentparagraph.Render(param.Description)) } @@ -493,10 +513,8 @@ func getApiExample(op *Op) string { if hassource { sample = strings.TrimSpace(source.(string)) } - } } - } } @@ -509,11 +527,13 @@ func getApiExample(op *Op) string { func matchOperation(showpath, verb string) (*Op, error) { ops := []*Op{} op := &Op{} + var found bool for path, item := range assets.OpenAPI.Spec().Paths.Paths { if path == showpath { ops = findOperation(item.PathItemProps) + break } } @@ -530,6 +550,7 @@ func matchOperation(showpath, verb string) (*Op, error) { if strings.ToLower(item.Verb) == verb { op = item found = true + break } } diff --git a/pkg/es/ccr.go b/pkg/es/ccr.go index 7817318..26aa3b2 100644 --- a/pkg/es/ccr.go +++ b/pkg/es/ccr.go @@ -46,9 +46,8 @@ func CcrStatus(conf *cfg.Config, leader, follower string) error { } // reverse attribution - f := follower - follower = leader - leader = f + follower, leader = leader, follower + slog.Debug("leader/follower attribution is invalid, reversing", "leader", leader, "follower", follower) } @@ -93,11 +92,13 @@ func CcrRemoteInfo(conf *cfg.Config, index string) error { slog.Debug("ccr remote info", "info", res) remote := "" + var info *types.ClusterRemoteProxyInfo for name, data := range res { remote = name info = data.(*types.ClusterRemoteProxyInfo) + break } diff --git a/pkg/es/ccr_follower.go b/pkg/es/ccr_follower.go index b2da1b7..feb8f9e 100644 --- a/pkg/es/ccr_follower.go +++ b/pkg/es/ccr_follower.go @@ -18,6 +18,7 @@ package es import ( "context" + "errors" "fmt" "log/slog" @@ -35,11 +36,12 @@ func getRemoteName(conf *cfg.Config) (string, error) { remote := "" for name := range res { remote = name + break } if remote == "" { - return "", fmt.Errorf("cluster doesn't have a follower") + return "", errors.New("cluster doesn't have a follower") } return remote, nil @@ -61,26 +63,31 @@ func CcrFollowerRenew(conf *cfg.Config, index string) error { if err := wrapError(IndexClose, conf, index); err != nil { return err } + fmt.Printf("closed %s", index) if err := wrapError(CcrFollowerPause, conf, index); err != nil { return err } + fmt.Printf("paused %s", index) if err := wrapError(CcrFollowerUnfollow, conf, index); err != nil { return err } + fmt.Printf("unfollowed %s", index) if err := wrapError(IndexDelete, conf, index); err != nil { return err } + fmt.Printf("deleted %s", index) if err := CcrFollowerAdd(conf, index); err != nil { return err } + fmt.Printf("added follower %s", index) return nil @@ -89,7 +96,6 @@ func CcrFollowerRenew(conf *cfg.Config, index string) error { func CcrFollowerResume(conf *cfg.Config, index string) error { _, err := conf.DefaultCluster.ES().Ccr.ResumeFollow(index). Do(context.Background()) - if err != nil { return fmt.Errorf("failed to resume ccr following: %w", esErrorString(err)) } @@ -100,7 +106,6 @@ func CcrFollowerResume(conf *cfg.Config, index string) error { func CcrFollowerPause(conf *cfg.Config, index string) error { _, err := conf.DefaultCluster.ES().Ccr.PauseFollow(index). Do(context.Background()) - if err != nil { return fmt.Errorf("failed to pause ccr following: %w", esErrorString(err)) } @@ -111,7 +116,6 @@ func CcrFollowerPause(conf *cfg.Config, index string) error { func CcrFollowerUnfollow(conf *cfg.Config, index string) error { _, err := conf.DefaultCluster.ES().Ccr.ForgetFollower(index). Do(context.Background()) - if err != nil { return fmt.Errorf("failed to unfollow index: %w", esErrorString(err)) } @@ -134,7 +138,6 @@ func CcrFollowerAdd(conf *cfg.Config, index string) error { } _, err = create.Do(context.Background()) - if err != nil { return fmt.Errorf("failed to create follower index: %w", esErrorString(err)) } diff --git a/pkg/es/cluster.go b/pkg/es/cluster.go index 178af8b..8222859 100644 --- a/pkg/es/cluster.go +++ b/pkg/es/cluster.go @@ -38,13 +38,15 @@ type clusterReachable struct { } func ClusterList(conf *cfg.Config) error { - var mu sync.Mutex - var wg sync.WaitGroup + var ( + mu sync.Mutex + wg sync.WaitGroup + ) + reachable := make(map[string]clusterReachable, len(conf.Clusters)) // check endpoints in parallel to speed things up for name, cluster := range conf.Clusters { - wg.Go(func() { online, err := cluster.IsReachable() @@ -119,26 +121,26 @@ func getClusterStatus(conf *cfg.Config) (*apiResponse, error) { var err error - for i := 0; i < gocount; i++ { - r := <-responses + for range gocount { + res := <-responses - err = errors.Join(err, r.error) + err = errors.Join(err, res.error) - switch r.which { + switch res.which { case ResponseHealth: - all.health = r.health + all.health = res.health case ResponseCcr: - all.ccr = r.ccr + all.ccr = res.ccr case ResponseInfo: - all.info = r.info + all.info = res.info case ResponseStats: - all.stats = r.stats + all.stats = res.stats case ResponseIndices: - all.indices = r.indices + all.indices = res.indices case ResponseTasks: - all.tasks = r.tasks + all.tasks = res.tasks case ResponseHealthReport: - all.healthreport = r.healthreport + all.healthreport = res.healthreport } } @@ -153,8 +155,10 @@ func ClusterStatus(conf *cfg.Config) error { slog.Debug("ES result", "cluster health", res.health) - var isleader bool - var ccrfollowing string + var ( + isleader bool + ccrfollowing string + ) if res.ccr != nil { isleader = len(res.ccr.AutoFollowStats.AutoFollowedClusters) == 0 @@ -171,6 +175,7 @@ func ClusterStatus(conf *cfg.Config) error { // look for red indices, if any redindices := 0 + for _, index := range *res.indices { if *index.Health == "red" { redindices++ @@ -179,6 +184,7 @@ func ClusterStatus(conf *cfg.Config) error { // look for long running tasks longtasks := 0 + for _, task := range *res.tasks { if strings.Contains(*task.RunningTime, "d") { longtasks++ @@ -211,7 +217,7 @@ func ClusterStatus(conf *cfg.Config) error { } if conf.Verbose { - table = gatherClusterStats(conf, res.stats, table) + table = gatherClusterStats(res.stats, table) } if res.health.Status.Name != "green" { @@ -239,9 +245,11 @@ func ClusterStatus(conf *cfg.Config) error { return nil } -func gatherClusterStats(conf *cfg.Config, clusterstats *clusterstats.Response, table *printer.Table) *printer.Table { - var querycount int64 - var vmversion string +func gatherClusterStats(clusterstats *clusterstats.Response, table *printer.Table) *printer.Table { + var ( + querycount int64 + vmversion string + ) for _, count := range clusterstats.Indices.Search.Queries { querycount += count diff --git a/pkg/es/cluster_reroute.go b/pkg/es/cluster_reroute.go index cda9d3a..47582ce 100644 --- a/pkg/es/cluster_reroute.go +++ b/pkg/es/cluster_reroute.go @@ -118,6 +118,7 @@ func ClusterRerouteAllocatePrimary(conf *cfg.Config, index string, stale bool) e if stale { which = "stale" } + return fmt.Errorf("failed to allocate %s primary shard: %w", which, err) } diff --git a/pkg/es/cluster_settings.go b/pkg/es/cluster_settings.go index 0587250..a4e85d4 100644 --- a/pkg/es/cluster_settings.go +++ b/pkg/es/cluster_settings.go @@ -40,6 +40,7 @@ func ClusterSettingsList(conf *cfg.Config) error { table := printer.NewTable(conf, 2, 0) table.Addheaders("setting", "value") + entries := [][]any{} settingshash := res.Persistent // == map[string]json.RawMessage @@ -77,6 +78,7 @@ func ClusterSettingsSet(conf *cfg.Config, args cli.Args) error { if err != nil { return fmt.Errorf("failed to marshall transient value <%v> to valid JSON: %w", value, err) } + put.AddTransient(setting, message) case conf.Persistent: fallthrough @@ -85,6 +87,7 @@ func ClusterSettingsSet(conf *cfg.Config, args cli.Args) error { if err != nil { return fmt.Errorf("failed to marshall persistent value <%v> to valid JSON: %w", value, err) } + put.AddPersistent(setting, message) } } @@ -106,7 +109,6 @@ func ClusterSettingsSetSingle(conf *cfg.Config, setting, value string) error { _, err = conf.DefaultCluster.ES().Cluster.PutSettings(). AddPersistent(setting, message). Do(context.Background()) - if err != nil { return fmt.Errorf("failed to set %s: %w", setting, esErrorString(err)) } diff --git a/pkg/es/cluster_settings_const.go b/pkg/es/cluster_settings_const.go index 7dfc524..f02c2e1 100644 --- a/pkg/es/cluster_settings_const.go +++ b/pkg/es/cluster_settings_const.go @@ -19,174 +19,309 @@ package es // manually extracted from https://www.elastic.co/docs/reference/elasticsearch/configuration-reference var validClusterSettings []string = []string{ - "xpack.security.audit.enabled", - "xpack.security.audit.logfile.events.include", - "xpack.security.audit.logfile.events.exclude", - "xpack.security.audit.logfile.events.emit_request_body", - "xpack.security.audit.logfile.emit_node_name", - "xpack.security.audit.logfile.emit_node_host_address", - "xpack.security.audit.logfile.emit_node_host_name", - "xpack.security.audit.logfile.emit_node_id", - "indices.breaker.total.use_real_memory", - "indices.breaker.total.limit", - "indices.breaker.fielddata.limit", - "indices.breaker.fielddata.overhead", - "indices.breaker.request.limit", - "indices.breaker.request.overhead", - "network.breaker.inflight_requests.limit", - "network.breaker.inflight_requests.overhead", - "script.max_compilations_rate", - "script.painless.regex.enabled", + "action.auto_create_index", + "action.destructive_requires_name", + "attribute_delimiters.groups", + "attribute_patterns.dn", + "attribute_patterns.groups", + "attribute_patterns.mail", + "attribute_patterns.name", + "attribute_patterns.principal", + "attributes.dn", + "attributes.groups", + "attributes.mail", + "attributes.name", + "attributes.principal", + "authentication.enabled", + "auth.secure_password", + "auth.username", "breaker.eql_sequence.limit", "breaker.eql_sequence.overhead", "breaker.eql_sequence.type", "breaker.model_inference.limit", "breaker.model_inference.overhead", "breaker.model_inference.type", + "cache.hash_algo", + "cache.max_users", + "cache.ttl", + "ccr.indices.recovery.chunk_size", + "ccr.indices.recovery.internal_action_timeout", + "ccr.indices.recovery.max_bytes_per_sec", + "ccr.indices.recovery.max_concurrent_file_chunks", + "ccr.indices.recovery.recovery_activity_timeout", + "claim_patterns.dn", + "claim_patterns.group", + "claim_patterns.groups", + "claim_patterns.mail", + "claim_patterns.name", + "claim_patterns.principal", + "claims.dn", + "claims.groups", + "claims.mail", + "claims.name", + "claims.principal", + "client_authentication.rotation_grace_period", + "client_authentication.shared_secret", + "client_authentication.type", + "cluster_alerts.management.blacklist", + "cluster_alerts.management.enabled", + "cluster.auto_shrink_voting_configuration", + "cluster.discovery_configuration_check.interval", + "cluster.election.duration", + "cluster.election.initial_timeout", + "cluster.election.max_timeout", + "cluster.fault_detection.follower_check.retry_count", + "cluster.fault_detection.follower_check.timeout", + "cluster.fault_detection.leader_check.interval", + "cluster.fault_detection.leader_check.retry_count", + "cluster.fault_detection.leader_check.timeout", + "cluster.follower_lag.timeout", + "cluster.indices.close.enable", + "cluster.info.update.interval", + "cluster.initial_master_nodes", + "cluster.join_validation.cache_timeout", + "cluster.lifecycle.default.rollover", + "cluster.max_voting_config_exclusions", + "cluster.no_master_block", + "cluster.publish.info_timeout", + "cluster.publish.timeout", + "cluster.reindex.pit.keep_alive", + "cluster.remote.initial_connect_timeout", + "cluster.remote.node.attr", + "cluster.remote.signing.certificate_authorities", + "cluster.remote.signing.diagnose.trust", + "cluster.remote.signing.truststore.algorithm", + "cluster.remote.signing.truststore.path", + "cluster.remote.signing.truststore.secure_password", + "cluster.remote.signing.truststore.type", + "cluster.routing.allocation.allow_rebalance", + "cluster.routing.allocation.awareness.attributes", + "cluster.routing.allocation.awareness.force.*", + "cluster.routing.allocation.balance.disk_usage", + "cluster.routing.allocation.balance.index", + "cluster.routing.allocation.balance.shard", + "cluster.routing.allocation.balance.threshold", + "cluster.routing.allocation.balance.write_load", + "cluster.routing.allocation.cluster_concurrent_rebalance", + "cluster.routing.allocation.disk.threshold_enabled", + "cluster.routing.allocation.disk.watermark.enable_for_single_data_node", + "cluster.routing.allocation.disk.watermark.flood_stage", + "cluster.routing.allocation.disk.watermark.flood_stage.frozen", + "cluster.routing.allocation.disk.watermark.flood_stage.frozen.max_headroom", + "cluster.routing.allocation.disk.watermark.flood_stage.max_headroom", + "cluster.routing.allocation.disk.watermark.high", + "cluster.routing.allocation.disk.watermark.high.max_headroom", + "cluster.routing.allocation.disk.watermark.low", + "cluster.routing.allocation.disk.watermark.low.max_headroom", "cluster.routing.allocation.enable", - "cluster.routing.allocation.same_shard.host", - "cluster.routing.allocation.total_shards_per_node", + "cluster.routing.allocation.exclude.{{attribute}}", + "cluster.routing.allocation.include.{{attribute}}", "cluster.routing.allocation.node_concurrent_incoming_recoveries", "cluster.routing.allocation.node_concurrent_outgoing_recoveries", "cluster.routing.allocation.node_concurrent_recoveries", "cluster.routing.allocation.node_initial_primaries_recoveries", - "cluster.routing.allocation.allow_rebalance", - "cluster.routing.rebalance.enable", - "cluster.routing.allocation.cluster_concurrent_rebalance", - "cluster.routing.allocation.type", - "cluster.routing.allocation.balance.threshold", - "cluster.routing.allocation.balance.shard", - "cluster.routing.allocation.balance.index", - "cluster.routing.allocation.balance.disk_usage", - "cluster.routing.allocation.balance.write_load", - "cluster.routing.allocation.disk.threshold_enabled", - "cluster.routing.allocation.disk.watermark.low", - "cluster.routing.allocation.disk.watermark.low.max_headroom", - "cluster.routing.allocation.disk.watermark.high", - "cluster.routing.allocation.disk.watermark.high.max_headroom", - "cluster.routing.allocation.disk.watermark.enable_for_single_data_node", - "cluster.routing.allocation.disk.watermark.flood_stage", - "cluster.routing.allocation.disk.watermark.flood_stage.max_headroom", - "cluster.routing.allocation.disk.watermark.flood_stage.frozen", - "cluster.routing.allocation.disk.watermark.flood_stage.frozen.max_headroom", - "cluster.info.update.interval", - "cluster.routing.allocation.awareness.attributes", - "cluster.routing.allocation.awareness.force.*", - "cluster.routing.allocation.include.{{attribute}}", "cluster.routing.allocation.require.{{attribute}}", - "cluster.routing.allocation.exclude.{{attribute}}", + "cluster.routing.allocation.same_shard.host", "cluster.routing.allocation.stats.cache.ttl", - "ccr.indices.recovery.max_bytes_per_sec", - "ccr.indices.recovery.max_concurrent_file_chunks", - "ccr.indices.recovery.chunk_size", - "ccr.indices.recovery.recovery_activity_timeout", - "ccr.indices.recovery.internal_action_timeout", - "data_streams.lifecycle.retention.max", - "data_streams.lifecycle.retention.default", - "data_streams.lifecycle.poll_interval", - "cluster.lifecycle.default.rollover", - "data_streams.lifecycle.target.merge.policy.merge_factor", - "data_streams.lifecycle.target.merge.policy.floor_segment", - "data_streams.lifecycle.signalling.error_retry_interval", + "cluster.routing.allocation.total_shards_per_node", + "cluster.routing.allocation.type", + "cluster.routing.rebalance.enable", + "connection.read_timeout", + "connection.timeout", "data_streams.lifecycle.downsampling.max_indices_in_progress", - "dlm.frozen.transition.poll_interval", - "dlm.frozen.transition.thread_pool.size", - "dlm.frozen.transition.thread_pool.queue_size", - "dlm.frozen.cleanup.poll_interval", - "index.lifecycle.prefer_ilm", - "index.lifecycle.origination_date", - "index.dlm.frozen.created", - "discovery.seed_hosts", - "discovery.seed_providers", - "discovery.type", - "cluster.initial_master_nodes", + "data_streams.lifecycle.poll_interval", + "data_streams.lifecycle.retention.default", + "data_streams.lifecycle.retention.max", + "data_streams.lifecycle.signalling.error_retry_interval", + "data_streams.lifecycle.target.merge.policy.floor_segment", + "data_streams.lifecycle.target.merge.policy.merge_factor", + "delegation.enabled", "discovery.cluster_formation_warning_timeout", "discovery.find_peers_interval", + "discovery.find_peers_warning_timeout", "discovery.probe.connect_timeout", "discovery.probe.handshake_timeout", "discovery.request_peers_timeout", - "discovery.find_peers_warning_timeout", + "discovery.seed_hosts", + "discovery.seed_providers", "discovery.seed_resolver.max_concurrent_resolvers", "discovery.seed_resolver.timeout", - "cluster.auto_shrink_voting_configuration", - "cluster.election.duration", - "cluster.election.initial_timeout", - "cluster.election.max_timeout", - "cluster.fault_detection.follower_check.timeout", - "cluster.fault_detection.follower_check.retry_count", - "cluster.fault_detection.leader_check.interval", - "cluster.fault_detection.leader_check.timeout", - "cluster.fault_detection.leader_check.retry_count", - "cluster.follower_lag.timeout", - "cluster.max_voting_config_exclusions", - "cluster.publish.info_timeout", - "cluster.publish.timeout", - "cluster.discovery_configuration_check.interval", - "cluster.join_validation.cache_timeout", - "cluster.no_master_block", - "monitor.fs.health.enabled", - "monitor.fs.health.refresh_interval", - "monitor.fs.health.slow_path_logging_threshold", + "discovery.type", + "dlm.frozen.cleanup.poll_interval", + "dlm.frozen.transition.poll_interval", + "dlm.frozen.transition.thread_pool.queue_size", + "dlm.frozen.transition.thread_pool.size", + "email_defaults.*", + "encryption.certificate", + "encryption.key", + "encryption.keystore.alias", + "encryption.keystore.path", + "encryption.keystore.secure_key_password", + "encryption.keystore.secure_password", + "encryption.keystore.type", + "encryption.secure_key_passphrase", "enrich.cache_size", + "enrich.cleanup_period", "enrich.coordinator_proxy.max_concurrent_requests", "enrich.coordinator_proxy.max_lookups_per_request", "enrich.coordinator_proxy.queue_capacity", "enrich.fetch_size", - "enrich.max_force_merge_attempts", - "enrich.cleanup_period", "enrich.max_concurrent_policy_executions", - "indices.fielddata.cache.size", + "enrich.max_force_merge_attempts", + "fallback_claims.aud", + "fallback_claims.sub", + "files.role_mapping", + "gateway.expected_data_nodes", + "gateway.recover_after_data_nodes", + "gateway.recover_after_time", + "group_search.base_dn", + "group_search.filter", + "group_search.scope", + "group_search.user_attribute", + "health.ilm.max_retries_per_step", + "health.ilm.max_time_on_action", + "health.ilm.max_time_on_step", "health.master_history.has_master_lookup_timeframe", - "master_history.max_age", "health.master_history.identity_changes_threshold", "health.master_history.no_master_transitions_threshold", "health.node.enabled", - "health.reporting.local.monitor.interval", - "health.ilm.max_time_on_action", - "health.ilm.max_time_on_step", - "health.ilm.max_retries_per_step", "health.periodic_logger.enabled", "health.periodic_logger.poll_interval", - "health.shard_capacity.unhealthy_threshold.yellow", - "health.shard_capacity.unhealthy_threshold.red", - "health.master_history.has_master_lookup_timeframe", - "master_history.max_age", - "health.master_history.identity_changes_threshold", - "health.master_history.no_master_transitions_threshold", - "health.node.enabled", "health.reporting.local.monitor.interval", - "health.ilm.max_time_on_action", - "health.ilm.max_time_on_step", - "health.ilm.max_retries_per_step", - "health.periodic_logger.enabled", - "health.periodic_logger.poll_interval", - "health.shard_capacity.unhealthy_threshold.yellow", "health.shard_capacity.unhealthy_threshold.red", - "indices.memory.index_buffer_size", - "indices.memory.min_index_buffer_size", - "indices.memory.max_index_buffer_size", + "health.shard_capacity.unhealthy_threshold.yellow", + "http.bind_host", + "http.client_stats.closed_channels.max_age", + "http.client_stats.closed_channels.max_count", + "http.client_stats.enabled", + "http.compression", + "http.compression_level", + "http.connection_pool_ttl", + "http.connection_read_timeout", + "http.connect_timeout", + "http.cors.enabled", + "http.detailed_errors.enabled", + "http.host", + "http.max_connections", + "http.max_content_length", + "http.max_endpoint_connections", + "http.max_header_size", + "http.max_initial_line_length", + "http.max_warning_header_count", + "http.max_warning_header_size", + "http.pipelining.max_events", + "http.port", + "http.proxy.host", + "http.proxy.port", + "http.proxy.scheme", + "http.publish_host", + "http.publish_port", + "http.socket_timeout", + "http.tcp.keep_alive", + "http.tcp.keep_count", + "http.tcp.keep_idle", + "http.tcp.keep_interval", + "http.tcp.no_delay", + "http.tcp.receive_buffer_size", + "http.tcp.reuse_address", + "http.tcp.send_buffer_size", + "idp.entity_id", + "idp.metadata.http.connect_timeout", + "idp.metadata.http.fail_on_error", + "idp.metadata.http.minimum_refresh", + "idp.metadata.http.read_timeout", + "idp.metadata.http.refresh", + "idp.metadata.path", + "idp.use_single_logout", + "index.dlm.frozen.created", "indexing_pressure.memory.limit", - "xpack.ilm.enabled", - "indices.lifecycle.history_index_enabled", - "indices.lifecycle.poll_interval", - "indices.lifecycle.rollover.only_if_has_documents", "index.lifecycle.indexing_complete", "index.lifecycle.name", "index.lifecycle.origination_date", "index.lifecycle.parse_origination_date", - "index.lifecycle.step.wait_time_threshold", + "index.lifecycle.prefer_ilm", "index.lifecycle.rollover_alias", - "action.auto_create_index", - "action.destructive_requires_name", - "cluster.indices.close.enable", - "stack.templates.enabled", - "xpack.profiling.enabled", - "xpack.profiling.templates.enabled", - "xpack.otel_data.registry.enabled", - "xpack.otel_data.histogram_field_type", - "reindex.remote.whitelist", + "index.lifecycle.step.wait_time_threshold", + "index.name.time_format", + "index.queries.cache.enabled", + "indices.breaker.fielddata.limit", + "indices.breaker.fielddata.overhead", + "indices.breaker.request.limit", + "indices.breaker.request.overhead", + "indices.breaker.total.limit", + "indices.breaker.total.use_real_memory", + "indices.fielddata.cache.size", + "indices.lifecycle.history_index_enabled", + "indices.lifecycle.poll_interval", + "indices.lifecycle.rollover.only_if_has_documents", + "indices.memory.index_buffer_size", + "indices.memory.max_index_buffer_size", + "indices.memory.min_index_buffer_size", + "indices.queries.cache.size", + "indices.query.bool.max_clause_count", + "indices.query.bool.max_nested_depth", + "indices.recovery.max_bytes_per_sec", + "indices.recovery.max_concurrent_file_chunks", + "indices.recovery.max_concurrent_operations", + "indices.recovery.max_concurrent_snapshot_file_downloads", + "indices.recovery.max_concurrent_snapshot_file_downloads_per_node", + "indices.recovery.use_snapshots", + "indices.requests.cache.expire", + "indices.requests.cache.size", + "jwt.cache.size", + "jwt.cache.ttl", + "keytab.path", + "krb.debug", + "load_balance.cache_ttl", + "load_balance.type", + "master_history.max_age", + "monitor.fs.health.enabled", + "monitor.fs.health.refresh_interval", + "monitor.fs.health.slow_path_logging_threshold", + "nameid.allow_create", + "nameid.sp_qualifier", + "network.bind_host", + "network.breaker.inflight_requests.limit", + "network.breaker.inflight_requests.overhead", + "network.host", + "network.publish_host", + "network.tcp.keep_alive", + "network.tcp.keep_count", + "network.tcp.keep_idle", + "network.tcp.keep_interval", + "network.tcp.no_delay", + "network.tcp.receive_buffer_size", + "network.tcp.reuse_address", + "network.tcp.send_buffer_size", + "network.thread.watchdog.interval", + "network.thread.watchdog.quiet_time", + "node.bandwidth.recovery.disk.read", + "node.bandwidth.recovery.disk.write", + "node.bandwidth.recovery.factor.read", + "node.bandwidth.recovery.factor.write", + "node.bandwidth.recovery.network", + "node.bandwidth.recovery.operator.factor", + "node.bandwidth.recovery.operator.factor.max_overcommit", + "node.bandwidth.recovery.operator.factor.read", + "node.bandwidth.recovery.operator.factor.write", + "node.roles: [ ml ]", + "node.roles: [ transform ]", + "op.authorization_endpoint", + "op.endsession_endpoint", + "op.issuer", + "op.jwkset_path", + "op.token_endpoint", + "op.userinfo_endpoint", + "org.elasticsearch.common.network.ThreadWatchdog", + "org.elasticsearch.transport.InboundHandler", + "org.elasticsearch.transport.OutboundHandler", + "pkc_jwkset_reload.enabled", + "pkc_jwkset_reload.file_interval", + "pkc_jwkset_reload.url_interval_max", + "pkc_jwkset_reload.url_interval_min", + "proxy.base_path", "reindex.remote.blocklist", - "cluster.reindex.pit.keep_alive", + "reindex.remote.whitelist", "reindex.ssl.certificate", "reindex.ssl.certificate_authorities", "reindex.ssl.key", @@ -194,721 +329,402 @@ var validClusterSettings []string = []string{ "reindex.ssl.keystore.key_password", "reindex.ssl.keystore.password", "reindex.ssl.keystore.path", - "reindex.ssl.keystore.type", - "reindex.ssl.secure_key_passphrase", "reindex.ssl.keystore.secure_key_password", "reindex.ssl.keystore.secure_password", + "reindex.ssl.keystore.type", + "reindex.ssl.secure_key_passphrase", "reindex.ssl.truststore.password", "reindex.ssl.truststore.path", "reindex.ssl.truststore.secure_password", "reindex.ssl.truststore.type", "reindex.ssl.verification_mode", - "indices.recovery.max_bytes_per_sec", - "indices.recovery.max_concurrent_file_chunks", - "indices.recovery.max_concurrent_operations", - "indices.recovery.use_snapshots", - "indices.recovery.max_concurrent_snapshot_file_downloads", - "indices.recovery.max_concurrent_snapshot_file_downloads_per_node", - "node.bandwidth.recovery.disk.read", - "node.bandwidth.recovery.disk.write", - "node.bandwidth.recovery.network", - "node.bandwidth.recovery.factor.read", - "node.bandwidth.recovery.factor.write", - "node.bandwidth.recovery.operator.factor.read", - "node.bandwidth.recovery.operator.factor.write", - "node.bandwidth.recovery.operator.factor", - "node.bandwidth.recovery.operator.factor.max_overcommit", - "xpack.inference.query_timeout", - "xpack.inference.logging.reset_interval", - "xpack.inference.logging.wait_duration", - "xpack.inference.http.max_response_size", - "xpack.inference.http.max_total_connections", - "xpack.inference.http.max_route_connections", + "remote_cluster.bind_host", + "remote_cluster.host", + "remote_cluster.port", + "remote_cluster.publish_host", + "remote_cluster.publish_port", + "remote_cluster_server.enabled", + "remote_cluster.tcp.keep_alive", + "remote_cluster.tcp.keep_count", + "remote_cluster.tcp.keep_idle", + "remote_cluster.tcp.keep_interval", + "remote_cluster.tcp.no_delay", + "remote_cluster.tcp.receive_buffer_size", + "remote_cluster.tcp.reuse_address", + "remote_cluster.tcp.send_buffer_size", + "repositories.default_repository", + "rp.client_auth_jwt_signature_algorithm", + "rp.client_auth_method", + "rp.client_id", + "rp.client_secret", + "rp.post_logout_redirect_uri", + "rp.redirect_uri", + "rp.requested_scopes", + "rp.response_type", + "rp.signature_algorithm", + "script.max_compilations_rate", + "script.painless.regex.enabled", + "search.aggs.allowed_inline_metric_scripts", + "search.aggs.allowed_stored_metric_scripts", + "search.aggs.only_allowed_metric_scripts", + "search.max_buckets", + "search.task_watchdog.cooldown_period", + "search.task_watchdog.coordinator_threshold", + "search.task_watchdog.data_node_threshold", + "search.task_watchdog.enabled", + "search.task_watchdog.interval", + "signing.certificate", + "signing.key", + "signing.keystore.alias", + "signing.keystore.path", + "signing.keystore.secure_key_password", + "signing.keystore.secure_password", + "signing.keystore.type", + "signing.saml_messages", + "signing.secure_key_passphrase", + "slm.health.failed_snapshot_warn_threshold", + "slm.history_index_enabled", + "slm.retention_duration", + "slm.retention_schedule", + "smtp.auth", + "smtp.connection_timeout", + "smtp.host", + "smtp.local_address", + "smtp.local_port", + "smtp.port", + "smtp.secure_password", + "smtp.send_partial", + "smtp.ssl.trust", + "smtp.starttls.enable", + "smtp.starttls.required", + "smtp.timeout", + "smtp.user", + "smtp.wait_on_quit", + "smtp.write_timeout", + "snapshot.max_concurrent_operations", + "sp.acs", + "sp.entity_id", + "sp.logout", + "ssl.certificate", + "ssl.certificate_authorities", + "ssl.cipher_suites", + "ssl.key", + "ssl.key_passphrase", + "ssl.keystore.key_password", + "ssl.keystore.password", + "ssl.keystore.path", + "ssl.keystore.secure_key_password", + "ssl.keystore.secure_password", + "ssl.keystore.type", + "ssl.secure_key_passphrase", + "ssl.secure_keystore.password", + "ssl.supported_protocols", + "ssl.truststore.password", + "ssl.truststore.path", + "ssl.truststore.secure_password", + "ssl.truststore.type", + "ssl.verification_mode", + "stack.templates.enabled", + "timeout.ldap_search", + "timeout.response", + "timeout.tcp_connect", + "timeout.tcp_read", + "transport.bind_host", + "transport.compress", + "transport.compression_scheme", + "transport.connect_timeout", + "transport.host", + "transport.ping_schedule", + "transport.port", + "transport.profiles.$PROFILE.xpack.security.filter.allow", + "transport.profiles.$PROFILE.xpack.security.filter.deny", + "transport.publish_host", + "transport.publish_port", + "transport.tcp.keep_alive", + "transport.tcp.keep_count", + "transport.tcp.keep_idle", + "transport.tcp.keep_interval", + "transport.tcp.no_delay", + "transport.tcp.receive_buffer_size", + "transport.tcp.reuse_address", + "transport.tcp.send_buffer_size", + "truststore.algorithm", + "truststore.password", + "truststore.path", + "truststore.secure_password", + "user_search.attribute", + "user_search.base_dn", + "user_search.down_level_filter", + "user_search.filter", + "user_search.pool.enabled", + "user_search.pool.health_check.dn", + "user_search.pool.health_check.enabled", + "user_search.pool.health_check.interval", + "user_search.pool.initial_size", + "user_search.pool.size", + "user_search.scope", + "user_search.upn_filter", + "vectors.indexing.use_gpu", + "wait_master.timeout", + "xpack.http.connection_pool_ttl", + "xpack.http.default_connection_timeout", + "xpack.http.default_read_timeout", + "xpack.http.max_response_size", + "xpack.http.proxy.host", + "xpack.http.proxy.port", + "xpack.http.proxy.scheme", + "xpack.http.ssl.certificate", + "xpack.http.ssl.certificate_authorities", + "xpack.http.ssl.cipher_suites", + "xpack.http.ssl.key", + "xpack.http.ssl.keystore.path", + "xpack.http.ssl.keystore.secure_key_password", + "xpack.http.ssl.keystore.secure_password", + "xpack.http.ssl.keystore.type", + "xpack.http.ssl.secure_key_passphrase", + "xpack.http.ssl.supported_protocols", + "xpack.http.ssl.truststore.path", + "xpack.http.ssl.truststore.secure_password", + "xpack.http.ssl.truststore.type", + "xpack.http.ssl.verification_mode", + "xpack.http.tcp.keep_alive", + "xpack.http.whitelist", + "xpack.ilm.enabled", + "xpack.inference.ccm.cache.expiry_time", + "xpack.inference.ccm.cache.weight", + "xpack.inference.endpoint.cache.enabled", + "xpack.inference.endpoint.cache.expiry_time", + "xpack.inference.endpoint.cache.weight", "xpack.inference.http.connection_eviction_interval", "xpack.inference.http.connection_eviction_max_idle_time", + "xpack.inference.http.max_response_size", + "xpack.inference.http.max_route_connections", + "xpack.inference.http.max_total_connections", "xpack.inference.http.request_executor.queue_capacity", "xpack.inference.http.retry.initial_delay", "xpack.inference.http.retry.max_delay_bound", "xpack.inference.http.retry.timeout", - "xpack.inference.truncator.reduction_percentage", - "xpack.inference.endpoint.cache.enabled", - "xpack.inference.endpoint.cache.weight", - "xpack.inference.endpoint.cache.expiry_time", + "xpack.inference.logging.reset_interval", + "xpack.inference.logging.wait_duration", "xpack.inference.oauth2.token_cache.enabled", - "xpack.inference.oauth2.token_cache.weight", "xpack.inference.oauth2.token_cache.expiry_time", - "xpack.inference.ccm.cache.weight", - "xpack.inference.ccm.cache.expiry_time", + "xpack.inference.oauth2.token_cache.weight", + "xpack.inference.query_timeout", + "xpack.inference.truncator.reduction_percentage", "xpack.license.self_generated.type", - "gateway.expected_data_nodes", - "gateway.recover_after_time", - "gateway.recover_after_data_nodes", - "node.roles: [ ml ]", + "xpack.ml.anomalies.heal_reindexed_v7.enabled", + "xpack.ml.enable_config_migration", "xpack.ml.enabled", + "xpack.ml.idle_job_auto_close_timeout", "xpack.ml.inference_model.cache_size", "xpack.ml.inference_model.time_to_live", + "xpack.ml.max_anomaly_records", "xpack.ml.max_inference_processors", + "xpack.ml.max_lazy_ml_nodes", "xpack.ml.max_machine_memory_percent", + "xpack.ml.max_ml_node_size", "xpack.ml.max_model_memory_limit", "xpack.ml.max_open_jobs", - "xpack.ml.nightly_maintenance_requests_per_second", - "xpack.ml.results_index_rollover_max_size", - "xpack.ml.anomalies.heal_reindexed_v7.enabled", - "xpack.ml.idle_job_auto_close_timeout", - "xpack.ml.node_concurrent_job_allocations", - "xpack.ml.enable_config_migration", - "xpack.ml.max_anomaly_records", - "xpack.ml.max_lazy_ml_nodes", - "xpack.ml.max_ml_node_size", - "xpack.ml.trained_models.graph_validation_enabled", "xpack.ml.model_repository", + "xpack.ml.nightly_maintenance_requests_per_second", + "xpack.ml.node_concurrent_job_allocations", "xpack.ml.persist_results_max_retries", "xpack.ml.process_connect_timeout", + "xpack.ml.results_index_rollover_max_size", + "xpack.ml.trained_models.graph_validation_enabled", "xpack.ml.use_auto_machine_memory_percent", - "xpack.monitoring.enabled", - "xpack.monitoring.collection.enabled", - "xpack.monitoring.collection.interval", - "xpack.monitoring.elasticsearch.collection.enabled", "xpack.monitoring.collection.cluster.stats.timeout", - "xpack.monitoring.collection.node.stats.timeout", - "xpack.monitoring.collection.indices", - "xpack.monitoring.collection.index.stats.timeout", + "xpack.monitoring.collection.enabled", "xpack.monitoring.collection.index.recovery.active_only", "xpack.monitoring.collection.index.recovery.timeout", - "xpack.monitoring.history.duration", + "xpack.monitoring.collection.index.stats.timeout", + "xpack.monitoring.collection.indices", + "xpack.monitoring.collection.interval", + "xpack.monitoring.collection.node.stats.timeout", + "xpack.monitoring.elasticsearch.collection.enabled", + "xpack.monitoring.enabled", "xpack.monitoring.exporters", - "cluster_alerts.management.enabled", - "wait_master.timeout", - "auth.username", - "auth.secure_password", - "connection.timeout", - "connection.read_timeout", - "proxy.base_path", - "index.name.time_format", - "cluster_alerts.management.enabled", - "cluster_alerts.management.blacklist", - "xpack.monitoring.exporters.$NAME.ssl.supported_protocols", - "xpack.monitoring.exporters.$NAME.ssl.verification_mode", + "xpack.monitoring.exporters.$NAME.ssl.certificate", + "xpack.monitoring.exporters.$NAME.ssl.certificate_authorities", "xpack.monitoring.exporters.$NAME.ssl.cipher_suites", "xpack.monitoring.exporters.$NAME.ssl.key", "xpack.monitoring.exporters.$NAME.ssl.key_passphrase", - "xpack.monitoring.exporters.$NAME.ssl.secure_key_passphrase", - "xpack.monitoring.exporters.$NAME.ssl.certificate", - "xpack.monitoring.exporters.$NAME.ssl.certificate_authorities", - "xpack.monitoring.exporters.$NAME.ssl.keystore.path", - "xpack.monitoring.exporters.$NAME.ssl.keystore.password", - "xpack.monitoring.exporters.$NAME.ssl.keystore.secure_password", "xpack.monitoring.exporters.$NAME.ssl.keystore.key_password", - "xpack.monitoring.exporters.$NAME.ssl.keystore.secure_key_password", - "xpack.monitoring.exporters.$NAME.ssl.truststore.path", - "xpack.monitoring.exporters.$NAME.ssl.truststore.password", - "xpack.monitoring.exporters.$NAME.ssl.truststore.secure_password", + "xpack.monitoring.exporters.$NAME.ssl.keystore.password", "xpack.monitoring.exporters.$NAME.ssl.keystore.path", + "xpack.monitoring.exporters.$NAME.ssl.keystore.secure_key_password", + "xpack.monitoring.exporters.$NAME.ssl.keystore.secure_password", "xpack.monitoring.exporters.$NAME.ssl.keystore.type", - "xpack.monitoring.exporters.$NAME.ssl.keystore.password", - "xpack.monitoring.exporters.$NAME.ssl.keystore.secure_password", - "xpack.monitoring.exporters.$NAME.ssl.keystore.key_password", - "xpack.monitoring.exporters.$NAME.ssl.keystore.secure_key_password", - "xpack.monitoring.exporters.$NAME.ssl.truststore.path", - "xpack.monitoring.exporters.$NAME.ssl.truststore.type", + "xpack.monitoring.exporters.$NAME.ssl.secure_key_passphrase", + "xpack.monitoring.exporters.$NAME.ssl.supported_protocols", "xpack.monitoring.exporters.$NAME.ssl.truststore.password", + "xpack.monitoring.exporters.$NAME.ssl.truststore.path", "xpack.monitoring.exporters.$NAME.ssl.truststore.secure_password", - "network.host", - "http.port", - "transport.port", - "remote_cluster.port", - "0.0.0.0", - "network.bind_host", - "network.publish_host", - "network.tcp.keep_alive", - "network.tcp.keep_idle", - "network.tcp.keep_interval", - "network.tcp.keep_count", - "network.tcp.no_delay", - "network.tcp.reuse_address", - "network.tcp.send_buffer_size", - "network.tcp.receive_buffer_size", - "http.host", - "http.bind_host", - "http.publish_host", - "http.publish_port", - "http.max_content_length", - "http.max_initial_line_length", - "http.max_header_size", - "http.compression", - "http.compression_level", - "http.cors.enabled", - "http.detailed_errors.enabled", - "http.pipelining.max_events", - "http.max_warning_header_count", - "http.max_warning_header_size", - "http.tcp.keep_alive", - "http.tcp.keep_idle", - "http.tcp.keep_interval", - "http.tcp.keep_count", - "http.tcp.no_delay", - "http.tcp.reuse_address", - "http.tcp.send_buffer_size", - "http.tcp.receive_buffer_size", - "http.client_stats.enabled", - "http.client_stats.closed_channels.max_count", - "http.client_stats.closed_channels.max_age", - "transport.host", - "transport.bind_host", - "transport.publish_host", - "transport.publish_port", - "transport.connect_timeout", - "transport.compress", - "transport.compression_scheme", - "transport.tcp.keep_alive", - "transport.tcp.keep_idle", - "transport.tcp.keep_interval", - "transport.tcp.keep_count", - "transport.tcp.no_delay", - "transport.tcp.reuse_address", - "transport.tcp.send_buffer_size", - "transport.tcp.receive_buffer_size", - "transport.ping_schedule", - "remote_cluster_server.enabled", - "remote_cluster.host", - "remote_cluster.bind_host", - "remote_cluster.publish_host", - "remote_cluster.publish_port", - "remote_cluster.tcp.keep_alive", - "remote_cluster.tcp.keep_idle", - "remote_cluster.tcp.keep_interval", - "remote_cluster.tcp.keep_count", - "remote_cluster.tcp.no_delay", - "remote_cluster.tcp.reuse_address", - "remote_cluster.tcp.send_buffer_size", - "remote_cluster.tcp.receive_buffer_size", - "org.elasticsearch.transport.InboundHandler", - "org.elasticsearch.transport.OutboundHandler", - "org.elasticsearch.common.network.ThreadWatchdog", - "network.thread.watchdog.interval", - "network.thread.watchdog.quiet_time", - "indices.queries.cache.size", - "index.queries.cache.enabled", - "vectors.indexing.use_gpu", - "cluster.remote.initial_connect_timeout", - "cluster.remote.node.attr", - "cluster.remote.signing.certificate_authorities", - "cluster.remote.signing.truststore.path", - "cluster.remote.signing.truststore.secure_password", - "cluster.remote.signing.truststore.algorithm", - "cluster.remote.signing.truststore.type", - "cluster.remote.signing.diagnose.trust", - "indices.query.bool.max_clause_count", - "search.max_buckets", - "search.aggs.only_allowed_metric_scripts", - "search.aggs.allowed_inline_metric_scripts", - "search.aggs.allowed_stored_metric_scripts", - "indices.query.bool.max_nested_depth", - "search.task_watchdog.enabled", - "search.task_watchdog.coordinator_threshold", - "search.task_watchdog.data_node_threshold", - "search.task_watchdog.interval", - "search.task_watchdog.cooldown_period", - "xpack.security.enabled", - "xpack.security.autoconfiguration.enabled", - "xpack.security.enrollment.enabled", - "xpack.security.hide_settings", - "xpack.security.fips_mode.enabled", - "xpack.security.fips_mode.required_providers", - "xpack.security.authc.password_hashing.algorithm", - "xpack.security.authc.anonymous.username", - "xpack.security.authc.anonymous.roles", - "xpack.security.authc.anonymous.authz_exception", - "xpack.security.automata.max_determinized_states", - "xpack.security.automata.cache.enabled", - "xpack.security.automata.cache.size", - "xpack.security.automata.cache.ttl", - "xpack.security.dls_fls.enabled", - "xpack.security.dls.bitset.cache.ttl", - "xpack.security.dls.bitset.cache.size", - "xpack.security.authc.token.enabled", - "xpack.security.authc.token.timeout", - "xpack.security.authc.api_key.enabled", - "xpack.security.authc.api_key.cache.ttl", - "xpack.security.authc.api_key.cache.max_keys", - "xpack.security.authc.api_key.cache.hash_algo", - "xpack.security.authc.api_key.delete.retention_period", - "xpack.security.authc.api_key.delete.interval", - "xpack.security.authc.api_key.delete.timeout", - "xpack.security.authc.api_key.hashing.algorithm", - "xpack.security.authc.realms.saml.*", - "xpack.security.authc.realms.oidc.*", - "xpack.security.authc.realms.kerberos.*", - "xpack.security.authc.realms.jwt.*", - "cache.ttl", - "cache.max_users", - "cache.hash_algo", - "authentication.enabled", - "cache.ttl", - "cache.max_users", - "cache.hash_algo", - "authentication.enabled", - "load_balance.type", - "load_balance.cache_ttl", - "user_search.base_dn", - "user_search.scope", - "user_search.filter", - "user_search.attribute", - "user_search.pool.enabled", - "user_search.pool.size", - "user_search.pool.initial_size", - "user_search.pool.health_check.enabled", - "user_search.pool.health_check.dn", - "user_search.pool.health_check.interval", - "group_search.base_dn", - "group_search.scope", - "group_search.filter", - "group_search.user_attribute", - "files.role_mapping", - "timeout.tcp_connect", - "timeout.tcp_read", - "timeout.response", - "timeout.ldap_search", - "ssl.key", - "ssl.key_passphrase", - "ssl.secure_key_passphrase", - "ssl.certificate", - "ssl.certificate_authorities", - "ssl.keystore.path", - "ssl.keystore.type", - "ssl.keystore.password", - "ssl.keystore.secure_password", - "ssl.keystore.key_password", - "ssl.keystore.secure_key_password", - "ssl.truststore.path", - "ssl.truststore.password", - "ssl.truststore.secure_password", - "ssl.truststore.type", - "ssl.verification_mode", - "ssl.supported_protocols", - "ssl.cipher_suites", - "cache.ttl", - "cache.max_users", - "cache.hash_algo", - "authentication.enabled", - "load_balance.type", - "load_balance.cache_ttl", - "files.role_mapping", - "user_search.base_dn", - "user_search.scope", - "user_search.filter", - "user_search.upn_filter", - "user_search.down_level_filter", - "user_search.pool.enabled", - "user_search.pool.size", - "user_search.pool.initial_size", - "user_search.pool.health_check.enabled", - "user_search.pool.health_check.dn", - "user_search.pool.health_check.interval", - "group_search.base_dn", - "group_search.scope", - "timeout.tcp_connect", - "timeout.tcp_read", - "timeout.response", - "timeout.ldap_search", - "ssl.certificate", - "ssl.certificate_authorities", - "ssl.key", - "ssl.key_passphrase", - "ssl.secure_key_passphrase", - "ssl.keystore.key_password", - "ssl.keystore.secure_key_password", - "ssl.keystore.password", - "ssl.secure_keystore.password", - "ssl.keystore.path", - "ssl.keystore.type", - "ssl.truststore.password", - "ssl.truststore.secure_password", - "ssl.truststore.path", - "ssl.truststore.type", - "ssl.verification_mode", - "ssl.supported_protocols", - "ssl.cipher_suites", - "cache.ttl", - "cache.max_users", - "cache.hash_algo", - "authentication.enabled", - "truststore.algorithm", - "truststore.password", - "truststore.secure_password", - "truststore.path", - "files.role_mapping", - "cache.ttl", - "cache.max_users", - "delegation.enabled", - "idp.entity_id", - "idp.metadata.path", - "idp.metadata.http.fail_on_error", - "idp.metadata.http.connect_timeout", - "idp.metadata.http.read_timeout", - "idp.metadata.http.refresh", - "idp.metadata.http.minimum_refresh", - "idp.use_single_logout", - "sp.entity_id", - "sp.acs", - "sp.logout", - "attributes.principal", - "attributes.groups", - "attributes.name", - "attributes.mail", - "attributes.dn", - "attribute_patterns.principal", - "attribute_patterns.groups", - "attribute_patterns.name", - "attribute_patterns.mail", - "attribute_patterns.dn", - "attribute_delimiters.groups", - "nameid.allow_create", - "nameid.sp_qualifier", - "signing.saml_messages", - "signing.key", - "signing.secure_key_passphrase", - "signing.certificate", - "signing.keystore.path", - "signing.keystore.type", - "signing.keystore.alias", - "signing.keystore.secure_password", - "signing.keystore.secure_key_password", - "encryption.key", - "encryption.secure_key_passphrase", - "encryption.certificate", - "encryption.keystore.path", - "encryption.keystore.type", - "encryption.keystore.alias", - "encryption.keystore.secure_password", - "encryption.keystore.secure_key_password", - "ssl.key", - "ssl.key_passphrase", - "ssl.secure_key_passphrase", - "ssl.certificate", - "ssl.certificate_authorities", - "ssl.keystore.path", - "ssl.keystore.type", - "ssl.keystore.password", - "ssl.keystore.secure_password", - "ssl.keystore.key_password", - "ssl.keystore.secure_key_password", - "ssl.truststore.path", - "ssl.truststore.type", - "ssl.truststore.password", - "ssl.truststore.secure_password", - "ssl.verification_mode", - "ssl.supported_protocols", - "ssl.cipher_suites", - "keytab.path", - "krb.debug", - "cache.ttl", - "cache.max_users", - "op.issuer", - "op.authorization_endpoint", - "op.token_endpoint", - "op.userinfo_endpoint", - "op.endsession_endpoint", - "op.jwkset_path", - "rp.client_id", - "rp.client_secret", - "rp.client_auth_method", - "rp.client_auth_jwt_signature_algorithm", - "rp.redirect_uri", - "rp.response_type", - "rp.signature_algorithm", - "rp.requested_scopes", - "rp.post_logout_redirect_uri", - "claims.principal", - "claims.groups", - "claims.name", - "claims.mail", - "claims.dn", - "claim_patterns.principal", - "claim_patterns.groups", - "claim_patterns.name", - "claim_patterns.mail", - "claim_patterns.dn", - "http.proxy.host", - "http.proxy.scheme", - "http.proxy.port", - "http.connect_timeout", - "http.connection_read_timeout", - "http.socket_timeout", - "http.max_connections", - "http.max_endpoint_connections", - "http.tcp.keep_alive", - "http.connection_pool_ttl", - "ssl.key", - "ssl.key_passphrase", - "ssl.secure_key_passphrase", - "ssl.certificate", - "ssl.certificate_authorities", - "ssl.keystore.path", - "ssl.keystore.type", - "ssl.keystore.password", - "ssl.keystore.secure_password", - "ssl.keystore.key_password", - "ssl.keystore.secure_key_password", - "ssl.truststore.path", - "ssl.truststore.type", - "ssl.truststore.password", - "ssl.truststore.secure_password", - "ssl.verification_mode", - "ssl.supported_protocols", - "ssl.cipher_suites", - "fallback_claims.sub", - "fallback_claims.aud", - "claims.dn", - "claim_patterns.dn", - "claims.groups", - "claim_patterns.group", - "claims.mail", - "claim_patterns.mail", - "claims.name", - "claim_patterns.name", - "claims.principal", - "claim_patterns.principal", - "client_authentication.type", - "client_authentication.shared_secret", - "client_authentication.rotation_grace_period", - "http.proxy.host", - "http.proxy.scheme", - "http.proxy.port", - "http.connect_timeout", - "http.connection_read_timeout", - "http.socket_timeout", - "http.max_connections", - "http.max_endpoint_connections", - "jwt.cache.size", - "jwt.cache.ttl", - "pkc_jwkset_reload.enabled", - "pkc_jwkset_reload.file_interval", - "pkc_jwkset_reload.url_interval_min", - "pkc_jwkset_reload.url_interval_max", - "ssl.key", - "ssl.key_passphrase", - "ssl.secure_key_passphrase", - "ssl.certificate", - "ssl.certificate_authorities", - "ssl.keystore.path", - "ssl.keystore.type", - "ssl.keystore.password", - "ssl.keystore.secure_password", - "ssl.keystore.key_password", - "ssl.keystore.secure_key_password", - "ssl.truststore.path", - "ssl.truststore.type", - "ssl.truststore.password", - "ssl.truststore.secure_password", - "ssl.verification_mode", - "ssl.supported_protocols", - "ssl.cipher_suites", - "xpack.security.ssl.diagnose.trust", - "xpack.security.http.ssl.enabled", - "xpack.security.http.ssl.supported_protocols", - "xpack.security.http.ssl.client_authentication", - "xpack.security.http.ssl.verification_mode", - "xpack.security.http.ssl.cipher_suites", - "xpack.security.http.ssl.key", - "xpack.security.http.ssl.key_passphrase", - "xpack.security.http.ssl.secure_key_passphrase", - "xpack.security.http.ssl.certificate", - "xpack.security.http.ssl.certificate_authorities", - "xpack.security.http.ssl.keystore.path", - "xpack.security.http.ssl.keystore.password", - "xpack.security.http.ssl.keystore.secure_password", - "xpack.security.http.ssl.keystore.key_password", - "xpack.security.http.ssl.keystore.secure_key_password", - "xpack.security.http.ssl.truststore.path", - "xpack.security.http.ssl.truststore.password", - "xpack.security.http.ssl.truststore.secure_password", - "xpack.security.http.ssl.keystore.path", - "xpack.security.http.ssl.keystore.type", - "xpack.security.http.ssl.keystore.password", - "xpack.security.http.ssl.keystore.secure_password", - "xpack.security.http.ssl.keystore.key_password", - "xpack.security.http.ssl.keystore.secure_key_password", - "xpack.security.http.ssl.truststore.path", - "xpack.security.http.ssl.truststore.type", - "xpack.security.http.ssl.truststore.password", - "xpack.security.http.ssl.truststore.secure_password", - "xpack.security.transport.ssl.enabled", - "xpack.security.transport.ssl.supported_protocols", - "xpack.security.transport.ssl.client_authentication", - "xpack.security.transport.ssl.verification_mode", - "xpack.security.transport.ssl.cipher_suites", - "xpack.security.transport.ssl.trust_restrictions.x509_fields", - "xpack.security.transport.ssl.handshake_timeout", - "xpack.security.transport.ssl.key", - "xpack.security.transport.ssl.key_passphrase", - "xpack.security.transport.ssl.secure_key_passphrase", - "xpack.security.transport.ssl.certificate", - "xpack.security.transport.ssl.certificate_authorities", - "xpack.security.loginAssistanceMessage", - "xpack.security.transport.ssl.keystore.path", - "xpack.security.transport.ssl.keystore.password", - "xpack.security.transport.ssl.keystore.secure_password", - "xpack.security.transport.ssl.keystore.key_password", - "xpack.security.transport.ssl.keystore.secure_key_password", - "xpack.security.transport.ssl.truststore.path", - "xpack.security.transport.ssl.truststore.password", - "xpack.security.transport.ssl.truststore.secure_password", - "xpack.security.transport.ssl.keystore.path", - "xpack.security.transport.ssl.keystore.type", - "xpack.security.transport.ssl.keystore.password", - "xpack.security.transport.ssl.keystore.secure_password", - "xpack.security.transport.ssl.keystore.key_password", - "xpack.security.transport.ssl.keystore.secure_key_password", - "xpack.security.transport.ssl.truststore.path", - "xpack.security.transport.ssl.truststore.type", - "xpack.security.transport.ssl.truststore.password", - "xpack.security.transport.ssl.truststore.secure_password", - "xpack.security.remote_cluster_server.ssl.enabled", - "xpack.security.remote_cluster_server.ssl.supported_protocols", - "xpack.security.remote_cluster_server.ssl.client_authentication", - "xpack.security.remote_cluster_server.ssl.verification_mode", - "xpack.security.remote_cluster_server.ssl.cipher_suites", - "xpack.security.remote_cluster_server.ssl.handshake_timeout", - "xpack.security.remote_cluster_server.ssl.key", - "xpack.security.remote_cluster_server.ssl.secure_key_passphrase", - "xpack.security.remote_cluster_server.ssl.certificate", - "xpack.security.remote_cluster_server.ssl.certificate_authorities", - "xpack.security.remote_cluster_server.ssl.keystore.path", - "xpack.security.remote_cluster_server.ssl.keystore.secure_password", - "xpack.security.remote_cluster_server.ssl.keystore.secure_key_password", - "xpack.security.remote_cluster_server.ssl.truststore.path", - "xpack.security.remote_cluster_server.ssl.truststore.secure_password", - "xpack.security.remote_cluster_server.ssl.keystore.path", - "xpack.security.remote_cluster_server.ssl.keystore.type", - "xpack.security.remote_cluster_server.ssl.keystore.secure_password", - "xpack.security.remote_cluster_server.ssl.keystore.secure_key_password", - "xpack.security.remote_cluster_server.ssl.truststore.path", - "xpack.security.remote_cluster_server.ssl.truststore.type", - "xpack.security.remote_cluster_server.ssl.truststore.secure_password", - "xpack.security.remote_cluster_client.ssl.enabled", - "xpack.security.remote_cluster_client.ssl.supported_protocols", - "xpack.security.remote_cluster_client.ssl.verification_mode", - "xpack.security.remote_cluster_client.ssl.cipher_suites", - "xpack.security.remote_cluster_client.ssl.handshake_timeout", - "xpack.security.remote_cluster_client.ssl.key", - "xpack.security.remote_cluster_client.ssl.secure_key_passphrase", - "xpack.security.remote_cluster_client.ssl.certificate", - "xpack.security.remote_cluster_client.ssl.certificate_authorities", - "xpack.security.remote_cluster_client.ssl.keystore.path", - "xpack.security.remote_cluster_client.ssl.keystore.secure_password", - "xpack.security.remote_cluster_client.ssl.keystore.secure_key_password", - "xpack.security.remote_cluster_client.ssl.truststore.path", - "xpack.security.remote_cluster_client.ssl.truststore.secure_password", - "xpack.security.remote_cluster_client.ssl.keystore.path", - "xpack.security.remote_cluster_client.ssl.keystore.type", - "xpack.security.remote_cluster_client.ssl.keystore.secure_password", - "xpack.security.remote_cluster_client.ssl.keystore.secure_key_password", - "xpack.security.remote_cluster_client.ssl.truststore.path", - "xpack.security.remote_cluster_client.ssl.truststore.type", - "xpack.security.remote_cluster_client.ssl.truststore.secure_password", - "xpack.security.transport.filter.allow", - "xpack.security.transport.filter.deny", - "xpack.security.http.filter.allow", - "xpack.security.http.filter.deny", - "transport.profiles.$PROFILE.xpack.security.filter.allow", - "transport.profiles.$PROFILE.xpack.security.filter.deny", - "xpack.security.remote_cluster.filter.allow", - "xpack.security.remote_cluster.filter.deny", - "indices.requests.cache.size", - "indices.requests.cache.expire", - "snapshot.max_concurrent_operations", - "repositories.default_repository", - "slm.history_index_enabled", - "slm.retention_schedule", - "slm.retention_duration", - "slm.health.failed_snapshot_warn_threshold", - "node.roles: [ transform ]", - "xpack.transform.enabled", - "xpack.transform.num_transform_failure_retries", - "xpack.watcher.enabled", - "xpack.watcher.encrypt_sensitive_data", - "xpack.watcher.encryption_key", - "xpack.watcher.max.history.record.size", - "xpack.watcher.trigger.schedule.engine", - "xpack.watcher.history.cleaner_service.enabled", - "xpack.http.proxy.host", - "xpack.http.proxy.port", - "xpack.http.proxy.scheme", - "xpack.http.default_connection_timeout", - "xpack.http.default_read_timeout", - "xpack.http.tcp.keep_alive", - "xpack.http.connection_pool_ttl", - "xpack.http.max_response_size", - "xpack.http.whitelist", - "xpack.http.ssl.supported_protocols", - "xpack.http.ssl.verification_mode", - "xpack.http.ssl.cipher_suites", - "xpack.http.ssl.key", - "xpack.http.ssl.secure_key_passphrase", - "xpack.http.ssl.certificate", - "xpack.http.ssl.certificate_authorities", - "xpack.http.ssl.keystore.path", - "xpack.http.ssl.keystore.secure_password", - "xpack.http.ssl.keystore.secure_key_password", - "xpack.http.ssl.truststore.path", - "xpack.http.ssl.truststore.secure_password", - "xpack.http.ssl.keystore.path", - "xpack.http.ssl.keystore.type", - "xpack.http.ssl.keystore.secure_password", - "xpack.http.ssl.keystore.secure_key_password", - "xpack.http.ssl.truststore.path", - "xpack.http.ssl.truststore.type", - "xpack.http.ssl.truststore.secure_password", - "xpack.notification.email.default_account", - "xpack.notification.email.recipient_allowlist", + "xpack.monitoring.exporters.$NAME.ssl.truststore.type", + "xpack.monitoring.exporters.$NAME.ssl.verification_mode", + "xpack.monitoring.history.duration", "xpack.notification.email.account", "xpack.notification.email.account.domain_allowlist", - "email_defaults.*", - "smtp.auth", - "smtp.host", - "smtp.port", - "smtp.user", - "smtp.secure_password", - "smtp.starttls.enable", - "smtp.starttls.required", - "smtp.ssl.trust", - "smtp.timeout", - "smtp.connection_timeout", - "smtp.write_timeout", - "smtp.local_address", - "smtp.local_port", - "smtp.send_partial", - "smtp.wait_on_quit", + "xpack.notification.email.default_account", "xpack.notification.email.html.sanitization.allow", "xpack.notification.email.html.sanitization.disallow", "xpack.notification.email.html.sanitization.enabled", - "xpack.notification.email.ssl.supported_protocols", - "xpack.notification.email.ssl.verification_mode", - "xpack.notification.email.ssl.cipher_suites", - "xpack.notification.email.ssl.key", - "xpack.notification.email.ssl.secure_key_passphrase", + "xpack.notification.email.recipient_allowlist", "xpack.notification.email.ssl.certificate", "xpack.notification.email.ssl.certificate_authorities", + "xpack.notification.email.ssl.cipher_suites", + "xpack.notification.email.ssl.key", "xpack.notification.email.ssl.keystore.path", - "xpack.notification.email.ssl.keystore.secure_password", "xpack.notification.email.ssl.keystore.secure_key_password", - "xpack.notification.email.ssl.truststore.path", - "xpack.notification.email.ssl.truststore.secure_password", - "xpack.notification.email.ssl.keystore.path", + "xpack.notification.email.ssl.keystore.secure_password", "xpack.notification.email.ssl.keystore.type", - "xpack.notification.email.ssl.keystore.secure_password", - "xpack.notification.email.ssl.keystore.secure_key_password", + "xpack.notification.email.ssl.secure_key_passphrase", + "xpack.notification.email.ssl.supported_protocols", "xpack.notification.email.ssl.truststore.path", - "xpack.notification.email.ssl.truststore.type", "xpack.notification.email.ssl.truststore.secure_password", - "xpack.notification.slack", - "xpack.notification.slack.default_account", - "xpack.notification.slack.account", - "xpack.notification.jira.default_account", + "xpack.notification.email.ssl.truststore.type", + "xpack.notification.email.ssl.verification_mode", "xpack.notification.jira.account", + "xpack.notification.jira.default_account", "xpack.notification.pagerduty", - "xpack.notification.pagerduty.default_account", "xpack.notification.pagerduty.account", + "xpack.notification.pagerduty.default_account", + "xpack.notification.slack", + "xpack.notification.slack.account", + "xpack.notification.slack.default_account", "xpack.notification.webhook.additional_token_enabled", + "xpack.otel_data.histogram_field_type", + "xpack.otel_data.registry.enabled", + "xpack.profiling.enabled", + "xpack.profiling.templates.enabled", + "xpack.security.audit.enabled", + "xpack.security.audit.logfile.emit_node_host_address", + "xpack.security.audit.logfile.emit_node_host_name", + "xpack.security.audit.logfile.emit_node_id", + "xpack.security.audit.logfile.emit_node_name", + "xpack.security.audit.logfile.events.emit_request_body", + "xpack.security.audit.logfile.events.exclude", + "xpack.security.audit.logfile.events.include", + "xpack.security.authc.anonymous.authz_exception", + "xpack.security.authc.anonymous.roles", + "xpack.security.authc.anonymous.username", + "xpack.security.authc.api_key.cache.hash_algo", + "xpack.security.authc.api_key.cache.max_keys", + "xpack.security.authc.api_key.cache.ttl", + "xpack.security.authc.api_key.delete.interval", + "xpack.security.authc.api_key.delete.retention_period", + "xpack.security.authc.api_key.delete.timeout", + "xpack.security.authc.api_key.enabled", + "xpack.security.authc.api_key.hashing.algorithm", + "xpack.security.authc.password_hashing.algorithm", + "xpack.security.authc.realms.jwt.*", + "xpack.security.authc.realms.kerberos.*", + "xpack.security.authc.realms.oidc.*", + "xpack.security.authc.realms.saml.*", + "xpack.security.authc.token.enabled", + "xpack.security.authc.token.timeout", + "xpack.security.autoconfiguration.enabled", + "xpack.security.automata.cache.enabled", + "xpack.security.automata.cache.size", + "xpack.security.automata.cache.ttl", + "xpack.security.automata.max_determinized_states", + "xpack.security.dls.bitset.cache.size", + "xpack.security.dls.bitset.cache.ttl", + "xpack.security.dls_fls.enabled", + "xpack.security.enabled", + "xpack.security.enrollment.enabled", + "xpack.security.fips_mode.enabled", + "xpack.security.fips_mode.required_providers", + "xpack.security.hide_settings", + "xpack.security.http.filter.allow", + "xpack.security.http.filter.deny", + "xpack.security.http.ssl.certificate", + "xpack.security.http.ssl.certificate_authorities", + "xpack.security.http.ssl.cipher_suites", + "xpack.security.http.ssl.client_authentication", + "xpack.security.http.ssl.enabled", + "xpack.security.http.ssl.key", + "xpack.security.http.ssl.key_passphrase", + "xpack.security.http.ssl.keystore.key_password", + "xpack.security.http.ssl.keystore.password", + "xpack.security.http.ssl.keystore.path", + "xpack.security.http.ssl.keystore.secure_key_password", + "xpack.security.http.ssl.keystore.secure_password", + "xpack.security.http.ssl.keystore.type", + "xpack.security.http.ssl.secure_key_passphrase", + "xpack.security.http.ssl.supported_protocols", + "xpack.security.http.ssl.truststore.password", + "xpack.security.http.ssl.truststore.path", + "xpack.security.http.ssl.truststore.secure_password", + "xpack.security.http.ssl.truststore.type", + "xpack.security.http.ssl.verification_mode", + "xpack.security.loginAssistanceMessage", + "xpack.security.remote_cluster_client.ssl.certificate", + "xpack.security.remote_cluster_client.ssl.certificate_authorities", + "xpack.security.remote_cluster_client.ssl.cipher_suites", + "xpack.security.remote_cluster_client.ssl.enabled", + "xpack.security.remote_cluster_client.ssl.handshake_timeout", + "xpack.security.remote_cluster_client.ssl.key", + "xpack.security.remote_cluster_client.ssl.keystore.path", + "xpack.security.remote_cluster_client.ssl.keystore.secure_key_password", + "xpack.security.remote_cluster_client.ssl.keystore.secure_password", + "xpack.security.remote_cluster_client.ssl.keystore.type", + "xpack.security.remote_cluster_client.ssl.secure_key_passphrase", + "xpack.security.remote_cluster_client.ssl.supported_protocols", + "xpack.security.remote_cluster_client.ssl.truststore.path", + "xpack.security.remote_cluster_client.ssl.truststore.secure_password", + "xpack.security.remote_cluster_client.ssl.truststore.type", + "xpack.security.remote_cluster_client.ssl.verification_mode", + "xpack.security.remote_cluster.filter.allow", + "xpack.security.remote_cluster.filter.deny", + "xpack.security.remote_cluster_server.ssl.certificate", + "xpack.security.remote_cluster_server.ssl.certificate_authorities", + "xpack.security.remote_cluster_server.ssl.cipher_suites", + "xpack.security.remote_cluster_server.ssl.client_authentication", + "xpack.security.remote_cluster_server.ssl.enabled", + "xpack.security.remote_cluster_server.ssl.handshake_timeout", + "xpack.security.remote_cluster_server.ssl.key", + "xpack.security.remote_cluster_server.ssl.keystore.path", + "xpack.security.remote_cluster_server.ssl.keystore.secure_key_password", + "xpack.security.remote_cluster_server.ssl.keystore.secure_password", + "xpack.security.remote_cluster_server.ssl.keystore.type", + "xpack.security.remote_cluster_server.ssl.secure_key_passphrase", + "xpack.security.remote_cluster_server.ssl.supported_protocols", + "xpack.security.remote_cluster_server.ssl.truststore.path", + "xpack.security.remote_cluster_server.ssl.truststore.secure_password", + "xpack.security.remote_cluster_server.ssl.truststore.type", + "xpack.security.remote_cluster_server.ssl.verification_mode", + "xpack.security.ssl.diagnose.trust", + "xpack.security.transport.filter.allow", + "xpack.security.transport.filter.deny", + "xpack.security.transport.ssl.certificate", + "xpack.security.transport.ssl.certificate_authorities", + "xpack.security.transport.ssl.cipher_suites", + "xpack.security.transport.ssl.client_authentication", + "xpack.security.transport.ssl.enabled", + "xpack.security.transport.ssl.handshake_timeout", + "xpack.security.transport.ssl.key", + "xpack.security.transport.ssl.key_passphrase", + "xpack.security.transport.ssl.keystore.key_password", + "xpack.security.transport.ssl.keystore.password", + "xpack.security.transport.ssl.keystore.path", + "xpack.security.transport.ssl.keystore.secure_key_password", + "xpack.security.transport.ssl.keystore.secure_password", + "xpack.security.transport.ssl.keystore.type", + "xpack.security.transport.ssl.secure_key_passphrase", + "xpack.security.transport.ssl.supported_protocols", + "xpack.security.transport.ssl.trust_restrictions.x509_fields", + "xpack.security.transport.ssl.truststore.password", + "xpack.security.transport.ssl.truststore.path", + "xpack.security.transport.ssl.truststore.secure_password", + "xpack.security.transport.ssl.truststore.type", + "xpack.security.transport.ssl.verification_mode", + "xpack.transform.enabled", + "xpack.transform.num_transform_failure_retries", + "xpack.watcher.enabled", + "xpack.watcher.encryption_key", + "xpack.watcher.encrypt_sensitive_data", + "xpack.watcher.history.cleaner_service.enabled", + "xpack.watcher.max.history.record.size", + "xpack.watcher.trigger.schedule.engine", } diff --git a/pkg/es/cluster_util.go b/pkg/es/cluster_util.go index e72115e..8f7b9ae 100644 --- a/pkg/es/cluster_util.go +++ b/pkg/es/cluster_util.go @@ -37,6 +37,7 @@ func checkClusterIsLeader(conf *cfg.Config, leader string) bool { Do(context.Background()) if err != nil { fmt.Printf("failed to get ccr stats from %s: %s", leader, esErrorString(err)) + return false } @@ -47,6 +48,7 @@ func checkClusterIsLeader(conf *cfg.Config, leader string) bool { if stats.AutoFollowStats.AutoFollowedClusters[0].ClusterName != "" { fmt.Println("leader/follower attribution is invalid, reverse cluster attribution and retry") + return false } @@ -58,14 +60,15 @@ func checkClusterStatus(conf *cfg.Config, leader, follower string) bool { status := map[string]*health.Response{} for _, cluster := range []string{leader, follower} { - st, err := conf.Clusters[leader].ES().Cluster.Health(). + clusterHealth, err := conf.Clusters[leader].ES().Cluster.Health(). Do(context.Background()) if err != nil { fmt.Printf("failed to get health from %s: %s", cluster, esErrorString(err)) + return false } - status[cluster] = st + status[cluster] = clusterHealth } table := printer.NewTable(conf, 3, 6) @@ -98,6 +101,7 @@ func checkClusterStatus(conf *cfg.Config, leader, follower string) bool { if err := table.Print(); err != nil { fmt.Println(err) + return false } @@ -120,6 +124,7 @@ func findIlmErrors(conf *cfg.Config, leader, follower string) bool { Do(context.Background()) if err != nil { fmt.Printf("failed to get ilm status from %s: %s", cluster, esErrorString(err)) + return false } @@ -158,6 +163,7 @@ func findIlmErrors(conf *cfg.Config, leader, follower string) bool { if err := table.Print(); err != nil { fmt.Println(err) + return false } } @@ -174,6 +180,7 @@ func findIndicesOnlyOnLeader(conf *cfg.Config, indices ClusterIndices, leader, f } indexOnlyOnLeader := map[string]*types.IndicesRecord{} + for name, index := range indices[leader] { if exclude.MatchString(name) { continue @@ -195,9 +202,11 @@ func findIndicesOnlyOnLeader(conf *cfg.Config, indices ClusterIndices, leader, f } isWritable := false + for _, alias := range res[name].Aliases { if *alias.IsWriteIndex { isWritable = true + break } } @@ -227,8 +236,10 @@ func findIndicesOnlyOnLeader(conf *cfg.Config, indices ClusterIndices, leader, f } table.Sort() + if err := table.Print(); err != nil { fmt.Println(err) + return false } @@ -275,8 +286,10 @@ func findOrphanedIndices(conf *cfg.Config, indices ClusterIndices, leader, follo } table.Sort() + if err := table.Print(); err != nil { fmt.Println(err) + return false } @@ -309,8 +322,10 @@ func findFailedFollowerIndices(conf *cfg.Config, indices ClusterIndices, followe } table.Sort() + if err := table.Print(); err != nil { fmt.Println(err) + return false } diff --git a/pkg/es/datastream.go b/pkg/es/datastream.go index 9cd652b..42f8339 100644 --- a/pkg/es/datastream.go +++ b/pkg/es/datastream.go @@ -65,21 +65,22 @@ func DatastreamList(conf *cfg.Config) error { table := printer.NewTable(conf, 7, size) table.Addheaders("name", "ilm policy", "hidden", "system", "replicated", "generation", "timestamp field") - for idx, ds := range list { - name := printer.Colorize(conf, ds.Status.String(), ds.Name) + for idx, datastream := range list { + name := printer.Colorize(conf, datastream.Status.String(), datastream.Name) + policy := "" - if ds.IlmPolicy != nil { - policy = *ds.IlmPolicy + if datastream.IlmPolicy != nil { + policy = *datastream.IlmPolicy } table.Entries[idx] = []any{ name, policy, - ds.Hidden, - *ds.System, - *ds.Replicated, - ds.Generation, - ds.TimestampField.Name, + datastream.Hidden, + *datastream.System, + *datastream.Replicated, + datastream.Generation, + datastream.TimestampField.Name, } if idx == size-1 { @@ -88,6 +89,7 @@ func DatastreamList(conf *cfg.Config) error { } table.Sort() + if err := table.Print(); err != nil { return err } @@ -147,22 +149,23 @@ func DatastreamShow(conf *cfg.Config, dsname string) error { table := printer.NewTable(conf, 11, 2) table.Addheaders("data stream property", "value") - ds := res.DataStreams[0] + datastream := res.DataStreams[0] + + name := printer.Colorize(conf, datastream.Status.String(), datastream.Name) - name := printer.Colorize(conf, ds.Status.String(), ds.Name) policy := "" - if ds.IlmPolicy != nil { - policy = *ds.IlmPolicy + if datastream.IlmPolicy != nil { + policy = *datastream.IlmPolicy } table.Entries = [][]any{ {"name", name}, {"ilm policy", policy}, - {"hidden", ds.Hidden}, - {"system", *ds.System}, - {"replicated", *ds.Replicated}, - {"generation", ds.Generation}, - {"timestamp field", ds.TimestampField.Name}, + {"hidden", datastream.Hidden}, + {"system", *datastream.System}, + {"replicated", *datastream.Replicated}, + {"generation", datastream.Generation}, + {"timestamp field", datastream.TimestampField.Name}, {"backing indices", stats.BackingIndices}, {"size", printer.Bytes(stats.TotalStoreSizeBytes)}, {"shards-failed", stats.Shards_.Failed}, @@ -173,13 +176,13 @@ func DatastreamShow(conf *cfg.Config, dsname string) error { return err } - table = printer.NewTable(conf, 5, len(ds.Indices)) + table = printer.NewTable(conf, 5, len(datastream.Indices)) table.Addheaders("backing index name", "uuid", "prefer ilm", "ilm policy", "managed by") - for idx, index := range ds.Indices { + for idx, index := range datastream.Indices { policy := "" - if ds.IlmPolicy != nil { - policy = *ds.IlmPolicy + if datastream.IlmPolicy != nil { + policy = *datastream.IlmPolicy } table.Entries[idx] = []any{ @@ -190,12 +193,13 @@ func DatastreamShow(conf *cfg.Config, dsname string) error { index.ManagedBy.Name, } - if idx < len(ds.Indices) { + if idx < len(datastream.Indices) { fmt.Println() } } table.Sort() + if err := table.Print(); err != nil { return err } @@ -206,7 +210,6 @@ func DatastreamShow(conf *cfg.Config, dsname string) error { func DatastreamCreate(conf *cfg.Config, dsname string) error { _, err := conf.DefaultCluster.ES().Indices.CreateDataStream(dsname). Do(context.Background()) - if err != nil { return fmt.Errorf("failed to create datastream: %w", esErrorString(err)) } @@ -217,7 +220,6 @@ func DatastreamCreate(conf *cfg.Config, dsname string) error { func DatastreamDelete(conf *cfg.Config, dsname string) error { _, err := conf.DefaultCluster.ES().Indices.DeleteDataStream(dsname). Do(context.Background()) - if err != nil { return fmt.Errorf("failed to delete datastream: %w", esErrorString(err)) } @@ -227,7 +229,6 @@ func DatastreamDelete(conf *cfg.Config, dsname string) error { func DatastreamRollover(conf *cfg.Config, ds string) error { res, err := RolloverAlias(conf, ds) - if err != nil { return fmt.Errorf("failed to rollover data stream: %w", esErrorString(err)) } diff --git a/pkg/es/doc.go b/pkg/es/doc.go index e001f67..30e9123 100644 --- a/pkg/es/doc.go +++ b/pkg/es/doc.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "math/rand/v2" + "strconv" "strings" "codeberg.org/scip/esctl/pkg/cfg" @@ -45,7 +46,7 @@ func DocAdd(conf *cfg.Config, jsondoc string) error { return fmt.Errorf("supplied document was not valid JSON: %w", err) } - now := fmt.Sprintf("%d", rand.Int64()) + now := strconv.FormatInt(rand.Int64(), 10) res, err := conf.DefaultCluster.ES().Create(conf.Index, now). Document(data). diff --git a/pkg/es/duration.go b/pkg/es/duration.go index 5922fb6..88edc65 100644 --- a/pkg/es/duration.go +++ b/pkg/es/duration.go @@ -45,7 +45,6 @@ Convert a duration into an integer. Valid time units are "s", "m", "h" and via https://codeberg.org/scip/tablizer/src/branch/main/lib/sort.go#L113 */ func parseDuration(duration string) time.Duration { - seconds := 0 for _, match := range matchDuration.FindAllStringSubmatch(duration, -1) { @@ -111,12 +110,14 @@ func formatDuration(val time.Duration) string { if esd.minutes == 0 { return fmt.Sprintf("%dh", esd.hours) } + return fmt.Sprintf("%dh:%dm", esd.hours, int64(esd.minutes)) default: if esd.minutes == 0 && hoursLeft == 0 { return fmt.Sprintf("%dd", int64(days)) } + return fmt.Sprintf("%dd:%dh:%dm", int64(days), int64(hoursLeft), diff --git a/pkg/es/errors.go b/pkg/es/errors.go index a810493..703827c 100644 --- a/pkg/es/errors.go +++ b/pkg/es/errors.go @@ -27,22 +27,20 @@ import ( func esErrorString(err error) error { msg := err.Error() - switch e := err.(type) { + //nolint:gocritic + switch errVal := err.(type) { case *types.ElasticsearchError: var causes strings.Builder - for _, cause := range e.ErrorCause.RootCause { - // FIXME: re-activate linter here, see https://github.com/golangci/golangci-lint/issues/6662 - //nolint:staticcheck - causes.WriteString(fmt.Sprintf("%s\n", *cause.Reason)) + for _, cause := range errVal.ErrorCause.RootCause { + fmt.Fprintf(&causes, "%s\n", *cause.Reason) } - if e.ErrorCause.Reason != nil { - msg = *e.ErrorCause.Reason + ": " + causes.String() + if errVal.ErrorCause.Reason != nil { + msg = *errVal.ErrorCause.Reason + ": " + causes.String() } else { - msg = fmt.Sprintf("http status %d: ", e.Status) + msg = fmt.Sprintf("http status %d: ", errVal.Status) } - } return errors.New(msg) diff --git a/pkg/es/ilm.go b/pkg/es/ilm.go index 8a65994..bac1a53 100644 --- a/pkg/es/ilm.go +++ b/pkg/es/ilm.go @@ -102,6 +102,7 @@ func IlmList(conf *cfg.Config, pattern string) error { } table.Sort() + return table.Print() } @@ -166,6 +167,7 @@ func IlmShowTree(conf *cfg.Config, ilm types.IlmPolicy) error { } indent += " " + continue } @@ -260,7 +262,7 @@ func ilmPhaseString(phase *types.Phase, short bool) string { } if phase.Actions.SearchableSnapshot != nil { - out = append(out, fmt.Sprintf("searchable-snapshot:%s", phase.Actions.SearchableSnapshot.SnapshotRepository)) + out = append(out, "searchable-snapshot:"+phase.Actions.SearchableSnapshot.SnapshotRepository) } return strings.Join(out, ",") @@ -286,6 +288,7 @@ func IlmExplain(conf *cfg.Config, index string) error { table.Addheaders("ilm status field", "value") info := "" + err = json.Unmarshal(ilm.StepInfo["reason"], &info) if err != nil { return fmt.Errorf("failed to unmarshal step info: %w", err) @@ -345,7 +348,9 @@ func IlmCreate(conf *cfg.Config, policyname string) error { if cfg.HaveHot() { hot := types.Phase{} + var actions types.IlmActionsVariant = esdsl.NewIlmActions() + rollover := &types.RolloverAction{} haveroll := false @@ -383,15 +388,14 @@ func IlmCreate(conf *cfg.Config, policyname string) error { hot.Actions = actions.IlmActionsCaster() phases.PhasesCaster().Hot = &hot - } else { - if policy != nil { - // update - phases.PhasesCaster().Hot = policy.Phases.Hot - } + } else if policy != nil { + // update + phases.PhasesCaster().Hot = policy.Phases.Hot } if cfg.HaveWarm() { warm := types.Phase{} + var actions types.IlmActionsVariant = esdsl.NewIlmActions() if policy != nil { @@ -417,15 +421,14 @@ func IlmCreate(conf *cfg.Config, policyname string) error { warm.Actions = actions.IlmActionsCaster() phases.PhasesCaster().Warm = &warm - } else { - if policy != nil && policy.Phases.Warm != nil { - // update - phases.PhasesCaster().Warm = policy.Phases.Warm - } + } else if policy != nil && policy.Phases.Warm != nil { + // update + phases.PhasesCaster().Warm = policy.Phases.Warm } if cfg.HaveCold() { cold := types.Phase{} + var actions types.IlmActionsVariant = esdsl.NewIlmActions() if policy != nil { @@ -452,15 +455,14 @@ func IlmCreate(conf *cfg.Config, policyname string) error { cold.Actions = actions.IlmActionsCaster() phases.PhasesCaster().Cold = &cold - } else { - if policy != nil && policy.Phases.Cold != nil { - // update - phases.PhasesCaster().Cold = policy.Phases.Cold - } + } else if policy != nil && policy.Phases.Cold != nil { + // update + phases.PhasesCaster().Cold = policy.Phases.Cold } if cfg.HaveFrozen() { froze := types.Phase{} + var actions types.IlmActionsVariant = esdsl.NewIlmActions() if policy != nil { @@ -479,16 +481,15 @@ func IlmCreate(conf *cfg.Config, policyname string) error { froze.Actions = actions.IlmActionsCaster() phases.PhasesCaster().Frozen = &froze - } else { - if policy != nil && policy.Phases.Frozen != nil { - // update - phases.PhasesCaster().Frozen = policy.Phases.Frozen - } + } else if policy != nil && policy.Phases.Frozen != nil { + // update + phases.PhasesCaster().Frozen = policy.Phases.Frozen } if cfg.HaveDelete() { del := types.Phase{} delete := types.DeleteAction{} + var actions types.IlmActionsVariant = esdsl.NewIlmActions() if policy != nil { @@ -507,11 +508,9 @@ func IlmCreate(conf *cfg.Config, policyname string) error { actions.IlmActionsCaster().Delete = &delete del.Actions = actions.IlmActionsCaster() phases.PhasesCaster().Delete = &del - } else { - if policy != nil && policy.Phases.Delete != nil { - // update - phases.PhasesCaster().Delete = policy.Phases.Delete - } + } else if policy != nil && policy.Phases.Delete != nil { + // update + phases.PhasesCaster().Delete = policy.Phases.Delete } put := &putlifecycle.Request{} diff --git a/pkg/es/ilm_forecast.go b/pkg/es/ilm_forecast.go index aaadd2f..ee659e1 100644 --- a/pkg/es/ilm_forecast.go +++ b/pkg/es/ilm_forecast.go @@ -138,6 +138,7 @@ func IlmForecastList(conf *cfg.Config, filter string) error { } table.Sort() + return table.Print() } @@ -154,6 +155,7 @@ func IlmForecastShow(conf *cfg.Config) error { } within := parseDuration(conf.Ilm.Within) + var toBeFreed int64 = 0 for _, phase := range phaseData { @@ -192,24 +194,26 @@ func getIlmPhaseData(conf *cfg.Config) ([]PhaseData, error) { wg.Wait() - var ilmdetails *explainlifecycle.Response - var indicesres *indices.Response - var ilmpolicies getlifecycle.Response + var ( + ilmdetails *explainlifecycle.Response + indicesres *indices.Response + ilmpolicies getlifecycle.Response + ) for range 3 { - r := <-responses + res := <-responses - if r.error != nil { - return nil, r.error + if res.error != nil { + return nil, res.error } - switch r.which { + switch res.which { case ResponseExplain: - ilmdetails = r.explainlifecycle + ilmdetails = res.explainlifecycle case ResponseIndices: - indicesres = r.indicesbytes + indicesres = res.indicesbytes case ResponseLifecycle: - ilmpolicies = *r.lifecycle + ilmpolicies = *res.lifecycle } } diff --git a/pkg/es/index.go b/pkg/es/index.go index d1a8f92..7929a7e 100644 --- a/pkg/es/index.go +++ b/pkg/es/index.go @@ -37,7 +37,6 @@ import ( func IndexNames(conf *cfg.Config) ([]string, error) { res, err := conf.DefaultCluster.ES().Cat.Indices(). Do(context.Background()) - if err != nil { return nil, fmt.Errorf("failed to get indicies: %w", esErrorString(err)) } @@ -126,6 +125,7 @@ func IndexList(conf *cfg.Config) error { } table.Sort() + return table.Print() } @@ -140,6 +140,7 @@ func IndexShow(conf *cfg.Config, indexpattern string) error { for name, index := range res { fields := make([]string, len(index.Mappings.Properties)) + idx := 0 for field := range index.Mappings.Properties { fields[idx] = field @@ -195,6 +196,7 @@ func IndexCreate(conf *cfg.Config, index string, mappings []string) error { if conf.Shards > 0 { settings.NumberOfShards(strconv.Itoa(conf.Shards)) } + if conf.Replicas > 0 { settings.NumberOfReplicas(strconv.Itoa(conf.Replicas)) } @@ -232,7 +234,6 @@ func IndexCreate(conf *cfg.Config, index string, mappings []string) error { _, err := create.Settings(settings). Do(context.Background()) - if err != nil { return fmt.Errorf("failed to create index: %w", esErrorString(err)) } @@ -253,7 +254,6 @@ func IndexDelete(conf *cfg.Config, index string) error { func IndexClose(conf *cfg.Config, index string) error { _, err := conf.DefaultCluster.ES().Indices.Close(index). Do(context.Background()) - if err != nil { return fmt.Errorf("failed to close index: %w", esErrorString(err)) } @@ -274,6 +274,7 @@ func IndexFields(conf *cfg.Config, index string) error { table.Addheaders("field", "type", "searchable", "aggretable", "metadata") idx := 0 + for name, field := range res.Fields { for fieldtype, caps := range field { // fields only have 1 type, so this one is it diff --git a/pkg/es/index_alias.go b/pkg/es/index_alias.go index f6622bb..1b5a8cb 100644 --- a/pkg/es/index_alias.go +++ b/pkg/es/index_alias.go @@ -52,7 +52,6 @@ func IndexAliasList(conf *cfg.Config) error { res, err := conf.DefaultCluster.ES().Indices.GetAlias(). Index("_all"). Do(context.Background()) - if err != nil { return fmt.Errorf("failed to list index aliases: %w", esErrorString(err)) } @@ -110,7 +109,6 @@ func IndexAliasDelete(conf *cfg.Config, index, alias string) error { func IndexAliasRollover(conf *cfg.Config, alias string) error { res, err := RolloverAlias(conf, alias) - if err != nil { return fmt.Errorf("failed to rollover index alias: %w", esErrorString(err)) } diff --git a/pkg/es/index_template.go b/pkg/es/index_template.go index 030ca88..f67ebc0 100644 --- a/pkg/es/index_template.go +++ b/pkg/es/index_template.go @@ -36,7 +36,6 @@ import ( func IndexTemplateList(conf *cfg.Config) error { res, err := conf.DefaultCluster.ES().Indices.GetIndexTemplate(). Do(context.Background()) - if err != nil { return fmt.Errorf("failed to get index templates: %w", esErrorString(err)) } @@ -56,6 +55,7 @@ func IndexTemplateList(conf *cfg.Config) error { } table.Sort() + return table.Print() } @@ -63,7 +63,6 @@ func IndexTemplateShow(conf *cfg.Config, tplname string) error { res, err := conf.DefaultCluster.ES().Indices.GetIndexTemplate(). Name(tplname). Do(context.Background()) - if err != nil { return fmt.Errorf("failed to get index template: %w", esErrorString(err)) } @@ -109,10 +108,11 @@ func IndexTemplateShow(conf *cfg.Config, tplname string) error { err = getIndexTemplateSettings(conf, tplname, table) if err != nil { - return nil + return err } fmt.Println() + if err := table.Print(); err != nil { return err } @@ -137,6 +137,7 @@ func IndexTemplateShow(conf *cfg.Config, tplname string) error { } fmt.Println() + if err := table.Print(); err != nil { return err } @@ -224,7 +225,6 @@ func IndexTemplateCreate(conf *cfg.Config, name string, mappings []string) error create.IndexPatterns(conf.Patterns...) _, err := create.Do(context.Background()) - if err != nil { return fmt.Errorf("failed to create index template: %w", esErrorString(err)) } @@ -241,7 +241,6 @@ func IndexTemplateModify(conf *cfg.Config, name string, mappings []string) error res, err := conf.DefaultCluster.ES().Indices.GetIndexTemplate(). Name(name). Do(context.Background()) - if err != nil { return fmt.Errorf("failed to get index template: %w", esErrorString(err)) } @@ -335,7 +334,6 @@ func IndexTemplateModify(conf *cfg.Config, name string, mappings []string) error modify.IndexPatterns(patterns...) _, err = modify.Do(context.Background()) - if err != nil { return fmt.Errorf("failed to modify index template: %w", esErrorString(err)) } @@ -366,7 +364,6 @@ func rolloverAliasIndexTemplate(conf *cfg.Config, name string) error { res, err := conf.DefaultCluster.ES().Indices.GetIndexTemplate(). Name(name). Do(context.Background()) - if err != nil { return fmt.Errorf("failed to get index template: %w", esErrorString(err)) } @@ -417,7 +414,6 @@ func rolloverAliasIndexTemplate(conf *cfg.Config, name string) error { } return nil - } func modMappings(mappings []string) (types.TypeMappingVariant, error) { diff --git a/pkg/es/index_template_settings.go b/pkg/es/index_template_settings.go index d28da25..86df76b 100644 --- a/pkg/es/index_template_settings.go +++ b/pkg/es/index_template_settings.go @@ -43,6 +43,7 @@ func getIndexTemplateSettings(conf *cfg.Config, tplname string, table *printer.T if err != nil { return err } + fmt.Println(output) } diff --git a/pkg/es/license.go b/pkg/es/license.go index b9fed8b..6f5b6c4 100644 --- a/pkg/es/license.go +++ b/pkg/es/license.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "log/slog" + "strconv" "codeberg.org/scip/esctl/pkg/cfg" "codeberg.org/scip/esctl/pkg/printer" @@ -38,12 +39,14 @@ func LicenseShow(conf *cfg.Config) error { lic := res.License - var maxnodes = "infinite" - var maxunits = "infinite" - var expire = "never" + var ( + maxnodes = "infinite" + maxunits = "infinite" + expire = "never" + ) if lic.MaxNodes != nil { - maxnodes = fmt.Sprintf("%d", *lic.MaxNodes) + maxnodes = strconv.FormatInt(*lic.MaxNodes, 10) } if lic.ExpiryDate != nil { @@ -51,7 +54,7 @@ func LicenseShow(conf *cfg.Config) error { } if lic.MaxResourceUnits != nil { - maxunits = fmt.Sprintf("%d", *lic.MaxResourceUnits) + maxunits = strconv.Itoa(*lic.MaxResourceUnits) } table.Entries = [][]any{ diff --git a/pkg/es/node.go b/pkg/es/node.go index 1bbaa5a..89ebf0a 100644 --- a/pkg/es/node.go +++ b/pkg/es/node.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "log/slog" + "strconv" "strings" "time" @@ -53,6 +54,7 @@ func NodeList(conf *cfg.Config) error { } table.Sort() + if err := table.Print(); err != nil { return err } @@ -187,7 +189,7 @@ func NodeClients(conf *cfg.Config, nodename string) error { table.AddRow( agent, - fmt.Sprintf("%d", *client.Id), + strconv.FormatInt(*client.Id, 10), time.UnixMilli(*client.LastRequestTimeMillis).String(), *client.RemoteAddress, uri, @@ -199,5 +201,6 @@ func NodeClients(conf *cfg.Config, nodename string) error { } table.Sort() + return table.Print() } diff --git a/pkg/es/parallel.go b/pkg/es/parallel.go index 6af0b0c..7d41046 100644 --- a/pkg/es/parallel.go +++ b/pkg/es/parallel.go @@ -69,7 +69,8 @@ func getApiData( which string) { defer wg.Done() - ar := apiResponse{} + apiRes := apiResponse{} + var arerr error switch which { @@ -77,47 +78,47 @@ func getApiData( res, err := es.Cluster.Health(). Do(context.Background()) - ar.health = res - ar.which = ResponseHealth + apiRes.health = res + apiRes.which = ResponseHealth arerr = err case "healthreport": report, err := getHealthReport(conf) - ar.healthreport = report - ar.which = ResponseHealthReport + apiRes.healthreport = report + apiRes.which = ResponseHealthReport arerr = err case "info": res, err := es.Info(). Do(context.Background()) - ar.info = res - ar.which = ResponseInfo + apiRes.info = res + apiRes.which = ResponseInfo arerr = err case "ccr": res, err := es.Ccr.Stats(). Do(context.Background()) - ar.ccr = res - ar.which = ResponseCcr + apiRes.ccr = res + apiRes.which = ResponseCcr arerr = err case "stats": res, err := es.Cluster.Stats(). Do(context.Background()) - ar.stats = res - ar.which = ResponseStats + apiRes.stats = res + apiRes.which = ResponseStats arerr = err case "indices": res, err := es.Cat.Indices(). Do(context.Background()) - ar.indices = &res - ar.which = ResponseIndices + apiRes.indices = &res + apiRes.which = ResponseIndices arerr = err case "indicesbytes": @@ -126,16 +127,16 @@ func getApiData( Bytes(bytes.Bytes{Name: "b"}). Do(context.Background()) - ar.indicesbytes = &res - ar.which = ResponseIndices + apiRes.indicesbytes = &res + apiRes.which = ResponseIndices arerr = err case "tasks": res, err := es.Cat.Tasks(). Do(context.Background()) - ar.tasks = &res - ar.which = ResponseTasks + apiRes.tasks = &res + apiRes.which = ResponseTasks arerr = err case "explain": @@ -143,8 +144,8 @@ func getApiData( ExplainLifecycle("_all"). Do(context.Background()) - ar.explainlifecycle = res - ar.which = ResponseExplain + apiRes.explainlifecycle = res + apiRes.which = ResponseExplain arerr = err case "policies": @@ -152,14 +153,14 @@ func getApiData( GetLifecycle(). Do(context.Background()) - ar.lifecycle = &res - ar.which = ResponseLifecycle + apiRes.lifecycle = &res + apiRes.which = ResponseLifecycle arerr = err } if arerr != nil { - ar.error = fmt.Errorf("failed to get data from API: %w", arerr) + apiRes.error = fmt.Errorf("failed to get data from API: %w", arerr) } - reschan <- ar + reschan <- apiRes } diff --git a/pkg/es/role.go b/pkg/es/role.go index 2e190a1..08341e8 100644 --- a/pkg/es/role.go +++ b/pkg/es/role.go @@ -34,6 +34,7 @@ func RoleNames(conf *cfg.Config) ([]string, error) { } roles := make([]string, len(res)) + idx := 0 for name := range res { roles[idx] = name @@ -66,6 +67,7 @@ func RoleList(conf *cfg.Config) error { } table.Sort() + return table.Print() } @@ -132,7 +134,9 @@ func roleRemoteClusters(conf *cfg.Config, role types.Role) error { table := printer.NewTable(conf, 2, len(role.RemoteCluster)) table.Addheaders("remote cluster", "privilege") + idx := 0 + for _, priv := range role.RemoteCluster { perms := []string{} for _, perm := range priv.Privileges { @@ -145,6 +149,7 @@ func roleRemoteClusters(conf *cfg.Config, role types.Role) error { } table.Sort() + return table.Print() } @@ -155,6 +160,7 @@ func roleClusters(conf *cfg.Config, role types.Role) error { table := printer.NewTable(conf, 1, len(role.Cluster)) table.Addheaders("cluster rights") + idx := 0 for _, cluster := range role.Cluster { table.Entries[idx] = []any{cluster.Name} @@ -162,6 +168,7 @@ func roleClusters(conf *cfg.Config, role types.Role) error { } table.Sort() + return table.Print() } @@ -172,7 +179,9 @@ func roleRemoteIndices(conf *cfg.Config, role types.Role) error { table := printer.NewTable(conf, 3, len(role.RemoteIndices)) table.Addheaders("remote index names", "index permissions", "allow restricted") + idx := 0 + for _, priv := range role.RemoteIndices { perms := []string{} for _, perm := range priv.Privileges { @@ -185,6 +194,7 @@ func roleRemoteIndices(conf *cfg.Config, role types.Role) error { } table.Sort() + return table.Print() } @@ -195,7 +205,9 @@ func roleIndices(conf *cfg.Config, role types.Role) error { table := printer.NewTable(conf, 3, len(role.Indices)) table.Addheaders("index names", "index permissions", "allow restricted") + idx := 0 + for _, priv := range role.Indices { perms := []string{} for _, perm := range priv.Privileges { @@ -208,6 +220,7 @@ func roleIndices(conf *cfg.Config, role types.Role) error { } table.Sort() + return table.Print() } @@ -218,6 +231,7 @@ func roleApplications(conf *cfg.Config, role types.Role) error { table := printer.NewTable(conf, 3, len(role.Applications)) table.Addheaders("application", "privileges", "resources") + idx := 0 for _, priv := range role.Applications { table.Entries[idx] = []any{ @@ -230,5 +244,6 @@ func roleApplications(conf *cfg.Config, role types.Role) error { } table.Sort() + return table.Print() } diff --git a/pkg/es/role_diff.go b/pkg/es/role_diff.go index 68c4171..4bdd416 100644 --- a/pkg/es/role_diff.go +++ b/pkg/es/role_diff.go @@ -179,6 +179,7 @@ func diffRoles(conf *cfg.Config, records map[string]Record, res getrole.Response // iterate over records from CSV and register only those which are not deployed for name := range records { reg := Register{name: name, defined: true} + _, deployed := deployed[name] if !deployed { rows = append(rows, reg) @@ -249,6 +250,7 @@ func getRoleMappingGroups(conf *cfg.Config, rolename string) ([]string, error) { } groups := []string{} + for _, mapping := range mappings { if slices.Contains(mapping.Roles, rolename) { for _, rule := range mapping.Rules.Any { @@ -262,13 +264,13 @@ func getRoleMappingGroups(conf *cfg.Config, rolename string) ([]string, error) { return groups, nil } -func compareSlices(name string, a, b []string) { - slices.Sort(a) - slices.Sort(b) +func compareSlices(name string, left, right []string) { + slices.Sort(left) + slices.Sort(right) - if slices.Compare(a, b) != 0 { + if slices.Compare(left, right) != 0 { fmt.Printf("%s differs:\ndeployed: %s\n csv: %s\n", - name, strings.Join(a, ","), strings.Join(b, ",")) + name, strings.Join(left, ","), strings.Join(right, ",")) } else { fmt.Printf("deployed %s matches csv definition\n", name) } @@ -289,16 +291,19 @@ func RoleDiffSingle(conf *cfg.Config, csvfile, rolename string) error { if !record.defined { fmt.Printf("role %s is not defined\n", rolename) + return nil } role, exists := res[rolename] if !exists { fmt.Printf("role %s is not deployed\n", rolename) + return nil } else { fmt.Printf("role %s is deployed\n", rolename) } + slog.Debug("found role", "role", role) if conf.Debug { @@ -310,6 +315,7 @@ func RoleDiffSingle(conf *cfg.Config, csvfile, rolename string) error { if err != nil { return err } + slog.Debug("group mappings", "groups", groups) // check cluster setting @@ -321,6 +327,7 @@ func RoleDiffSingle(conf *cfg.Config, csvfile, rolename string) error { // check index names+privs indices := []string{} privs := []string{} + for _, index := range role.Indices { for _, name := range index.Names { indices = append(indices, strings.ReplaceAll(name, "**", "*")) @@ -333,6 +340,7 @@ func RoleDiffSingle(conf *cfg.Config, csvfile, rolename string) error { // check kibana application space spaces := []string{} + for _, app := range role.Applications { for _, resource := range app.Resources { if strings.Contains(resource, "space:") { diff --git a/pkg/es/rollover.go b/pkg/es/rollover.go index f7ca971..5ee79e9 100644 --- a/pkg/es/rollover.go +++ b/pkg/es/rollover.go @@ -31,14 +31,17 @@ func RolloverConditions(conf *cfg.Config) types.RolloverConditionsVariant { if conf.MaxAge != "" { cond.MaxAge(esdsl.NewDuration().String(conf.MaxAge)) } + if conf.MaxDocs > 0 { cond.MaxDocs(int64(conf.MaxDocs)) } + if conf.MaxShardSize > 0 { bs := esdsl.NewByteSize() bs.Int64(int64(conf.MaxShardSize)) cond.MaxPrimaryShardSize(bs) } + if conf.MaxShardDocs > 0 { cond.MaxPrimaryShardDocs(int64(conf.MaxShardDocs)) } diff --git a/pkg/es/search.go b/pkg/es/search.go index f1ffa1a..44a6bda 100644 --- a/pkg/es/search.go +++ b/pkg/es/search.go @@ -111,6 +111,7 @@ func explainSearch(conf *cfg.Config, search *search.Search) error { func explain(res *types.ExplanationDetail, indent string) { fmt.Println(indent + "- " + res.Description) + for _, ex := range res.Details { fmt.Println(indent + " - " + ex.Description) fmt.Println(indent + fmt.Sprintf(" score: %f", res.Value)) @@ -167,6 +168,7 @@ func searchOnce(conf *cfg.Config, search *search.Search) error { // https://www.elastic.co/docs/reference/elasticsearch/clients/go/using-the-api/searching#_pit_search_after func searchPit(conf *cfg.Config, req *search.Request) error { ctx := context.Background() + pit, err := conf.DefaultCluster.ES().OpenPointInTime(conf.Index).KeepAlive("1m").Do(ctx) if err != nil { return fmt.Errorf("failed to open point-in-time request for search: %w", err) diff --git a/pkg/es/search_filter.go b/pkg/es/search_filter.go index 8a364fc..5385ab2 100644 --- a/pkg/es/search_filter.go +++ b/pkg/es/search_filter.go @@ -63,6 +63,7 @@ type filter struct { // must match literally, and range filters using -r "@timestamp:2026-05-28T10:00:00 to now" func NewFilter(query string) (*filter, error) { var separator string + var criteria int // we use the constants on top for this switch { @@ -76,7 +77,7 @@ func NewFilter(query string) (*filter, error) { part := strings.Split(query, separator) if len(part) != 2 { - return nil, fmt.Errorf("search queries must be in the form fieldpattern where must be one of: = or !=") + return nil, errors.New("search queries must be in the form fieldpattern where must be one of: = or !=") } flt := &filter{term: part[0], filter: part[1], criteria: criteria} @@ -234,7 +235,6 @@ func addFilters(conf *cfg.Config) ([]types.QueryVariant, error) { } filters[count-1] = rng - } return filters, nil @@ -247,7 +247,6 @@ func addSort(conf *cfg.Config, search *search.Search) *search.Search { Index(conf.Index). Fields(conf.SortBy). Do(context.Background()) - if err != nil { // whatever it was, do not add Sort() slog.Debug("get field capabilities", "field", conf.SortBy, "error", esErrorString(err)) diff --git a/pkg/es/shard.go b/pkg/es/shard.go index 38e5dcd..8c93ab7 100644 --- a/pkg/es/shard.go +++ b/pkg/es/shard.go @@ -40,8 +40,7 @@ func colorzizeShard(conf *cfg.Config, state, name string) string { } func resolvePrirep(state string) string { - switch state { - case "p": + if state == "p" { return "primary" } @@ -125,6 +124,7 @@ func printShards(conf *cfg.Config, shardlist shards.Response) error { } table.Sort() + if err := table.Print(); err != nil { return err } diff --git a/pkg/es/snapshot.go b/pkg/es/snapshot.go index 789abde..f9b2449 100644 --- a/pkg/es/snapshot.go +++ b/pkg/es/snapshot.go @@ -98,6 +98,7 @@ func SnapshotList(conf *cfg.Config) error { } table.Sort() + if err := table.Print(); err != nil { return err } diff --git a/pkg/es/task.go b/pkg/es/task.go index 1e35909..b3ffe11 100644 --- a/pkg/es/task.go +++ b/pkg/es/task.go @@ -30,7 +30,6 @@ import ( func TaskList(conf *cfg.Config) error { res, err := conf.DefaultCluster.ES().Cat.Tasks(). Do(context.Background()) - if err != nil { return fmt.Errorf("failed to get index templates: %w", esErrorString(err)) } @@ -39,6 +38,7 @@ func TaskList(conf *cfg.Config) error { table := printer.NewTable(conf, 8, len(res)) table.Addheaders("task id", "action", "start time", "run time", "node", "type") + for idx, task := range res { var ts time.Time @@ -66,7 +66,6 @@ func TaskCancel(conf *cfg.Config, taskid string) error { _, err := conf.DefaultCluster.ES().Tasks.Cancel(). TaskId(taskid). Do(context.Background()) - if err != nil { return fmt.Errorf("failed to cancel task: %w", esErrorString(err)) } diff --git a/pkg/printer/cast.go b/pkg/printer/cast.go index 40ad367..8a3c27d 100644 --- a/pkg/printer/cast.go +++ b/pkg/printer/cast.go @@ -26,6 +26,7 @@ import ( ) func any2string(in any) string { + //nolint:gocritic switch val := in.(type) { case string: return val @@ -45,10 +46,10 @@ func any2string(in any) string { return val.Format("2006-01-02 15:04:05") case []byte: return string(val) - case types.DateTime, types.Percentage: - return val.(string) case nil: return "null" + case types.Percentage, types.DateTime: + return val.(string) } return "" @@ -78,6 +79,7 @@ func (data *Table) preprocessRows() { // determine max width per column for _, entries := range data.rows { currentWidth := 0 + for idx, entry := range entries { length := visibleLen(entry) diff --git a/pkg/printer/doc.go b/pkg/printer/doc.go index e64ae7f..b970dd9 100644 --- a/pkg/printer/doc.go +++ b/pkg/printer/doc.go @@ -33,7 +33,6 @@ func PrintDocs(conf *cfg.Config, hits []types.Hit) { for _, hit := range hits { PrintDoc(conf, hit) } - } func PrintDoc(conf *cfg.Config, hit types.Hit) { diff --git a/pkg/printer/pager.go b/pkg/printer/pager.go index 037cc3d..5cfb023 100644 --- a/pkg/printer/pager.go +++ b/pkg/printer/pager.go @@ -34,12 +34,14 @@ var ( titleStyle = func() lipgloss.Style { b := lipgloss.RoundedBorder() b.Right = "├" + return lipgloss.NewStyle().BorderStyle(b).Padding(0, 1) }() infoStyle = func() lipgloss.Style { b := lipgloss.RoundedBorder() b.Left = "┤" + return titleStyle.BorderStyle(b) }() ) @@ -56,10 +58,9 @@ func (m model) Init() tea.Cmd { } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - var ( - cmd tea.Cmd - cmds []tea.Cmd - ) + var cmd tea.Cmd + + cmds := make([]tea.Cmd, 1) switch msg := msg.(type) { case tea.KeyMsg: @@ -90,7 +91,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // Handle keyboard and mouse events in the viewport m.viewport, cmd = m.viewport.Update(msg) - cmds = append(cmds, cmd) + cmds[0] = cmd return m, tea.Batch(cmds...) } @@ -99,6 +100,7 @@ func (m model) View() string { if !m.ready { return "\n Initializing..." } + return fmt.Sprintf("%s\n%s\n%s", m.headerView(), m.viewport.View(), m.footerView()) } @@ -106,23 +108,25 @@ func (m model) headerView() string { // title := titleStyle.Render("RPN Help Overview") title := titleStyle.Render(m.title) line := strings.Repeat("─", max(0, m.viewport.Width-lipgloss.Width(title))) + return lipgloss.JoinHorizontal(lipgloss.Center, title, line) } func (m model) footerView() string { info := infoStyle.Render(fmt.Sprintf("%3.f%%", m.viewport.ScrollPercent()*100)) line := strings.Repeat("─", max(0, m.viewport.Width-lipgloss.Width(info))) + return lipgloss.JoinHorizontal(lipgloss.Center, line, info) } func Pager(title, message string) { - p := tea.NewProgram( + pager := tea.NewProgram( model{content: message, title: title}, tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel ) - if _, err := p.Run(); err != nil { + if _, err := pager.Run(); err != nil { fmt.Println("could not run pager:", err) os.Exit(1) } diff --git a/pkg/printer/table.go b/pkg/printer/table.go index fe59672..fd40b36 100644 --- a/pkg/printer/table.go +++ b/pkg/printer/table.go @@ -55,6 +55,7 @@ func NewTable(conf *cfg.Config, columns, rows int) *Table { func NewTableEmpty(conf *cfg.Config) *Table { table := Table{Mode: conf.Output, maxwidth: cfg.GetTermWidth()} table.alignInts = conf.AlignInts + return &table } @@ -70,14 +71,14 @@ func (table *Table) WithHeaders(headers ...string) *Table { return table } -func (data *Table) Print() error { - switch data.Mode { +func (table *Table) Print() error { + switch table.Mode { case "json": - return data.PrintJSON() + return table.PrintJSON() case "yaml": - return data.PrintYAML() + return table.PrintYAML() default: - return data.PrintTSV() + return table.PrintTSV() } } @@ -85,22 +86,8 @@ var ( ansiCtrlSeq = regexp.MustCompile(`\033.[0-9;]+m`) ) -// needed for json and yaml output -func (data *Table) toMap() []map[string]any { - raw := make([]map[string]any, len(data.Entries)) - - for idx, entries := range data.Entries { - raw[idx] = make(map[string]any, len(data.Headers)) - for eidx, entry := range entries { - raw[idx][data.Headers[eidx]] = entry - } - } - - return raw -} - -func (data *Table) PrintYAML() error { - raw := data.toMap() +func (table *Table) PrintYAML() error { + raw := table.toMap() body, err := yaml.Marshal(raw) if err != nil { @@ -112,8 +99,8 @@ func (data *Table) PrintYAML() error { return nil } -func (data *Table) PrintJSON() error { - raw := data.toMap() +func (table *Table) PrintJSON() error { + raw := table.toMap() body, err := json.MarshalIndent(raw, "", " ") if err != nil { @@ -125,34 +112,35 @@ func (data *Table) PrintJSON() error { return nil } -func (data *Table) PrintTSV() error { +func (table *Table) PrintTSV() error { // length's, convert cell types - data.preprocessRows() + table.preprocessRows() // output headers - for idx, header := range data.Headers { - if idx+1 != len(data.Headers) { - fmt.Print(header, strings.Repeat(" ", data.lenHeaders[idx]-visibleLen(header))) + for idx, header := range table.Headers { + if idx+1 != len(table.Headers) { + fmt.Print(header, strings.Repeat(" ", table.lenHeaders[idx]-visibleLen(header))) } else { // no padding for last header fmt.Print(header) } - if idx < len(data.Headers)-1 { + if idx < len(table.Headers)-1 { fmt.Print(" ") } } + fmt.Println() - for _, entries := range data.rows { + for _, entries := range table.rows { currentWidth := 0 for idx, entry := range entries { length := visibleLen(entry) - if length+currentWidth > data.maxwidth && data.maxwidth-currentWidth > 1 { + if length+currentWidth > table.maxwidth && table.maxwidth-currentWidth > 1 { // text is too wide to be put into one line, wrap it - wrapper := wordwrap.Wrapper(data.maxwidth-currentWidth, false) + wrapper := wordwrap.Wrapper(table.maxwidth-currentWidth, false) wrapped := wrapper(entry) // and indent it @@ -165,55 +153,71 @@ func (data *Table) PrintTSV() error { } } - currentWidth += data.lenHeaders[idx] + currentWidth += table.lenHeaders[idx] - if isInt(entry) && data.alignInts { + switch { + case isInt(entry) && table.alignInts: // align right - fmt.Print(strings.Repeat(" ", data.lenHeaders[idx]-length), entry) - } else if length < data.lenHeaders[idx] && idx+1 != len(entries) { + fmt.Print(strings.Repeat(" ", table.lenHeaders[idx]-length), entry) + case length < table.lenHeaders[idx] && idx+1 != len(entries): // pad right, if required - fmt.Print(entry, strings.Repeat(" ", data.lenHeaders[idx]-length)) - } else { + fmt.Print(entry, strings.Repeat(" ", table.lenHeaders[idx]-length)) + default: // no padding for last entry fmt.Print(entry) } - if idx < len(data.Headers)-1 { + if idx < len(table.Headers)-1 { fmt.Print(" ") } } + fmt.Println() } return nil } -func (data *Table) Sort() { +func (table *Table) Sort() { // sanity checks - if len(data.Entries) == 0 { + if len(table.Entries) == 0 { return } - data.preprocessRows() + table.preprocessRows() - sort.Slice(data.rows, func(i, j int) bool { - return data.rows[i][0] < data.rows[j][0] + sort.Slice(table.rows, func(i, j int) bool { + return table.rows[i][0] < table.rows[j][0] }) } -func (data *Table) Addheaders(headers ...string) { +func (table *Table) Addheaders(headers ...string) { for idx, header := range headers { - switch data.Mode { + switch table.Mode { case "json", "yaml": - data.Headers[idx] = strings.ReplaceAll(strings.ToLower(header), " ", "_") + table.Headers[idx] = strings.ReplaceAll(strings.ToLower(header), " ", "_") default: - data.Headers[idx] = bold(strings.ReplaceAll(strings.ToUpper(header), " ", "-")) + table.Headers[idx] = bold(strings.ReplaceAll(strings.ToUpper(header), " ", "-")) } } } -func (data *Table) AddRow(fields ...any) { - data.Entries = append(data.Entries, fields) +func (table *Table) AddRow(fields ...any) { + table.Entries = append(table.Entries, fields) +} + +// needed for json and yaml output +func (table *Table) toMap() []map[string]any { + raw := make([]map[string]any, len(table.Entries)) + + for idx, entries := range table.Entries { + raw[idx] = make(map[string]any, len(table.Headers)) + for eidx, entry := range entries { + raw[idx][table.Headers[eidx]] = entry + } + } + + return raw } // return the length of a string but only visible chars, w/o ansi color escapes