mirror of
https://codeberg.org/scip/esctl.git
synced 2026-08-24 10:54:18 +02:00
Compare commits
1 Commits
internal/r
...
feature/au
| Author | SHA1 | Date | |
|---|---|---|---|
| 489074da01 |
174
pkg/cfg/automate.go
Normal file
174
pkg/cfg/automate.go
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
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 cfg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Automator struct {
|
||||
IsReachable bool
|
||||
Error error
|
||||
Env []string
|
||||
Cluster *Cluster
|
||||
|
||||
// all bash commands, .name must succeed first
|
||||
Name string `yaml:"name"`
|
||||
User string `yaml:"user"`
|
||||
Pass string `yaml:"pass"`
|
||||
Uri string `yaml:"uri"`
|
||||
}
|
||||
|
||||
type AutomatorRunner struct {
|
||||
Output string
|
||||
Error error
|
||||
}
|
||||
|
||||
func NewAutoEnv() []string {
|
||||
return []string{
|
||||
"PATH=" + os.Getenv("PATH"),
|
||||
"HOME=" + os.Getenv("HOME"),
|
||||
"SHELL=" + os.Getenv("SHELL"),
|
||||
"KUBECONFIG=" + os.Getenv("KUBECONFIG"),
|
||||
}
|
||||
}
|
||||
|
||||
func NewAutomator() Automator {
|
||||
autocfg := filepath.Join([]string{os.Getenv("HOME"), ".config", "esctl", "automate.yaml"}...)
|
||||
|
||||
auto := Automator{Env: NewAutoEnv()}
|
||||
|
||||
if !fileExists(autocfg) {
|
||||
return auto
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(autocfg)
|
||||
if err != nil {
|
||||
auto.Error = fmt.Errorf("failed to read config file: %w", err)
|
||||
return auto
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(data, &auto)
|
||||
if err != nil {
|
||||
auto.Error = fmt.Errorf("failed to unmarshal config file: %w", err)
|
||||
return auto
|
||||
}
|
||||
|
||||
hasname := execute(auto.Name, auto.Env)
|
||||
if hasname.Error != nil {
|
||||
auto.Error = hasname.Error
|
||||
return auto
|
||||
}
|
||||
auto.Name = hasname.Output
|
||||
|
||||
hasuser := execute(auto.User, auto.Env)
|
||||
if hasuser.Error != nil {
|
||||
auto.Error = hasuser.Error
|
||||
return auto
|
||||
}
|
||||
auto.User = hasuser.Output
|
||||
|
||||
haspass := execute(auto.Pass, auto.Env)
|
||||
if haspass.Error != nil {
|
||||
auto.Error = haspass.Error
|
||||
return auto
|
||||
}
|
||||
auto.Pass = haspass.Output
|
||||
|
||||
hasuri := execute(auto.Uri, auto.Env)
|
||||
if hasuri.Error != nil {
|
||||
auto.Error = hasuri.Error
|
||||
return auto
|
||||
}
|
||||
auto.Uri = hasuri.Output
|
||||
|
||||
auto.IsReachable = true
|
||||
|
||||
auto.Cluster = &Cluster{
|
||||
Name: auto.Name,
|
||||
Uri: auto.Uri,
|
||||
User: auto.User,
|
||||
Pass: auto.Pass,
|
||||
}
|
||||
|
||||
return auto
|
||||
}
|
||||
|
||||
// FIXME: execute auto.Exec, if defined, in a go routine and let it run forever, might be a tunnel
|
||||
func (auto *Automator) Exec() {}
|
||||
|
||||
// FIXME: cache automator results, only check auto.Name and if it matches the cache use those vars, but run auto.Exec anyway
|
||||
func (auto *Automator) Cache() {}
|
||||
|
||||
func execute(code string, env []string) *AutomatorRunner {
|
||||
timeoutCtx, cancel := context.WithTimeout(context.Background(),
|
||||
time.Duration(10)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
var cmd *exec.Cmd
|
||||
|
||||
// pipe code into bash
|
||||
cmd = exec.CommandContext(timeoutCtx, "bash")
|
||||
cmd.Stdin = strings.NewReader(code)
|
||||
|
||||
cmd.Env = env
|
||||
errbuf := &bytes.Buffer{}
|
||||
cmd.Stderr = errbuf
|
||||
|
||||
done := make(chan bool)
|
||||
out := AutomatorRunner{}
|
||||
|
||||
go func() {
|
||||
output, err := cmd.Output()
|
||||
|
||||
out.Output = strings.TrimSpace(string(output))
|
||||
|
||||
switch {
|
||||
case err != nil:
|
||||
out.Error = err
|
||||
case errbuf.Len() > 0:
|
||||
out.Error = fmt.Errorf(errbuf.String())
|
||||
case timeoutCtx.Err() == context.DeadlineExceeded:
|
||||
out.Error = errors.New("timed out")
|
||||
}
|
||||
|
||||
slog.Debug("executed automator",
|
||||
"code", code,
|
||||
"output", out.Output,
|
||||
"error", out.Error)
|
||||
|
||||
done <- true
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return &out
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,8 +223,7 @@ func (cluster *Cluster) IsReachable() (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (conf *Config) SetupES() error {
|
||||
for name, cluster := range conf.Clusters {
|
||||
func (conf *Config) SetupElasticClient(name string, cluster *Cluster) error {
|
||||
cluster.Name = name
|
||||
cluster.DebugHTTP = conf.DebugHTTP
|
||||
|
||||
@@ -244,6 +243,15 @@ func (conf *Config) SetupES() error {
|
||||
}
|
||||
|
||||
cluster.SetClient(es)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (conf *Config) SetupElasticClients() error {
|
||||
for name, cluster := range conf.Clusters {
|
||||
if err := conf.SetupElasticClient(name, cluster); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
Version string = `v0.0.24`
|
||||
Version string = `v0.0.25`
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -133,7 +133,7 @@ func (conf *Config) Init() error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := conf.SetupES(); err != nil {
|
||||
if err := conf.SetupElasticClients(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -153,6 +153,9 @@ func (conf *Config) Init() error {
|
||||
conf.DefaultCluster.Default = true
|
||||
}
|
||||
} else {
|
||||
// load auto config, if any
|
||||
auto := NewAutomator()
|
||||
|
||||
// we need to determine ourselfes
|
||||
if len(conf.Clusters) == 1 {
|
||||
// ok, just one cluster configured, use this, of course
|
||||
@@ -161,6 +164,14 @@ func (conf *Config) Init() error {
|
||||
conf.CurrentCluster = name
|
||||
conf.DefaultCluster.Default = true
|
||||
}
|
||||
} else if auto.Error == nil && auto.IsReachable {
|
||||
// use auto conf
|
||||
if err := conf.SetupElasticClient(auto.Name, auto.Cluster); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conf.DefaultCluster = auto.Cluster
|
||||
conf.CurrentCluster = auto.Name
|
||||
} else {
|
||||
// multiple ones exists, look if one is set as default
|
||||
for name, cluster := range conf.Clusters {
|
||||
|
||||
Reference in New Issue
Block a user