mirror of
https://github.com/foomo/posh-providers.git
synced 2025-10-16 12:35:41 +00:00
211 lines
5.1 KiB
Go
211 lines
5.1 KiB
Go
package etcd
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"strings"
|
|
|
|
prompt2 "github.com/c-bata/go-prompt"
|
|
"github.com/foomo/posh-providers/kubernets/kubectl"
|
|
"github.com/foomo/posh/pkg/command/tree"
|
|
"github.com/foomo/posh/pkg/env"
|
|
"github.com/foomo/posh/pkg/log"
|
|
"github.com/foomo/posh/pkg/prompt/goprompt"
|
|
"github.com/foomo/posh/pkg/readline"
|
|
"github.com/foomo/posh/pkg/util/prints"
|
|
"github.com/foomo/posh/pkg/util/suggests"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type Command struct {
|
|
l log.Logger
|
|
etcd *ETCD
|
|
kubectl *kubectl.Kubectl
|
|
commandTree tree.Root
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// ~ Constructor
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
func NewCommand(l log.Logger, etcd *ETCD, kubectl *kubectl.Kubectl, opts ...Option) *Command {
|
|
inst := &Command{
|
|
l: l.Named("etcd"),
|
|
etcd: etcd,
|
|
kubectl: kubectl,
|
|
}
|
|
|
|
args := tree.Args{
|
|
{
|
|
Name: "path",
|
|
Suggest: func(ctx context.Context, t tree.Root, r *readline.Readline) []prompt2.Suggest {
|
|
if value, ok := inst.etcd.cfg.Cluster(r.Args().At(0)); ok {
|
|
return suggests.List(value.Paths)
|
|
}
|
|
return nil
|
|
},
|
|
},
|
|
}
|
|
flags := func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error {
|
|
if r.Args().HasIndex(0) {
|
|
fs.Internal().String("profile", "", "Profile to use.")
|
|
if err := fs.Internal().SetValues("profile", inst.kubectl.Cluster(r.Args().At(0)).Profiles(ctx)...); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
inst.commandTree = tree.New(&tree.Node{
|
|
Name: "etcd",
|
|
Description: "Read and write to etcd",
|
|
Nodes: tree.Nodes{
|
|
{
|
|
Name: "cluster",
|
|
Values: func(ctx context.Context, r *readline.Readline) []goprompt.Suggest {
|
|
var ret []string
|
|
for _, cluster := range inst.etcd.kubectl.Clusters() {
|
|
if _, ok := inst.etcd.cfg.Cluster(cluster.Name()); ok {
|
|
ret = append(ret, cluster.Name())
|
|
}
|
|
}
|
|
return suggests.List(ret)
|
|
},
|
|
Nodes: tree.Nodes{
|
|
{
|
|
Name: "get",
|
|
Args: args,
|
|
Flags: flags,
|
|
Execute: inst.get,
|
|
},
|
|
{
|
|
Name: "edit",
|
|
Args: args,
|
|
Flags: flags,
|
|
Execute: inst.edit,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
return inst
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// ~ Public methods
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
func (c *Command) Name() string {
|
|
return c.commandTree.Node().Name
|
|
}
|
|
|
|
func (c *Command) Description() string {
|
|
return c.commandTree.Node().Description
|
|
}
|
|
|
|
func (c *Command) Complete(ctx context.Context, r *readline.Readline) []goprompt.Suggest {
|
|
return c.commandTree.Complete(ctx, r)
|
|
}
|
|
|
|
func (c *Command) Execute(ctx context.Context, r *readline.Readline) error {
|
|
return c.commandTree.Execute(ctx, r)
|
|
}
|
|
|
|
func (c *Command) Help(ctx context.Context, r *readline.Readline) string {
|
|
return c.commandTree.Help(ctx, r)
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// ~ Private methods
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
func (c *Command) get(ctx context.Context, r *readline.Readline) error {
|
|
etcdPath := r.Args().At(2)
|
|
ifs := r.FlagSets().Internal()
|
|
|
|
profile, err := ifs.GetString("profile")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if cluster, ok := c.etcd.cfg.Cluster(r.Args().At(0)); !ok {
|
|
return errors.New("invalid cluster")
|
|
} else if out, err := c.etcd.GetPath(ctx, cluster, profile, etcdPath); err != nil {
|
|
return errors.Wrap(err, out)
|
|
} else {
|
|
prints.Code(c.l, etcdPath, out+"\n", "yaml")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Command) edit(ctx context.Context, r *readline.Readline) error {
|
|
var (
|
|
prev []byte
|
|
next []byte
|
|
)
|
|
cluster, ok := c.etcd.cfg.Cluster(r.Args().At(0))
|
|
if !ok {
|
|
return errors.New("invalid cluster")
|
|
}
|
|
|
|
etcdPath := r.Args().At(2)
|
|
ifs := r.FlagSets().Internal()
|
|
filename := env.Path(c.etcd.cfg.ConfigPath, etcdPath)
|
|
|
|
profile, err := ifs.GetString("profile")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
{ // retrieve data
|
|
if value, err := c.etcd.GetPath(ctx, cluster, profile, etcdPath); err != nil {
|
|
return err
|
|
} else {
|
|
prev = []byte(strings.ReplaceAll(value, "\r\r\n", "\n"))
|
|
}
|
|
}
|
|
|
|
{ // write to file
|
|
if err := os.MkdirAll(path.Dir(filename), 0700); err != nil {
|
|
return err
|
|
} else if err := os.WriteFile(filename, prev, 0600); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
{ // edit file
|
|
d := "vim"
|
|
if value := os.Getenv("EDITOR"); value != "" {
|
|
d = value
|
|
}
|
|
editor := exec.Command(d, filename)
|
|
editor.Stdin = os.Stdin
|
|
editor.Stdout = os.Stdout
|
|
editor.Stderr = os.Stderr
|
|
if err := editor.Run(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
{ // read in file
|
|
if value, err := os.ReadFile(filename); err != nil {
|
|
return err
|
|
} else if bytes.Equal(prev, value) {
|
|
c.l.Info("no changes")
|
|
return nil
|
|
} else {
|
|
next = value
|
|
}
|
|
}
|
|
|
|
c.l.Info("updating config")
|
|
if out, err := c.etcd.SetPath(ctx, cluster, profile, etcdPath, string(next)); err != nil {
|
|
return errors.Wrap(err, out)
|
|
}
|
|
return nil
|
|
}
|