diff --git a/TODO.md b/TODO.md index 7620b83..eb684e1 100644 --- a/TODO.md +++ b/TODO.md @@ -1,2 +1,5 @@ - [Go client docs](https://www.elastic.co/docs/reference/elasticsearch/clients/go/typed-api) - [ES API docs](https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-indices-get) + +- Fix index names custom completion +- add cluster default which would add a flag to the config, so that no -C is needed subsequently diff --git a/cmd/index.go b/cmd/index.go index d2eecca..6a6d688 100644 --- a/cmd/index.go +++ b/cmd/index.go @@ -18,6 +18,7 @@ package cmd import ( "context" + "fmt" "codeberg.org/scip/esctl/pkg/cfg" "codeberg.org/scip/esctl/pkg/es" @@ -34,6 +35,8 @@ func Index(conf *cfg.Config) *cli.Command { Commands: []*cli.Command{ IndexList(conf), IndexShow(conf), + IndexCreate(conf), + IndexDelete(conf), }, } } @@ -88,5 +91,76 @@ func IndexShow(conf *cfg.Config) *cli.Command { return nil }, + + // FIXME: doesn't work at all + // FIXME: also it would ONLY work if the user uses env vars, -C would not be + // there when the completion output is being generated + ShellComplete: func(ctx context.Context, cmd *cli.Command) { + if cmd.NArg() > 0 { + return + } + + indices, err := es.IndexNames(conf) + if err != nil { + return + } + + for _, index := range indices { + fmt.Println(index) + } + }, + } +} + +func IndexCreate(conf *cfg.Config) *cli.Command { + return &cli.Command{ + Name: "create", + Aliases: []string{"+"}, + Usage: "create a new index", + + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "wait", + Usage: "wait for active shards", + Destination: &conf.Wait, + Aliases: []string{"w"}, + }, + &cli.IntFlag{ + Name: "shards", + Usage: "number of shards to create", + Destination: &conf.Shards, + Aliases: []string{"s"}, + }, + &cli.IntFlag{ + Name: "replicas", + Usage: "number of replicas to create", + Destination: &conf.Replicas, + Aliases: []string{"r"}, + }, + }, + + Action: func(ctx context.Context, cmd *cli.Command) error { + if err := es.IndexCreate(conf, cmd.Args().Get(0)); err != nil { + return err + } + + return nil + }, + } +} + +func IndexDelete(conf *cfg.Config) *cli.Command { + return &cli.Command{ + Name: "delete", + Aliases: []string{"rm"}, + Usage: "delete an index", + + Action: func(ctx context.Context, cmd *cli.Command) error { + if err := es.IndexDelete(conf, cmd.Args().Get(0)); err != nil { + return err + } + + return nil + }, } } diff --git a/pkg/cfg/config.go b/pkg/cfg/config.go index e0e7269..3785e96 100644 --- a/pkg/cfg/config.go +++ b/pkg/cfg/config.go @@ -46,6 +46,8 @@ type Config struct { DefaultCluster *Cluster Index string // index: -i Failed, Partials bool // index: flags + Shards, Replicas int // index create: -s -r + Wait bool // index create: -w From, To, MaxItems int // search: flags Filter []string // search: -F Exclude string // cluster compare: -e (regexp) diff --git a/pkg/es/index.go b/pkg/es/index.go index 4585cdd..bb65180 100644 --- a/pkg/es/index.go +++ b/pkg/es/index.go @@ -20,11 +20,39 @@ import ( "context" "fmt" "log/slog" + "strconv" + "time" "codeberg.org/scip/esctl/pkg/cfg" + "github.com/elastic/go-elasticsearch/v9/typedapi/esdsl" "github.com/elastic/go-elasticsearch/v9/typedapi/types/enums/healthstatus" ) +type Settings struct { + wait_for_active_shards string + number_of_shards int + number_of_replicas int +} + +// used for completion +func IndexNames(conf *cfg.Config) ([]string, error) { + res, err := conf.DefaultCluster.ES.Cat.Indices(). + Header("content-type", "application/json"). + Header("accept", "application/json"). + Do(context.Background()) + + if err != nil { + return nil, fmt.Errorf("Error getting indicies: %s", err) + } + + indices := make([]string, len(res)) + for idx, index := range res { + indices[idx] = *index.Index + } + + return indices, nil +} + func IndexList(conf *cfg.Config) error { cat := conf.DefaultCluster.ES.Cat.Indices(). // we need to add custom request headers, required for older ES instances @@ -70,6 +98,10 @@ func IndexList(conf *cfg.Config) error { } func IndexShow(conf *cfg.Config, index string) error { + if index == "" { + return fmt.Errorf("no index specified") + } + res, err := conf.DefaultCluster.ES.Indices.Get(index). // we need to add custom request headers, required for older ES instances Header("content-type", "application/json"). @@ -81,7 +113,73 @@ func IndexShow(conf *cfg.Config, index string) error { slog.Debug("ES result", "index", res) - // FIXME: add table printer like SnapshotShow + table := NewTable(2, 5) + table.Addheaders("field", "value") + + ts, err := strconv.ParseInt(res[index].Settings.Index.CreationDate.(string), 10, 64) + if err != nil { + ts = 0 + } + + created := time.Unix(ts/1000, 0) + + table.entries = [][]string{ + {"name", index}, + {"replicas", *res[index].Settings.Index.NumberOfReplicas}, + {"shards", *res[index].Settings.Index.NumberOfShards}, + {"created", created.Format("2006-01-02 15:04:05")}, + {"uuid", *res[index].Settings.Index.Uuid}, + } + + table.PrintMarkdown() + + return nil +} + +func IndexCreate(conf *cfg.Config, index string) error { + if index == "" { + return fmt.Errorf("no index specified") + } + + settings := esdsl.NewIndexSettings() + + create := conf.DefaultCluster.ES.Indices.Create(index). + Header("content-type", "application/json"). + Header("accept", "application/json") + + if conf.Wait { + create.WaitForActiveShards("all") + } + + if conf.Shards > 0 { + settings = settings.NumberOfShards(strconv.Itoa(conf.Shards)) + } + if conf.Replicas > 0 { + settings = settings.NumberOfReplicas(strconv.Itoa(conf.Replicas)) + } + + _, err := create.Settings(settings). + Do(context.Background()) + + if err != nil { + return fmt.Errorf("failed to create index: %s", err) + } + + return nil +} + +func IndexDelete(conf *cfg.Config, index string) error { + if index == "" { + return fmt.Errorf("no index specified") + } + + _, err := conf.DefaultCluster.ES.Indices.Delete(index). + Header("content-type", "application/json"). + Header("accept", "application/json"). + Do(context.Background()) + if err != nil { + return fmt.Errorf("failed to delete index: %s", err) + } return nil }