mirror of
https://codeberg.org/scip/esctl.git
synced 2026-08-24 11:24:17 +02:00
add index template {ls,sh} (#33)
This commit is contained in:
@@ -70,6 +70,11 @@ Command tree:
|
|||||||
list
|
list
|
||||||
set
|
set
|
||||||
status
|
status
|
||||||
|
datastream
|
||||||
|
create
|
||||||
|
delete
|
||||||
|
list
|
||||||
|
show
|
||||||
debug
|
debug
|
||||||
doc
|
doc
|
||||||
add
|
add
|
||||||
@@ -90,6 +95,9 @@ Command tree:
|
|||||||
list
|
list
|
||||||
modify
|
modify
|
||||||
show
|
show
|
||||||
|
template
|
||||||
|
list
|
||||||
|
show
|
||||||
node
|
node
|
||||||
list
|
list
|
||||||
show
|
show
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SETTINGS = `https://www.elastic.co/docs/reference/elasticsearch/configuration-reference`
|
SETTINGS = ``
|
||||||
)
|
)
|
||||||
|
|
||||||
func ClusterSettings(conf *cfg.Config) *cli.Command {
|
func ClusterSettings(conf *cfg.Config) *cli.Command {
|
||||||
@@ -85,7 +85,9 @@ func ClusterSettingsSet(conf *cfg.Config) *cli.Command {
|
|||||||
Name: "set",
|
Name: "set",
|
||||||
Usage: "set|update cluster settings",
|
Usage: "set|update cluster settings",
|
||||||
Aliases: []string{"set", "update"},
|
Aliases: []string{"set", "update"},
|
||||||
UsageText: "set [options] setting:value [setting:value ...]\n\nReference: " + SETTINGS,
|
UsageText: "set [options] setting:value [setting:value ...]",
|
||||||
|
CustomHelpTemplate: addReference(
|
||||||
|
"https://www.elastic.co/docs/reference/elasticsearch/configuration-reference"),
|
||||||
|
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
|
|||||||
@@ -41,8 +41,11 @@ func Index(conf *cfg.Config) *cli.Command {
|
|||||||
IndexClose(conf),
|
IndexClose(conf),
|
||||||
IndexAllocation(conf),
|
IndexAllocation(conf),
|
||||||
IndexModify(conf),
|
IndexModify(conf),
|
||||||
IndexAlias(conf),
|
|
||||||
IndexFields(conf),
|
IndexFields(conf),
|
||||||
|
|
||||||
|
// sub commands
|
||||||
|
IndexAlias(conf),
|
||||||
|
IndexTemplate(conf),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,6 +168,7 @@ Valid field mapping types: integer, text, date, keyword`,
|
|||||||
Usage: "number of replicas to create",
|
Usage: "number of replicas to create",
|
||||||
Destination: &conf.Replicas,
|
Destination: &conf.Replicas,
|
||||||
Aliases: []string{"r"},
|
Aliases: []string{"r"},
|
||||||
|
Value: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -240,6 +244,7 @@ func IndexModify(conf *cfg.Config) *cli.Command {
|
|||||||
Usage: "number of replicas",
|
Usage: "number of replicas",
|
||||||
Destination: &conf.Replicas,
|
Destination: &conf.Replicas,
|
||||||
Aliases: []string{"r"},
|
Aliases: []string{"r"},
|
||||||
|
Value: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
203
cmd/index_template.go
Normal file
203
cmd/index_template.go
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2026 Thomas von Dein
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"codeberg.org/scip/esctl/pkg/cfg"
|
||||||
|
"codeberg.org/scip/esctl/pkg/es"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IndexTemplate(conf *cfg.Config) *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "template",
|
||||||
|
Aliases: []string{"tpl"},
|
||||||
|
Usage: "manage index templates",
|
||||||
|
|
||||||
|
Commands: []*cli.Command{
|
||||||
|
IndexTemplateList(conf),
|
||||||
|
IndexTemplateShow(conf),
|
||||||
|
IndexTemplateCreate(conf, false),
|
||||||
|
IndexTemplateCreate(conf, true),
|
||||||
|
IndexTemplateDelete(conf),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func IndexTemplateList(conf *cfg.Config) *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "list",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
Usage: "list index templates",
|
||||||
|
|
||||||
|
Action: func(ctx context.Context, cmd *cli.Command) error {
|
||||||
|
return es.IndexTemplateList(conf)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func IndexTemplateShow(conf *cfg.Config) *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "show",
|
||||||
|
Aliases: []string{"sh"},
|
||||||
|
Usage: "show details about an index template",
|
||||||
|
|
||||||
|
Action: func(ctx context.Context, cmd *cli.Command) error {
|
||||||
|
index := cmd.Args().Get(0)
|
||||||
|
if index == "" {
|
||||||
|
return errors.New("no index template specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
return es.IndexTemplateShow(conf, cmd.Args().Get(0))
|
||||||
|
},
|
||||||
|
|
||||||
|
ShellComplete: func(ctx context.Context, cmd *cli.Command) {
|
||||||
|
complete(cmd, Cindex)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func IndexTemplateCreate(conf *cfg.Config, modify bool) *cli.Command {
|
||||||
|
name := "create"
|
||||||
|
alias := "+"
|
||||||
|
required := true
|
||||||
|
|
||||||
|
if modify {
|
||||||
|
name = "modify"
|
||||||
|
alias = "mod"
|
||||||
|
required = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cli.Command{
|
||||||
|
Name: name,
|
||||||
|
Aliases: []string{alias},
|
||||||
|
Usage: name + " a new index template",
|
||||||
|
UsageText: name + ` <name> <fieldmapping:type>...`,
|
||||||
|
CustomHelpTemplate: addReference(
|
||||||
|
`Valid field mapping types: integer, text, date, keyword
|
||||||
|
|
||||||
|
Doc for index settings:
|
||||||
|
https://www.elastic.co/docs/reference/elasticsearch/index-settings
|
||||||
|
`),
|
||||||
|
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "data-stream",
|
||||||
|
Usage: "template creates a data stream",
|
||||||
|
Destination: &conf.Stream,
|
||||||
|
Aliases: []string{"S"},
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "allow-auto-create",
|
||||||
|
Usage: "automatically create indices",
|
||||||
|
Destination: &conf.AutoCreate,
|
||||||
|
Aliases: []string{"a"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "mode",
|
||||||
|
Usage: "index mode, one of: standard, timeseries, logsdb or lookup",
|
||||||
|
Destination: &conf.Mode,
|
||||||
|
Aliases: []string{"m"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "retention",
|
||||||
|
Usage: "data retention, e.g. -1 (infinitely), 30d, 3m or 1y (data stream only)",
|
||||||
|
Destination: &conf.Retention,
|
||||||
|
Aliases: []string{"r"},
|
||||||
|
Required: required,
|
||||||
|
},
|
||||||
|
&cli.IntFlag{
|
||||||
|
Name: "priority",
|
||||||
|
Usage: "index template priority",
|
||||||
|
Destination: &conf.Priority,
|
||||||
|
Aliases: []string{"p"},
|
||||||
|
},
|
||||||
|
&cli.StringSliceFlag{
|
||||||
|
Name: "index-pattern",
|
||||||
|
Usage: "index matching pattern",
|
||||||
|
Destination: &conf.Patterns,
|
||||||
|
Aliases: []string{"i"},
|
||||||
|
Required: required,
|
||||||
|
},
|
||||||
|
&cli.StringSliceFlag{
|
||||||
|
Name: "settings",
|
||||||
|
Usage: "individual template settings, format: name:value, multiple possible",
|
||||||
|
Destination: &conf.Settings,
|
||||||
|
Aliases: []string{"s"},
|
||||||
|
},
|
||||||
|
&cli.StringSliceFlag{
|
||||||
|
Name: "component",
|
||||||
|
Usage: "include component template, multiple possible",
|
||||||
|
Destination: &conf.Components,
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
},
|
||||||
|
&cli.StringSliceFlag{
|
||||||
|
Name: "meta",
|
||||||
|
Usage: "custom meta field, format: name:value, multiple possible",
|
||||||
|
Destination: &conf.Meta,
|
||||||
|
Aliases: []string{"M"},
|
||||||
|
},
|
||||||
|
&cli.StringSliceFlag{
|
||||||
|
Name: "aliases",
|
||||||
|
Usage: "set up aliases to associate with indices",
|
||||||
|
Destination: &conf.Aliases,
|
||||||
|
Aliases: []string{"A"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Action: func(ctx context.Context, cmd *cli.Command) error {
|
||||||
|
args := cmd.Args()
|
||||||
|
if args.Len() == 0 {
|
||||||
|
return fmt.Errorf("no name specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
mappings := args.Slice()[1:]
|
||||||
|
|
||||||
|
if modify {
|
||||||
|
return es.IndexTemplateModify(conf, args.Get(0), mappings)
|
||||||
|
}
|
||||||
|
|
||||||
|
return es.IndexTemplateCreate(conf, args.Get(0), mappings)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func IndexTemplateDelete(conf *cfg.Config) *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "delete",
|
||||||
|
Aliases: []string{"rm"},
|
||||||
|
Usage: "delete an index template",
|
||||||
|
|
||||||
|
Action: func(ctx context.Context, cmd *cli.Command) error {
|
||||||
|
index := cmd.Args().Get(0)
|
||||||
|
if index == "" {
|
||||||
|
return errors.New("no index template specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
return es.IndexTemplateDelete(conf, cmd.Args().Get(0))
|
||||||
|
},
|
||||||
|
|
||||||
|
ShellComplete: func(ctx context.Context, cmd *cli.Command) {
|
||||||
|
complete(cmd, Cindex)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,7 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
const SearchUsage = `<sep> might be one of:
|
const SearchUsage = `field <sep> might be one of:
|
||||||
=: Must match
|
=: Must match
|
||||||
!=: Must not match
|
!=: Must not match
|
||||||
|
|
||||||
@@ -43,15 +43,15 @@ For datetime range format refer to:
|
|||||||
https://www.elastic.co/docs/reference/elasticsearch/rest-apis/common-options#date-math
|
https://www.elastic.co/docs/reference/elasticsearch/rest-apis/common-options#date-math
|
||||||
|
|
||||||
For timestamp formats refer to:
|
For timestamp formats refer to:
|
||||||
https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-date-format
|
https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-date-format`
|
||||||
`
|
|
||||||
|
|
||||||
func Search(conf *cfg.Config) *cli.Command {
|
func Search(conf *cfg.Config) *cli.Command {
|
||||||
return &cli.Command{
|
return &cli.Command{
|
||||||
Name: "search",
|
Name: "search",
|
||||||
Aliases: []string{"/"},
|
Aliases: []string{"/"},
|
||||||
Usage: "search within an index",
|
Usage: "search within an index",
|
||||||
UsageText: "search [options] [<[field<sep>]pattern> ...]\n" + SearchUsage,
|
UsageText: "search [options] [<[field<sep>]pattern> ...]\n",
|
||||||
|
CustomHelpTemplate: addReference(SearchUsage),
|
||||||
|
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
|
|||||||
35
cmd/utilities.go
Normal file
35
cmd/utilities.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2026 Thomas von Dein
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func addReference(ref string) string {
|
||||||
|
indentedRef := []string{}
|
||||||
|
|
||||||
|
for _, line := range strings.Split(ref, "\n") {
|
||||||
|
indentedRef = append(indentedRef, " "+line)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s\nREFERENCE:\n%s\n",
|
||||||
|
cli.SubcommandHelpTemplate,
|
||||||
|
strings.Join(indentedRef, "\n"))
|
||||||
|
}
|
||||||
4
go.mod
4
go.mod
@@ -25,6 +25,7 @@ require (
|
|||||||
github.com/fatih/color v1.19.0
|
github.com/fatih/color v1.19.0
|
||||||
github.com/mattn/go-isatty v0.0.22
|
github.com/mattn/go-isatty v0.0.22
|
||||||
github.com/olekukonko/tablewriter v1.1.4
|
github.com/olekukonko/tablewriter v1.1.4
|
||||||
|
github.com/tidwall/gjson v1.19.0
|
||||||
github.com/tlinden/yadu v0.1.3
|
github.com/tlinden/yadu v0.1.3
|
||||||
github.com/urfave/cli/v3 v3.9.1-0.20260524212652-be8b79d0c8de
|
github.com/urfave/cli/v3 v3.9.1-0.20260524212652-be8b79d0c8de
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
@@ -37,19 +38,16 @@ require (
|
|||||||
github.com/go-logr/logr v1.4.3 // indirect
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/goccy/go-json v0.10.6 // indirect
|
github.com/goccy/go-json v0.10.6 // indirect
|
||||||
github.com/lmittmann/tint v1.1.3 // indirect
|
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.23 // indirect
|
github.com/mattn/go-runewidth v0.0.23 // indirect
|
||||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
|
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
|
||||||
github.com/olekukonko/errors v1.2.0 // indirect
|
github.com/olekukonko/errors v1.2.0 // indirect
|
||||||
github.com/olekukonko/ll v0.1.8 // indirect
|
github.com/olekukonko/ll v0.1.8 // indirect
|
||||||
github.com/tidwall/gjson v1.19.0 // indirect
|
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
github.com/tidwall/pretty v1.2.0 // indirect
|
github.com/tidwall/pretty v1.2.0 // indirect
|
||||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect
|
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
52
go.sum
52
go.sum
@@ -2,28 +2,24 @@ github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs
|
|||||||
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
|
||||||
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
|
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
|
||||||
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
|
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
|
||||||
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
||||||
|
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
|
||||||
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
||||||
github.com/clipperhouse/displaywidth v0.10.0 h1:GhBG8WuerxjFQQYeuZAeVTuyxuX+UraiZGD4HJQ3Y8g=
|
github.com/clipperhouse/displaywidth v0.10.0 h1:GhBG8WuerxjFQQYeuZAeVTuyxuX+UraiZGD4HJQ3Y8g=
|
||||||
github.com/clipperhouse/displaywidth v0.10.0/go.mod h1:XqJajYsaiEwkxOj4bowCTMcT1SgvHo9flfF3jQasdbs=
|
github.com/clipperhouse/displaywidth v0.10.0/go.mod h1:XqJajYsaiEwkxOj4bowCTMcT1SgvHo9flfF3jQasdbs=
|
||||||
github.com/clipperhouse/uax29/v2 v2.6.0 h1:z0cDbUV+aPASdFb2/ndFnS9ts/WNXgTNNGFoKXuhpos=
|
github.com/clipperhouse/uax29/v2 v2.6.0 h1:z0cDbUV+aPASdFb2/ndFnS9ts/WNXgTNNGFoKXuhpos=
|
||||||
github.com/clipperhouse/uax29/v2 v2.6.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
github.com/clipperhouse/uax29/v2 v2.6.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/elastic/elastic-transport-go/v8 v8.9.0 h1:KeT/2P54F0xS0S8Y3Pf+tFDg4HmBgReQMB+BMz8dDAs=
|
|
||||||
github.com/elastic/elastic-transport-go/v8 v8.9.0/go.mod h1:ssMTvNS2hwf7CaiGsRRsx4gQHFZ/jS/DkLcISxekWzc=
|
|
||||||
github.com/elastic/elastic-transport-go/v8 v8.11.0 h1:taYmqC2M6+fZt/+W+ENYh/W5L9+KrlJGOSbEJs8egWc=
|
github.com/elastic/elastic-transport-go/v8 v8.11.0 h1:taYmqC2M6+fZt/+W+ENYh/W5L9+KrlJGOSbEJs8egWc=
|
||||||
github.com/elastic/elastic-transport-go/v8 v8.11.0/go.mod h1:DZQ0szCNywc9F+C9l/Kkd4n69SvJVj0I3yK1Of7s3l8=
|
github.com/elastic/elastic-transport-go/v8 v8.11.0/go.mod h1:DZQ0szCNywc9F+C9l/Kkd4n69SvJVj0I3yK1Of7s3l8=
|
||||||
github.com/elastic/go-elasticsearch/v9 v9.3.2 h1:nvtvfN/Gsp/rzUPz/9yILwDAsYJ3s5L0VmhR16zPKCA=
|
|
||||||
github.com/elastic/go-elasticsearch/v9 v9.3.2/go.mod h1:ubKUMJCJbX5V/gW5MIn2NQZyaEZ61ubXwJmD5UMNrM8=
|
|
||||||
github.com/elastic/go-elasticsearch/v9 v9.3.4 h1:vnqXl6jnlA+ZwlfRaKF9BR9woZC1KSB17gtyvgf8zVU=
|
github.com/elastic/go-elasticsearch/v9 v9.3.4 h1:vnqXl6jnlA+ZwlfRaKF9BR9woZC1KSB17gtyvgf8zVU=
|
||||||
github.com/elastic/go-elasticsearch/v9 v9.3.4/go.mod h1:ubKUMJCJbX5V/gW5MIn2NQZyaEZ61ubXwJmD5UMNrM8=
|
github.com/elastic/go-elasticsearch/v9 v9.3.4/go.mod h1:ubKUMJCJbX5V/gW5MIn2NQZyaEZ61ubXwJmD5UMNrM8=
|
||||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
|
||||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
|
||||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
|
||||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
|
||||||
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
||||||
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
|
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
@@ -33,31 +29,34 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
|||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
|
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
|
||||||
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
github.com/lmittmann/tint v1.1.3 h1:Hv4EaHWXQr+GTFnOU4VKf8UvAtZgn0VuKT+G0wFlO3I=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/lmittmann/tint v1.1.3/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
|
||||||
github.com/mattn/go-isatty v0.0.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs=
|
|
||||||
github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
|
||||||
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
|
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
|
||||||
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
||||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
|
||||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
|
||||||
github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
|
github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
|
||||||
github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
|
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
|
||||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
|
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
|
||||||
github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo=
|
github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo=
|
||||||
github.com/olekukonko/errors v1.2.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
github.com/olekukonko/errors v1.2.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
||||||
github.com/olekukonko/ll v0.1.6 h1:lGVTHO+Qc4Qm+fce/2h2m5y9LvqaW+DCN7xW9hsU3uA=
|
|
||||||
github.com/olekukonko/ll v0.1.6/go.mod h1:NVUmjBb/aCtUpjKk75BhWrOlARz3dqsM+OtszpY4o88=
|
|
||||||
github.com/olekukonko/ll v0.1.8 h1:ysHCJRGHYKzmBSdz9w5AySztx7lG8SQY+naTGYUbsz8=
|
github.com/olekukonko/ll v0.1.8 h1:ysHCJRGHYKzmBSdz9w5AySztx7lG8SQY+naTGYUbsz8=
|
||||||
github.com/olekukonko/ll v0.1.8/go.mod h1:RPRC6UcscfFZgjo1nulkfMH5IM0QAYim0LfnMvUuozw=
|
github.com/olekukonko/ll v0.1.8/go.mod h1:RPRC6UcscfFZgjo1nulkfMH5IM0QAYim0LfnMvUuozw=
|
||||||
github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I=
|
github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I=
|
||||||
github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY=
|
github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||||
|
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||||
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU=
|
github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU=
|
||||||
github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc=
|
github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc=
|
||||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
@@ -66,10 +65,6 @@ github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
|||||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
github.com/tlinden/yadu v0.1.3 h1:5cRCUmj+l5yvlM2irtpFBIJwVV2DPEgYSaWvF19FtcY=
|
github.com/tlinden/yadu v0.1.3 h1:5cRCUmj+l5yvlM2irtpFBIJwVV2DPEgYSaWvF19FtcY=
|
||||||
github.com/tlinden/yadu v0.1.3/go.mod h1:l3bRmHKL9zGAR6pnBHY2HRPxBecf7L74BoBgOOpTcUA=
|
github.com/tlinden/yadu v0.1.3/go.mod h1:l3bRmHKL9zGAR6pnBHY2HRPxBecf7L74BoBgOOpTcUA=
|
||||||
github.com/urfave/cli/v3 v3.8.0 h1:XqKPrm0q4P0q5JpoclYoCAv0/MIvH/jZ2umzuf8pNTI=
|
|
||||||
github.com/urfave/cli/v3 v3.8.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
|
|
||||||
github.com/urfave/cli/v3 v3.9.0 h1:AV9lIiPv3ukYnxunaCUsHnEozptYmDN2F0+yWqLMn/c=
|
|
||||||
github.com/urfave/cli/v3 v3.9.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
|
|
||||||
github.com/urfave/cli/v3 v3.9.1-0.20260524212652-be8b79d0c8de h1:ESKPiS7inVoBnv4FmgGNZdjWBI/wmvaragoyD3D9nM4=
|
github.com/urfave/cli/v3 v3.9.1-0.20260524212652-be8b79d0c8de h1:ESKPiS7inVoBnv4FmgGNZdjWBI/wmvaragoyD3D9nM4=
|
||||||
github.com/urfave/cli/v3 v3.9.1-0.20260524212652-be8b79d0c8de/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
|
github.com/urfave/cli/v3 v3.9.1-0.20260524212652-be8b79d0c8de/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
|
||||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||||
@@ -78,18 +73,15 @@ go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
|||||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok=
|
||||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM=
|
|
||||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80=
|
|
||||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
|
||||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
@@ -61,6 +61,18 @@ type Config struct {
|
|||||||
Primary bool // index allocation: -p
|
Primary bool // index allocation: -p
|
||||||
Searchable bool // index fields: -s
|
Searchable bool // index fields: -s
|
||||||
Aggretable bool // index fields: -a
|
Aggretable bool // index fields: -a
|
||||||
|
|
||||||
|
Priority int // index template create: -p
|
||||||
|
Settings []string // index template create: -s
|
||||||
|
Patterns []string // index template create: -i
|
||||||
|
Components []string // index template create: -c
|
||||||
|
Meta []string // index template create: -M
|
||||||
|
Aliases []string // index template create: -A
|
||||||
|
Stream bool // index template create: -S
|
||||||
|
AutoCreate bool // index template create: -a
|
||||||
|
Mode string // index template create: -a
|
||||||
|
Retention string // index template create: -r
|
||||||
|
|
||||||
From, To, MaxItems int // search: flags
|
From, To, MaxItems int // search: flags
|
||||||
Filter []string // search: -F
|
Filter []string // search: -F
|
||||||
Path string // search+doc sh: -p
|
Path string // search+doc sh: -p
|
||||||
@@ -75,7 +87,7 @@ type Config struct {
|
|||||||
Ascending bool // sort: -a
|
Ascending bool // sort: -a
|
||||||
Exclude string // cluster compare: -e (regexp)
|
Exclude string // cluster compare: -e (regexp)
|
||||||
All, Verbose bool // cluster status: -a -v
|
All, Verbose bool // cluster status: -a -v
|
||||||
Persistent, Transient, Default bool // -p -t -D cluster settings set
|
Persistent, Transient, Default bool // cluster settings set: -p -t -D
|
||||||
Force bool // ccr follower renew: -f
|
Force bool // ccr follower renew: -f
|
||||||
HaveJQ bool // determined at runtime by ourselfes
|
HaveJQ bool // determined at runtime by ourselfes
|
||||||
DebugHTTP bool // root: --debug-http
|
DebugHTTP bool // root: --debug-http
|
||||||
|
|||||||
@@ -27,36 +27,6 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// recursively traverse the raw settings hash and build a flat map
|
|
||||||
// consisting of the translated path and its value.
|
|
||||||
//
|
|
||||||
// e.g.
|
|
||||||
// logger:
|
|
||||||
//
|
|
||||||
// org:
|
|
||||||
// elasticsearch:
|
|
||||||
// transport:
|
|
||||||
// OutboundHandler: "ERROR"
|
|
||||||
//
|
|
||||||
// gets:
|
|
||||||
//
|
|
||||||
// logger.org.elasticsearch.transport.OutboundHandler: "ERROR"
|
|
||||||
func getJsonPath(raw map[string]any, topic string) map[string]string {
|
|
||||||
paths := map[string]string{}
|
|
||||||
|
|
||||||
for name, data := range raw {
|
|
||||||
path := topic + "." + name
|
|
||||||
switch value := data.(type) {
|
|
||||||
case string:
|
|
||||||
paths[path] = value
|
|
||||||
case map[string]any:
|
|
||||||
paths = getJsonPath(value, path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return paths
|
|
||||||
}
|
|
||||||
|
|
||||||
func ClusterSettingsList(conf *cfg.Config) error {
|
func ClusterSettingsList(conf *cfg.Config) error {
|
||||||
res, err := conf.DefaultCluster.ES.Cluster.GetSettings().
|
res, err := conf.DefaultCluster.ES.Cluster.GetSettings().
|
||||||
Header("content-type", "application/json").
|
Header("content-type", "application/json").
|
||||||
@@ -87,7 +57,7 @@ func ClusterSettingsList(conf *cfg.Config) error {
|
|||||||
return fmt.Errorf("failed to unmarshall setting for topic %s: %s", topic, err)
|
return fmt.Errorf("failed to unmarshall setting for topic %s: %s", topic, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
paths := getJsonPath(data, topic)
|
paths := getJsonPath(map[string]string{}, data, topic)
|
||||||
slog.Debug("settings", topic, paths)
|
slog.Debug("settings", topic, paths)
|
||||||
|
|
||||||
for setting, value := range paths {
|
for setting, value := range paths {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -397,3 +398,44 @@ func getClusterData(es *elasticsearch.TypedClient, wg *sync.WaitGroup, reschan c
|
|||||||
|
|
||||||
reschan <- ar
|
reschan <- ar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// recursively traverse the raw settings hash and build a flat map
|
||||||
|
// consisting of the translated path and its value.
|
||||||
|
//
|
||||||
|
// e.g.
|
||||||
|
// logger:
|
||||||
|
//
|
||||||
|
// org:
|
||||||
|
// elasticsearch:
|
||||||
|
// transport:
|
||||||
|
// OutboundHandler: "ERROR"
|
||||||
|
//
|
||||||
|
// gets:
|
||||||
|
//
|
||||||
|
// logger.org.elasticsearch.transport.OutboundHandler: "ERROR"
|
||||||
|
func getJsonPath(paths map[string]string, raw map[string]any, topic string) map[string]string {
|
||||||
|
for name, data := range raw {
|
||||||
|
path := topic + "." + name
|
||||||
|
|
||||||
|
switch value := data.(type) {
|
||||||
|
case string:
|
||||||
|
paths[path] = value
|
||||||
|
case *string:
|
||||||
|
paths[path] = *value
|
||||||
|
case int:
|
||||||
|
paths[path] = strconv.Itoa(value)
|
||||||
|
case *int:
|
||||||
|
paths[path] = strconv.Itoa(*value)
|
||||||
|
case map[string]any:
|
||||||
|
paths = getJsonPath(paths, value, path)
|
||||||
|
case []any:
|
||||||
|
val := []string{}
|
||||||
|
for _, item := range value {
|
||||||
|
val = append(val, fmt.Sprintf("%v", item))
|
||||||
|
}
|
||||||
|
paths[path] = strings.Join(val, ",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return paths
|
||||||
|
}
|
||||||
|
|||||||
@@ -126,11 +126,7 @@ func IndexList(conf *cfg.Config) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
table.Sort()
|
table.Sort()
|
||||||
if err := table.Print(); err != nil {
|
return table.Print()
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func IndexShow(conf *cfg.Config, indexpattern string) error {
|
func IndexShow(conf *cfg.Config, indexpattern string) error {
|
||||||
|
|||||||
449
pkg/es/index_template.go
Normal file
449
pkg/es/index_template.go
Normal file
@@ -0,0 +1,449 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2026 Thomas von Dein
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package es
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"codeberg.org/scip/esctl/pkg/cfg"
|
||||||
|
"codeberg.org/scip/esctl/pkg/printer"
|
||||||
|
"github.com/elastic/go-elasticsearch/v9/typedapi/esdsl"
|
||||||
|
"github.com/elastic/go-elasticsearch/v9/typedapi/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// used for completion
|
||||||
|
func IndexTemplateList(conf *cfg.Config) error {
|
||||||
|
res, err := conf.DefaultCluster.ES.Indices.GetIndexTemplate().
|
||||||
|
Header("content-type", "application/json").
|
||||||
|
Header("accept", "application/json").
|
||||||
|
Do(context.Background())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get index templates: %s", esErrorString(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Debug("res", "index templates", res)
|
||||||
|
|
||||||
|
table := printer.NewTable(conf, 5, len(res.IndexTemplates))
|
||||||
|
table.Addheaders("name", "description", "priority")
|
||||||
|
|
||||||
|
for idx, tpl := range res.IndexTemplates {
|
||||||
|
desc, err := json.Marshal(tpl.IndexTemplate.Meta_["description"])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal meta json data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
table.Entries[idx] = []string{
|
||||||
|
tpl.Name,
|
||||||
|
string(desc),
|
||||||
|
fmt.Sprintf("%d", tpl.IndexTemplate.Priority),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table.Sort()
|
||||||
|
return table.Print()
|
||||||
|
}
|
||||||
|
|
||||||
|
func IndexTemplateShow(conf *cfg.Config, tplname string) error {
|
||||||
|
res, err := conf.DefaultCluster.ES.Indices.GetIndexTemplate().
|
||||||
|
Name(tplname).
|
||||||
|
Header("content-type", "application/json").
|
||||||
|
Header("accept", "application/json").
|
||||||
|
Do(context.Background())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get index template: %s", esErrorString(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Debug("res", "index template", res)
|
||||||
|
|
||||||
|
if len(res.IndexTemplates) != 1 {
|
||||||
|
return errors.New("multiple or no index template matched the pattern")
|
||||||
|
}
|
||||||
|
|
||||||
|
tpl := res.IndexTemplates[0]
|
||||||
|
|
||||||
|
table := printer.NewTable(conf, 2, 6)
|
||||||
|
table.Addheaders("index template property", "value")
|
||||||
|
|
||||||
|
desc, err := json.Marshal(tpl.IndexTemplate.Meta_["description"])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal meta json data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hasds := tpl.IndexTemplate.DataStream != nil
|
||||||
|
|
||||||
|
aliases := []string{}
|
||||||
|
for alias := range tpl.IndexTemplate.Template.Aliases {
|
||||||
|
aliases = append(aliases, alias)
|
||||||
|
}
|
||||||
|
|
||||||
|
table.Entries = [][]string{
|
||||||
|
{"name", tpl.Name},
|
||||||
|
{"description", string(desc)},
|
||||||
|
{"index patterns", strings.Join(tpl.IndexTemplate.IndexPatterns, ",")},
|
||||||
|
{"composed of", strings.Join(tpl.IndexTemplate.ComposedOf, ",")},
|
||||||
|
{"data stream enabled", fmt.Sprintf("%t", hasds)},
|
||||||
|
{"aliases", strings.Join(aliases, ",")},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := table.Print(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
table = printer.NewTable(conf, 2, 0)
|
||||||
|
table.Addheaders("index setting property", "value")
|
||||||
|
|
||||||
|
err = getIndexTemplateSettings(conf, tplname, table)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
if err := table.Print(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
table = printer.NewTable(conf, 2, 0)
|
||||||
|
table.Addheaders("index field mapping", "type")
|
||||||
|
|
||||||
|
for name, field := range tpl.IndexTemplate.Template.Mappings.Properties {
|
||||||
|
typeval := ""
|
||||||
|
|
||||||
|
switch val := field.(type) {
|
||||||
|
case *types.IntegerNumberProperty:
|
||||||
|
typeval = val.Type
|
||||||
|
case *types.KeywordProperty:
|
||||||
|
typeval = val.Type
|
||||||
|
case *types.DateProperty:
|
||||||
|
typeval = val.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
table.Entries = append(table.Entries, []string{
|
||||||
|
name, typeval,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
if err := table.Print(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func IndexTemplateCreate(conf *cfg.Config, name string, mappings []string) error {
|
||||||
|
settings := esdsl.NewIndexSettings()
|
||||||
|
maps := esdsl.NewIndexTemplateMapping()
|
||||||
|
|
||||||
|
create := conf.DefaultCluster.ES.Indices.PutIndexTemplate(name).
|
||||||
|
Header("content-type", "application/json").
|
||||||
|
Header("accept", "application/json")
|
||||||
|
|
||||||
|
if conf.Shards > 0 {
|
||||||
|
settings = settings.NumberOfShards(strconv.Itoa(conf.Shards))
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.Replicas > 0 {
|
||||||
|
settings = settings.NumberOfReplicas(strconv.Itoa(conf.Replicas))
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.Stream {
|
||||||
|
create.DataStream(esdsl.NewDataStreamVisibility())
|
||||||
|
|
||||||
|
if conf.Retention != "" {
|
||||||
|
maps.Lifecycle(
|
||||||
|
esdsl.NewDataStreamLifecycle().
|
||||||
|
DataRetention(
|
||||||
|
esdsl.NewDuration().String(conf.Retention)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.AutoCreate {
|
||||||
|
create.AllowAutoCreate(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.Mode != "" {
|
||||||
|
if !slices.Contains([]string{"standard", "timeseries", "logsdb", "lookup"}, conf.Mode) {
|
||||||
|
return errors.New("mode must be one of: standard, timeseries, logsdb or lookup")
|
||||||
|
}
|
||||||
|
|
||||||
|
settings = settings.Mode(conf.Mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(mappings) > 0 {
|
||||||
|
typemaps := esdsl.NewTypeMapping()
|
||||||
|
|
||||||
|
for _, mapping := range mappings {
|
||||||
|
parts := strings.Split(mapping, ":")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"invalid mapping %s, expect <name:type> (type: integer, text, date, keyword)",
|
||||||
|
mapping)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch parts[1] {
|
||||||
|
case "text":
|
||||||
|
typemaps.AddProperty(parts[0], esdsl.NewTextProperty())
|
||||||
|
case "integer":
|
||||||
|
typemaps.AddProperty(parts[0], esdsl.NewIntegerNumberProperty())
|
||||||
|
case "date":
|
||||||
|
typemaps.AddProperty(parts[0], esdsl.NewDateProperty())
|
||||||
|
case "keyword":
|
||||||
|
typemaps.AddProperty(parts[0], esdsl.NewKeywordProperty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maps.Mappings(typemaps)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(conf.Components) > 0 {
|
||||||
|
create.ComposedOf(conf.Components...)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, alias := range conf.Aliases {
|
||||||
|
maps.AddAlias(alias, esdsl.NewAlias())
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(conf.Meta) > 0 {
|
||||||
|
metadata := map[string]json.RawMessage{}
|
||||||
|
|
||||||
|
for _, meta := range conf.Meta {
|
||||||
|
parts := strings.Split(meta, ":")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return errors.New("meta data must be in the form key:value")
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := json.Marshal(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to json marshal metadata %s: %w", meta, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata[parts[0]] = msg
|
||||||
|
}
|
||||||
|
|
||||||
|
create.Meta_(esdsl.NewMetadata(metadata))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(conf.Settings) > 0 {
|
||||||
|
usersettings := map[string]json.RawMessage{}
|
||||||
|
|
||||||
|
for _, meta := range conf.Settings {
|
||||||
|
parts := strings.Split(meta, ":")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return errors.New("settings data must be in the form key:value")
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := json.Marshal(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to json marshal metadata %s: %w", meta, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
usersettings[parts[0]] = msg
|
||||||
|
}
|
||||||
|
|
||||||
|
settings = settings.IndexSettings(usersettings)
|
||||||
|
}
|
||||||
|
|
||||||
|
maps.Settings(settings)
|
||||||
|
create.Template(maps)
|
||||||
|
create.IndexPatterns(conf.Patterns...)
|
||||||
|
|
||||||
|
_, err := create.Do(context.Background())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create index template: %s", esErrorString(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: func's too long, refactor
|
||||||
|
// FIXME: it's not yet possible to remove items in lists (aliases, mappings, settings), just overwrite them
|
||||||
|
func IndexTemplateModify(conf *cfg.Config, name string, mappings []string) error {
|
||||||
|
maps := esdsl.NewIndexTemplateMapping()
|
||||||
|
|
||||||
|
// load existing index mapping
|
||||||
|
res, err := conf.DefaultCluster.ES.Indices.GetIndexTemplate().
|
||||||
|
Name(name).
|
||||||
|
Header("content-type", "application/json").
|
||||||
|
Header("accept", "application/json").
|
||||||
|
Do(context.Background())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get index template: %s", esErrorString(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Debug("res", "index template", res)
|
||||||
|
|
||||||
|
if len(res.IndexTemplates) != 1 {
|
||||||
|
return errors.New("multiple or no index template matched the pattern")
|
||||||
|
}
|
||||||
|
|
||||||
|
tpl := res.IndexTemplates[0]
|
||||||
|
|
||||||
|
// our modify PUT request
|
||||||
|
modify := conf.DefaultCluster.ES.Indices.PutIndexTemplate(name).
|
||||||
|
Header("content-type", "application/json").
|
||||||
|
Header("accept", "application/json")
|
||||||
|
|
||||||
|
// load existing settings, if any
|
||||||
|
settings := tpl.IndexTemplate.Template.Settings
|
||||||
|
|
||||||
|
// pre fill mappings and aliases
|
||||||
|
maps.Mappings(tpl.IndexTemplate.Template.Mappings)
|
||||||
|
maps.Aliases(tpl.IndexTemplate.Template.Aliases)
|
||||||
|
|
||||||
|
// pre fill meta, if any
|
||||||
|
metadata := map[string]json.RawMessage{}
|
||||||
|
for key, value := range tpl.IndexTemplate.Meta_ {
|
||||||
|
metadata[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// pre fill components
|
||||||
|
modify.ComposedOf(tpl.IndexTemplate.ComposedOf...)
|
||||||
|
|
||||||
|
if conf.Stream {
|
||||||
|
modify.DataStream(esdsl.NewDataStreamVisibility())
|
||||||
|
|
||||||
|
if conf.Retention != "" {
|
||||||
|
maps.Lifecycle(
|
||||||
|
esdsl.NewDataStreamLifecycle().
|
||||||
|
DataRetention(
|
||||||
|
esdsl.NewDuration().String(conf.Retention)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modify.AllowAutoCreate(conf.AutoCreate)
|
||||||
|
|
||||||
|
if conf.Mode != "" {
|
||||||
|
if !slices.Contains([]string{"standard", "timeseries", "logsdb", "lookup"}, conf.Mode) {
|
||||||
|
return errors.New("mode must be one of: standard, timeseries, logsdb or lookup")
|
||||||
|
}
|
||||||
|
|
||||||
|
*settings.Mode = conf.Mode
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(mappings) > 0 {
|
||||||
|
typemaps := esdsl.NewTypeMapping()
|
||||||
|
|
||||||
|
for _, mapping := range mappings {
|
||||||
|
parts := strings.Split(mapping, ":")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"invalid mapping %s, expect <name:type> (type: integer, text, date, keyword)",
|
||||||
|
mapping)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch parts[1] {
|
||||||
|
case "text":
|
||||||
|
typemaps.AddProperty(parts[0], esdsl.NewTextProperty())
|
||||||
|
case "integer":
|
||||||
|
typemaps.AddProperty(parts[0], esdsl.NewIntegerNumberProperty())
|
||||||
|
case "date":
|
||||||
|
typemaps.AddProperty(parts[0], esdsl.NewDateProperty())
|
||||||
|
case "keyword":
|
||||||
|
typemaps.AddProperty(parts[0], esdsl.NewKeywordProperty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maps.Mappings(typemaps)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(conf.Components) > 0 {
|
||||||
|
modify.ComposedOf(conf.Components...)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, alias := range conf.Aliases {
|
||||||
|
maps.AddAlias(alias, esdsl.NewAlias())
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(conf.Meta) > 0 {
|
||||||
|
for _, meta := range conf.Meta {
|
||||||
|
parts := strings.Split(meta, ":")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return errors.New("meta data must be in the form key:value")
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := json.Marshal(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to json marshal metadata %s: %w", meta, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata[parts[0]] = msg
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
modify.Meta_(esdsl.NewMetadata(metadata))
|
||||||
|
|
||||||
|
if len(conf.Settings) > 0 {
|
||||||
|
usersettings := map[string]json.RawMessage{}
|
||||||
|
|
||||||
|
for _, meta := range conf.Settings {
|
||||||
|
parts := strings.Split(meta, ":")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return errors.New("settings data must be in the form key:value")
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := json.Marshal(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to json marshal metadata %s: %w", meta, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
usersettings[parts[0]] = msg
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.IndexSettings = usersettings
|
||||||
|
}
|
||||||
|
|
||||||
|
patterns := tpl.IndexTemplate.IndexPatterns
|
||||||
|
if len(conf.Patterns) > 0 {
|
||||||
|
patterns = conf.Patterns
|
||||||
|
}
|
||||||
|
|
||||||
|
maps.Settings(settings)
|
||||||
|
modify.Template(maps)
|
||||||
|
modify.IndexPatterns(patterns...)
|
||||||
|
|
||||||
|
_, err = modify.Do(context.Background())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to modify index template: %s", esErrorString(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func IndexTemplateDelete(conf *cfg.Config, name string) error {
|
||||||
|
_, err := conf.DefaultCluster.ES.Indices.DeleteIndexTemplate(name).
|
||||||
|
Header("content-type", "application/json").
|
||||||
|
Header("accept", "application/json").
|
||||||
|
Do(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to delete index template: %s", esErrorString(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
61
pkg/es/index_template_settings.go
Normal file
61
pkg/es/index_template_settings.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package es
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"codeberg.org/scip/esctl/pkg/cfg"
|
||||||
|
"codeberg.org/scip/esctl/pkg/printer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TplTemplateData struct {
|
||||||
|
Settings map[string]any `json:"settings"`
|
||||||
|
Aliases map[string]any `json:"aliases"`
|
||||||
|
Lifecycle map[string]any `json:"lifecycle"`
|
||||||
|
Mappings map[string]any `json:"mappings"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TplTemplate struct {
|
||||||
|
Template TplTemplateData `json:"template"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TplTpl struct {
|
||||||
|
IndexTemplate TplTemplate `json:"index_template"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tpl struct {
|
||||||
|
IndexTemplates []TplTpl `json:"index_templates"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIndexTemplateSettings(conf *cfg.Config, tplname string, table *printer.Table) error {
|
||||||
|
raw, err := CallAPI(conf, "GET", "/_index_template/"+tplname, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
data := Tpl{}
|
||||||
|
if err = json.Unmarshal(raw, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.Debug {
|
||||||
|
if err := prettyfiJson(conf, raw); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data.IndexTemplates) == 0 {
|
||||||
|
return fmt.Errorf("index_template %s not found", tplname)
|
||||||
|
}
|
||||||
|
|
||||||
|
tpl := data.IndexTemplates[0].IndexTemplate.Template.Settings
|
||||||
|
for topic, val := range tpl {
|
||||||
|
paths := getJsonPath(map[string]string{}, val.(map[string]any), topic)
|
||||||
|
|
||||||
|
for setting, value := range paths {
|
||||||
|
table.Entries = append(table.Entries, []string{setting, fmt.Sprintf("%v", value)})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -110,12 +110,14 @@ func Repl(conf *cfg.Config) error {
|
|||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = CallAPI(conf, parts[0], parts[1], data)
|
raw, err := CallAPI(conf, parts[0], parts[1], data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("failed to call API: %s\n", esErrorString(err))
|
fmt.Printf("failed to call API: %s\n", esErrorString(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.SetPrompt("> ")
|
if err := prettyfiJson(conf, raw); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -125,7 +127,7 @@ func encodeAuth(username, password string) string {
|
|||||||
return base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
|
return base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
|
||||||
}
|
}
|
||||||
|
|
||||||
func CallAPI(conf *cfg.Config, verb, path, data string) error {
|
func CallAPI(conf *cfg.Config, verb, path, data string) ([]byte, error) {
|
||||||
verb = strings.ToUpper(verb)
|
verb = strings.ToUpper(verb)
|
||||||
|
|
||||||
// we're using port-forwards anyway
|
// we're using port-forwards anyway
|
||||||
@@ -137,7 +139,7 @@ func CallAPI(conf *cfg.Config, verb, path, data string) error {
|
|||||||
|
|
||||||
req, err := http.NewRequest(verb, conf.DefaultCluster.Uri+path, bytes.NewBuffer([]byte(data)))
|
req, err := http.NewRequest(verb, conf.DefaultCluster.Uri+path, bytes.NewBuffer([]byte(data)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
req.Header.Add("Content-Type", "application/json")
|
||||||
@@ -147,16 +149,16 @@ func CallAPI(conf *cfg.Config, verb, path, data string) error {
|
|||||||
// actually execute the request
|
// actually execute the request
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read and print response
|
// Read and print response
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read response body: %s", err)
|
return nil, fmt.Errorf("failed to read response body: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return prettyfiJson(conf, body)
|
return body, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func prettyfiJson(conf *cfg.Config, raw []byte) error {
|
func prettyfiJson(conf *cfg.Config, raw []byte) error {
|
||||||
|
|||||||
Reference in New Issue
Block a user