diff --git a/cmd/root.go b/cmd/root.go index 96d2eb1..645899e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -37,10 +37,7 @@ func Finish(err error) int { } func Main() int { - conf, err := cfg.Init() - if err != nil { - return Finish(err) - } + conf := cfg.NewConfig() cmd := &cli.Command{ Name: "esctl", @@ -57,6 +54,21 @@ func Main() int { Sources: cli.EnvVars("ES_DEBUG"), Destination: &conf.Debug, }, + &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, + }, }, Commands: []*cli.Command{ @@ -67,7 +79,12 @@ func Main() int { }, Before: func(ctx context.Context, cmd *cli.Command) (context.Context, error) { + if err := conf.Init(); err != nil { + Finish(err) + } + log.Init(conf) + return nil, nil }, } diff --git a/pkg/cfg/config.go b/pkg/cfg/config.go index 097f99b..13a3013 100644 --- a/pkg/cfg/config.go +++ b/pkg/cfg/config.go @@ -19,56 +19,147 @@ package cfg import ( "crypto/tls" "errors" + "fmt" "net/http" "os" + "github.com/alecthomas/repr" "github.com/elastic/elastic-transport-go/v8/elastictransport" "github.com/elastic/go-elasticsearch/v9" + "gopkg.in/yaml.v3" ) const ( - Version string = `v0.0.1` + Version string = `v0.0.2` ) +type Cluster struct { + Uri, User, Pass string + ES *elasticsearch.TypedClient +} + type Config struct { - Uri, User, Pass string - ES *elasticsearch.TypedClient - Debug bool + ConfigFile string // -c + CurrentCluster string // -C + Debug bool // -d + Clusters map[string]*Cluster + DefaultCluster *Cluster From, To, MaxItems int Index string Filter []string Failed, Partials bool } -func Init() (*Config, error) { - cfg := Config{ +func NewConfig() *Config { + return &Config{Clusters: map[string]*Cluster{}} +} + +func (conf *Config) Init() error { + if conf.ConfigFile != "" { + if err := conf.LoadConfig(); err != nil { + return err + } + } else { + if err := conf.LoadEnv(); err != nil { + return err + } + } + + if conf.CurrentCluster != "" { + current, exists := conf.Clusters[conf.CurrentCluster] + if !exists { + return fmt.Errorf("no cluster with alias %s configured", conf.CurrentCluster) + } else { + conf.DefaultCluster = current + } + } + + if conf.Debug { + repr.Println(conf) + } + + conf.SetupES() + + return nil +} + +func (conf *Config) LoadEnv() error { + cluster := Cluster{ Uri: os.Getenv("ES_URI"), User: os.Getenv("ES_USER"), Pass: os.Getenv("ES_PASS"), } switch { - case cfg.Uri == "": - return nil, errors.New("ES_URI unset") - case cfg.User == "": - return nil, errors.New("ES_USER unset") - case cfg.Pass == "": - return nil, errors.New("ES_PASS unset") + case cluster.Uri == "": + return errors.New("ES_URI unset") + case cluster.User == "": + return errors.New("ES_USER unset") + case cluster.Pass == "": + return errors.New("ES_PASS unset") } - es, _ := elasticsearch.NewTyped( - elasticsearch.WithAddresses(cfg.Uri), - elasticsearch.WithBasicAuth(cfg.User, cfg.Pass), - elasticsearch.WithTransportOptions( - elastictransport.WithTransport( - &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }, - ), - ), - ) + conf.Clusters["default"] = &cluster + conf.DefaultCluster = &cluster - cfg.ES = es - - return &cfg, nil + return nil +} + +func (conf *Config) LoadConfig() error { + if conf.ConfigFile == "" { + return nil + } + + data, err := os.ReadFile(conf.ConfigFile) + if err != nil { + return fmt.Errorf("failed to read config file: %w", err) + } + + newconf := &Config{} + + err = yaml.Unmarshal(data, newconf) + if err != nil { + return fmt.Errorf("failed to unmarshal config file: %w", err) + } + + if len(newconf.Clusters) > 0 { + conf.Clusters = newconf.Clusters + + _, exists := conf.Clusters["default"] + if !exists { + // no "default", just use the first we stumble upon + for _, cluster := range conf.Clusters { + conf.DefaultCluster = cluster + break + } + } else { + conf.DefaultCluster = newconf.Clusters["default"] + } + } + + return nil +} + +func (conf *Config) SetupES() error { + for _, cluster := range conf.Clusters { + es, err := elasticsearch.NewTyped( + elasticsearch.WithAddresses(cluster.Uri), + elasticsearch.WithBasicAuth(cluster.User, cluster.Pass), + elasticsearch.WithTransportOptions( + elastictransport.WithTransport( + &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + ), + ), + ) + + if err != nil { + return fmt.Errorf("failed to setup elasticsearch connection: %w", err) + } + + cluster.ES = es + } + + return nil } diff --git a/pkg/es/aux.go b/pkg/es/aux.go index 041e312..ef0bacd 100644 --- a/pkg/es/aux.go +++ b/pkg/es/aux.go @@ -26,7 +26,7 @@ import ( ) func Health(conf *cfg.Config) error { - res, err := conf.ES.Cluster.Health().Do(context.Background()) + res, err := conf.DefaultCluster.ES.Cluster.Health().Do(context.Background()) if err != nil { log.Fatalf("Error getting health: %s", err) } diff --git a/pkg/es/index.go b/pkg/es/index.go index 21ccc2b..e7d7ecf 100644 --- a/pkg/es/index.go +++ b/pkg/es/index.go @@ -26,7 +26,7 @@ import ( ) func IndexList(conf *cfg.Config) error { - cat := conf.ES.Cat.Indices() + cat := conf.DefaultCluster.ES.Cat.Indices() if conf.Failed { cat = cat.Health(healthstatus.Red) @@ -67,7 +67,7 @@ func IndexList(conf *cfg.Config) error { } func IndexShow(conf *cfg.Config, index string) error { - res, err := conf.ES.Indices.Get(index).Do(context.Background()) + res, err := conf.DefaultCluster.ES.Indices.Get(index).Do(context.Background()) if err != nil { return fmt.Errorf("failed to get index: %s", err) } diff --git a/pkg/es/search.go b/pkg/es/search.go index e085a92..7df1ae3 100644 --- a/pkg/es/search.go +++ b/pkg/es/search.go @@ -53,7 +53,7 @@ func Search(conf *cfg.Config, q string) error { query.Filter(filters...) } - res, err := conf.ES.Search(). + res, err := conf.DefaultCluster.ES.Search(). Index(conf.Index). Request(&search.Request{ Query: query.QueryCaster(), diff --git a/pkg/es/snapshot.go b/pkg/es/snapshot.go index ddedb65..53812ce 100644 --- a/pkg/es/snapshot.go +++ b/pkg/es/snapshot.go @@ -42,7 +42,7 @@ type Snapshot struct { func SnapshotList(conf *cfg.Config) error { // get partial indicies - ires, err := conf.ES.Cat.Indices().Do(context.Background()) + ires, err := conf.DefaultCluster.ES.Cat.Indices().Do(context.Background()) if err != nil { return fmt.Errorf("Error getting indicies: %s", err) } @@ -55,7 +55,7 @@ func SnapshotList(conf *cfg.Config) error { } // get snapshots - sres, err := conf.ES.Cat.Snapshots().Do(context.Background()) + sres, err := conf.DefaultCluster.ES.Cat.Snapshots().Do(context.Background()) if err != nil { return fmt.Errorf("Error getting snapshots: %s", err) } @@ -103,7 +103,7 @@ func SnapshotList(conf *cfg.Config) error { } func SnapshotShow(conf *cfg.Config, snapshot string) error { - res, err := conf.ES.Snapshot.Get("*", snapshot).Do(context.Background()) + res, err := conf.DefaultCluster.ES.Snapshot.Get("*", snapshot).Do(context.Background()) if err != nil { return fmt.Errorf("failed to get snapshot: %s", err) } diff --git a/sample-2-clusters.yaml b/sample-2-clusters.yaml new file mode 100644 index 0000000..0ca27b7 --- /dev/null +++ b/sample-2-clusters.yaml @@ -0,0 +1,9 @@ +clusters: + default: + uri: https://es.foo.bar:9200/ + user: elastic + pass: 123456 + other: + uri: https://myes.foo:9200/ + user: elastic + pass: asdasdasd