From cf5818d9633a76ce471fd3ed732ed80a8e68a2a3 Mon Sep 17 00:00:00 2001 From: Pavel Sviderski Date: Fri, 29 Nov 2024 13:30:15 +1000 Subject: [PATCH] =?UTF-8?q?merge=20ucind=20cluster=20configuration=20to=20?= =?UTF-8?q?the=20user=E2=80=99s=20uncloud=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/ucind/main.go | 19 ++++++++++- internal/ucind/cluster.go | 15 ++++++++ internal/ucind/config.go | 68 +++++++++++++++++++++++++++++++++++++ internal/ucind/provision.go | 8 +++-- 4 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 internal/ucind/config.go diff --git a/cmd/ucind/main.go b/cmd/ucind/main.go index 4f90df76..12905126 100644 --- a/cmd/ucind/main.go +++ b/cmd/ucind/main.go @@ -5,11 +5,14 @@ import ( "fmt" "github.com/docker/docker/client" "github.com/spf13/cobra" + "os" + "strings" "uncloud/cmd/ucind/cluster" "uncloud/internal/ucind" ) func main() { + var configPath string cmd := &cobra.Command{ Use: "ucind", Short: "A CLI tool for running Uncloud test clusters using Docker.", @@ -24,13 +27,27 @@ func main() { return fmt.Errorf("create Docker client: %w", err) } - p := ucind.NewProvisioner(cli) + if strings.HasPrefix(configPath, "~/") { + home, err := os.UserHomeDir() + if err != nil { + return fmt.Errorf("get user home directory to resolve %q: %w", configPath, err) + } + configPath = strings.Replace(configPath, "~", home, 1) + } + configUpdater := ucind.NewConfigUpdater(configPath) + + p := ucind.NewProvisioner(cli, configUpdater) // Persist the provisioner in the context so it can be used by subcommands. cmd.SetContext(context.WithValue(cmd.Context(), "provisioner", p)) return nil }, } + // TODO: allow to override using UNCLOUD_CONFIG env var. + cmd.PersistentFlags().StringVar(&configPath, "uncloud-config", "~/.config/uncloud/config.toml", + "path to the Uncloud configuration file.") + _ = cmd.MarkPersistentFlagFilename("uncloud-config", "toml") + cmd.AddCommand( cluster.NewRootCommand(), ) diff --git a/internal/ucind/cluster.go b/internal/ucind/cluster.go index d5def2af..1c679f4d 100644 --- a/internal/ucind/cluster.go +++ b/internal/ucind/cluster.go @@ -75,6 +75,13 @@ func (p *Provisioner) CreateCluster(ctx context.Context, name string, opts Creat return c, err } + if p.configUpdater != nil { + if err = p.configUpdater.AddCluster(c); err != nil { + return c, fmt.Errorf("add cluster to Uncloud config: %w", err) + } + fmt.Printf("Cluster '%s' added to Uncloud config.\n", c.Name) + } + return c, nil } @@ -210,5 +217,13 @@ func (p *Provisioner) RemoveCluster(ctx context.Context, name string) error { if err = p.dockerCli.NetworkRemove(ctx, name); err != nil { return fmt.Errorf("remove Docker network '%s': %w", name, err) } + + if p.configUpdater != nil { + if err = p.configUpdater.RemoveCluster(name); err != nil { + return fmt.Errorf("remove cluster from Uncloud config: %w", err) + } + fmt.Printf("Cluster '%s' removed from Uncloud config.\n", name) + } + return nil } diff --git a/internal/ucind/config.go b/internal/ucind/config.go new file mode 100644 index 00000000..cb1f73b8 --- /dev/null +++ b/internal/ucind/config.go @@ -0,0 +1,68 @@ +package ucind + +import ( + "fmt" + "uncloud/internal/cli/config" +) + +type ConfigUpdater struct { + path string +} + +func NewConfigUpdater(path string) *ConfigUpdater { + return &ConfigUpdater{path: path} +} + +func (u *ConfigUpdater) AddCluster(c Cluster) error { + cfg, err := config.NewFromFile(u.path) + if err != nil { + return fmt.Errorf("read Uncloud config: %w", err) + } + + if _, ok := cfg.Clusters[c.Name]; ok { + return fmt.Errorf("cluster '%s' already exists", c.Name) + } + + clusterCfg := &config.Cluster{ + Name: c.Name, + Connections: make([]config.MachineConnection, len(c.Machines)), + } + for i, m := range c.Machines { + clusterCfg.Connections[i] = config.MachineConnection{ + TCP: m.APIAddress, + } + } + + cfg.Clusters[c.Name] = clusterCfg + cfg.CurrentCluster = c.Name + + if err = cfg.Save(); err != nil { + return fmt.Errorf("save config: %w", err) + } + return nil +} + +func (u *ConfigUpdater) RemoveCluster(name string) error { + cfg, err := config.NewFromFile(u.path) + if err != nil { + return fmt.Errorf("read Uncloud config: %w", err) + } + + if _, ok := cfg.Clusters[name]; !ok { + return nil + } + + delete(cfg.Clusters, name) + + if cfg.CurrentCluster == name { + cfg.CurrentCluster = "" + } + if _, ok := cfg.Clusters["default"]; ok { + cfg.CurrentCluster = "default" + } + + if err = cfg.Save(); err != nil { + return fmt.Errorf("save config: %w", err) + } + return nil +} diff --git a/internal/ucind/provision.go b/internal/ucind/provision.go index 5f76e95c..dcef530f 100644 --- a/internal/ucind/provision.go +++ b/internal/ucind/provision.go @@ -8,11 +8,13 @@ import ( var ErrNotFound = errors.New("not found") type Provisioner struct { - dockerCli *client.Client + dockerCli *client.Client + configUpdater *ConfigUpdater } -func NewProvisioner(cli *client.Client) *Provisioner { +func NewProvisioner(cli *client.Client, configUpdater *ConfigUpdater) *Provisioner { return &Provisioner{ - dockerCli: cli, + dockerCli: cli, + configUpdater: configUpdater, } }