add support for config file and multiple clusters config (#1)

This commit is contained in:
T. von Dein
2026-04-27 09:11:10 +02:00
parent 496f4aeda3
commit 17f92874e5
7 changed files with 154 additions and 37 deletions

View File

@@ -37,10 +37,7 @@ func Finish(err error) int {
} }
func Main() int { func Main() int {
conf, err := cfg.Init() conf := cfg.NewConfig()
if err != nil {
return Finish(err)
}
cmd := &cli.Command{ cmd := &cli.Command{
Name: "esctl", Name: "esctl",
@@ -57,6 +54,21 @@ func Main() int {
Sources: cli.EnvVars("ES_DEBUG"), Sources: cli.EnvVars("ES_DEBUG"),
Destination: &conf.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{ Commands: []*cli.Command{
@@ -67,7 +79,12 @@ func Main() int {
}, },
Before: func(ctx context.Context, cmd *cli.Command) (context.Context, error) { Before: func(ctx context.Context, cmd *cli.Command) (context.Context, error) {
if err := conf.Init(); err != nil {
Finish(err)
}
log.Init(conf) log.Init(conf)
return nil, nil return nil, nil
}, },
} }

View File

@@ -19,46 +19,132 @@ package cfg
import ( import (
"crypto/tls" "crypto/tls"
"errors" "errors"
"fmt"
"net/http" "net/http"
"os" "os"
"github.com/alecthomas/repr"
"github.com/elastic/elastic-transport-go/v8/elastictransport" "github.com/elastic/elastic-transport-go/v8/elastictransport"
"github.com/elastic/go-elasticsearch/v9" "github.com/elastic/go-elasticsearch/v9"
"gopkg.in/yaml.v3"
) )
const ( const (
Version string = `v0.0.1` Version string = `v0.0.2`
) )
type Config struct { type Cluster struct {
Uri, User, Pass string Uri, User, Pass string
ES *elasticsearch.TypedClient ES *elasticsearch.TypedClient
Debug bool }
type Config struct {
ConfigFile string // -c
CurrentCluster string // -C
Debug bool // -d
Clusters map[string]*Cluster
DefaultCluster *Cluster
From, To, MaxItems int From, To, MaxItems int
Index string Index string
Filter []string Filter []string
Failed, Partials bool Failed, Partials bool
} }
func Init() (*Config, error) { func NewConfig() *Config {
cfg := 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"), Uri: os.Getenv("ES_URI"),
User: os.Getenv("ES_USER"), User: os.Getenv("ES_USER"),
Pass: os.Getenv("ES_PASS"), Pass: os.Getenv("ES_PASS"),
} }
switch { switch {
case cfg.Uri == "": case cluster.Uri == "":
return nil, errors.New("ES_URI unset") return errors.New("ES_URI unset")
case cfg.User == "": case cluster.User == "":
return nil, errors.New("ES_USER unset") return errors.New("ES_USER unset")
case cfg.Pass == "": case cluster.Pass == "":
return nil, errors.New("ES_PASS unset") return errors.New("ES_PASS unset")
} }
es, _ := elasticsearch.NewTyped( conf.Clusters["default"] = &cluster
elasticsearch.WithAddresses(cfg.Uri), conf.DefaultCluster = &cluster
elasticsearch.WithBasicAuth(cfg.User, cfg.Pass),
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( elasticsearch.WithTransportOptions(
elastictransport.WithTransport( elastictransport.WithTransport(
&http.Transport{ &http.Transport{
@@ -68,7 +154,12 @@ func Init() (*Config, error) {
), ),
) )
cfg.ES = es if err != nil {
return fmt.Errorf("failed to setup elasticsearch connection: %w", err)
return &cfg, nil }
cluster.ES = es
}
return nil
} }

View File

@@ -26,7 +26,7 @@ import (
) )
func Health(conf *cfg.Config) error { 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 { if err != nil {
log.Fatalf("Error getting health: %s", err) log.Fatalf("Error getting health: %s", err)
} }

View File

@@ -26,7 +26,7 @@ import (
) )
func IndexList(conf *cfg.Config) error { func IndexList(conf *cfg.Config) error {
cat := conf.ES.Cat.Indices() cat := conf.DefaultCluster.ES.Cat.Indices()
if conf.Failed { if conf.Failed {
cat = cat.Health(healthstatus.Red) cat = cat.Health(healthstatus.Red)
@@ -67,7 +67,7 @@ func IndexList(conf *cfg.Config) error {
} }
func IndexShow(conf *cfg.Config, index string) 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 { if err != nil {
return fmt.Errorf("failed to get index: %s", err) return fmt.Errorf("failed to get index: %s", err)
} }

View File

@@ -53,7 +53,7 @@ func Search(conf *cfg.Config, q string) error {
query.Filter(filters...) query.Filter(filters...)
} }
res, err := conf.ES.Search(). res, err := conf.DefaultCluster.ES.Search().
Index(conf.Index). Index(conf.Index).
Request(&search.Request{ Request(&search.Request{
Query: query.QueryCaster(), Query: query.QueryCaster(),

View File

@@ -42,7 +42,7 @@ type Snapshot struct {
func SnapshotList(conf *cfg.Config) error { func SnapshotList(conf *cfg.Config) error {
// get partial indicies // 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 { if err != nil {
return fmt.Errorf("Error getting indicies: %s", err) return fmt.Errorf("Error getting indicies: %s", err)
} }
@@ -55,7 +55,7 @@ func SnapshotList(conf *cfg.Config) error {
} }
// get snapshots // get snapshots
sres, err := conf.ES.Cat.Snapshots().Do(context.Background()) sres, err := conf.DefaultCluster.ES.Cat.Snapshots().Do(context.Background())
if err != nil { if err != nil {
return fmt.Errorf("Error getting snapshots: %s", err) 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 { 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 { if err != nil {
return fmt.Errorf("failed to get snapshot: %s", err) return fmt.Errorf("failed to get snapshot: %s", err)
} }

9
sample-2-clusters.yaml Normal file
View File

@@ -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