mirror of
https://codeberg.org/scip/esctl.git
synced 2026-08-24 07:54:17 +02:00
283 lines
6.6 KiB
Go
283 lines
6.6 KiB
Go
/*
|
|
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"
|
|
"fmt"
|
|
golog "log"
|
|
"os"
|
|
"runtime/pprof"
|
|
"strings"
|
|
|
|
"codeberg.org/scip/esctl/pkg/cfg"
|
|
"codeberg.org/scip/esctl/pkg/es"
|
|
"codeberg.org/scip/esctl/pkg/log"
|
|
|
|
"github.com/urfave/cli/v3"
|
|
)
|
|
|
|
func Finish(err error) int {
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "Error: ", err.Error())
|
|
return 1
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func Main() int {
|
|
conf := cfg.NewConfig()
|
|
tree := false
|
|
|
|
cmd := &cli.Command{
|
|
Name: "esctl",
|
|
Usage: "manage elasticsearch from cli",
|
|
EnableShellCompletion: true,
|
|
|
|
Flags: []cli.Flag{
|
|
&cli.BoolFlag{
|
|
Name: "debug",
|
|
Aliases: []string{"d"},
|
|
Value: false,
|
|
Usage: "enable debugging",
|
|
Sources: cli.EnvVars("ES_DEBUG"),
|
|
Destination: &conf.Debug,
|
|
},
|
|
&cli.BoolFlag{
|
|
Name: "debug-http",
|
|
Value: false,
|
|
Usage: "enable HTTP debugging",
|
|
Destination: &conf.DebugHTTP,
|
|
},
|
|
&cli.BoolFlag{
|
|
Name: "show-command-tree",
|
|
Value: false,
|
|
Usage: "generate a command tree",
|
|
Destination: &tree,
|
|
Hidden: true,
|
|
},
|
|
&cli.BoolFlag{
|
|
Name: "align-ints",
|
|
Aliases: []string{"I"},
|
|
Value: false,
|
|
Usage: "right align integers in tabular output",
|
|
Destination: &conf.AlignInts,
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "config",
|
|
Aliases: []string{"c"},
|
|
Value: "",
|
|
Usage: "config file",
|
|
Sources: cli.EnvVars("ES_CONFIG"),
|
|
Destination: &conf.ConfigFile,
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "cluster",
|
|
Aliases: []string{"C"},
|
|
Value: "",
|
|
Usage: "cluster alias to work with",
|
|
Destination: &conf.CurrentCluster,
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "output",
|
|
Aliases: []string{"o"},
|
|
Value: "",
|
|
Usage: "output mode (tsv, markdown, json, yaml) default: tsv",
|
|
Destination: &conf.Output,
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "profile-file",
|
|
Usage: "golang profiler output file",
|
|
Destination: &conf.ProfileFile,
|
|
Hidden: true,
|
|
},
|
|
},
|
|
|
|
Commands: []*cli.Command{
|
|
Api(conf),
|
|
Ccr(conf),
|
|
Cluster(conf),
|
|
Datastream(conf),
|
|
Doc(conf),
|
|
Ilm(conf),
|
|
Index(conf),
|
|
Node(conf),
|
|
Roles(conf),
|
|
Search(conf),
|
|
Shard(conf),
|
|
Snapshot(conf),
|
|
Task(conf),
|
|
Version(conf),
|
|
Debug(conf),
|
|
HelpJsonPath(conf),
|
|
},
|
|
|
|
Before: func(ctx context.Context, cmd *cli.Command) (context.Context, error) {
|
|
if tree {
|
|
if err := Tree(cmd); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
os.Exit(0)
|
|
}
|
|
|
|
if err := conf.Init(); err != nil {
|
|
if len(os.Args) > 1 {
|
|
return nil, err
|
|
} else {
|
|
fmt.Println(cmd.UsageText)
|
|
return nil, nil
|
|
}
|
|
}
|
|
|
|
log.Init(conf)
|
|
|
|
if conf.ProfileFile != "" {
|
|
// enable cpu profiling. Do NOT use q to stop the game but
|
|
// close the window to get a profile
|
|
fd, err := os.Create(conf.ProfileFile)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() {
|
|
if err := fd.Close(); err != nil {
|
|
golog.Fatal(err)
|
|
}
|
|
}()
|
|
|
|
if err := pprof.StartCPUProfile(fd); err != nil {
|
|
golog.Fatal(err)
|
|
}
|
|
|
|
defer pprof.StopCPUProfile()
|
|
}
|
|
|
|
return nil, nil
|
|
},
|
|
}
|
|
|
|
return Finish(cmd.Run(context.Background(), os.Args))
|
|
}
|
|
|
|
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)
|
|
return err
|
|
},
|
|
}
|
|
}
|
|
|
|
func Version(conf *cfg.Config) *cli.Command {
|
|
return &cli.Command{
|
|
Name: "version",
|
|
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",
|
|
cfg.Version, cfg.BUILD, cfg.BRANCH, cfg.COMMIT, cfg.GOVERSION, cfg.APIVERSION)
|
|
return err
|
|
},
|
|
}
|
|
}
|
|
|
|
func Debug(conf *cfg.Config) *cli.Command {
|
|
return &cli.Command{
|
|
Name: "debug",
|
|
Usage: "developer only",
|
|
|
|
Flags: []cli.Flag{
|
|
&cli.StringFlag{
|
|
Name: "index",
|
|
Usage: "index to search within",
|
|
Sources: cli.EnvVars("ES_INDEX"),
|
|
Destination: &conf.Index,
|
|
Aliases: []string{"i"},
|
|
},
|
|
},
|
|
|
|
Action: func(ctx context.Context, cmd *cli.Command) error {
|
|
return es.Debug(conf)
|
|
},
|
|
}
|
|
}
|
|
|
|
func Tree(cmd *cli.Command) error {
|
|
max := 20
|
|
|
|
return cmd.Walk(func(cmd *cli.Command) error {
|
|
path := cmd.Path()
|
|
command := path[len(path)-1]
|
|
|
|
if len(path) == 1 || command == "help" {
|
|
return nil
|
|
}
|
|
|
|
indent := strings.Repeat(" ", len(path[1:])-1)
|
|
space := strings.Repeat(" ", max-(len(command)+len(indent)))
|
|
|
|
fmt.Printf("%s%s %s - %s\n", indent, command, space, cmd.Usage)
|
|
|
|
return nil
|
|
})
|
|
}
|