mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 19:13:34 +00:00
feat(connect): add --connect flag to connect to remote cluster machine without using config
This commit is contained in:
@@ -77,7 +77,7 @@ func NewInitCommand() *cobra.Command {
|
||||
)
|
||||
cmd.Flags().StringVarP(
|
||||
&opts.sshKey, "ssh-key", "i", "",
|
||||
"path to SSH private key for SSH remote login. (default ~/.ssh/id_*)",
|
||||
"Path to SSH private key for SSH remote login. (default ~/.ssh/id_*)",
|
||||
)
|
||||
cmd.Flags().StringVarP(
|
||||
&opts.cluster, "cluster", "c", "",
|
||||
|
||||
+35
-10
@@ -4,32 +4,53 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"uncloud/cmd/uncloud/caddy"
|
||||
"uncloud/cmd/uncloud/dns"
|
||||
"uncloud/cmd/uncloud/machine"
|
||||
"uncloud/cmd/uncloud/service"
|
||||
"uncloud/internal/cli"
|
||||
"uncloud/internal/cli/config"
|
||||
"uncloud/internal/fs"
|
||||
)
|
||||
|
||||
type globalOptions struct {
|
||||
configPath string
|
||||
connect string
|
||||
}
|
||||
|
||||
func main() {
|
||||
var configPath string
|
||||
opts := globalOptions{}
|
||||
cmd := &cobra.Command{
|
||||
Use: "uncloud",
|
||||
Short: "A CLI tool for managing Uncloud resources such as clusters, machines, and services.",
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if strings.HasPrefix(configPath, "~/") {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get user home directory to resolve %q: %w", configPath, err)
|
||||
var conn *config.MachineConnection
|
||||
if opts.connect != "" {
|
||||
if strings.HasPrefix(opts.connect, "tcp://") {
|
||||
addrPort, err := netip.ParseAddrPort(opts.connect[len("tcp://"):])
|
||||
if err != nil {
|
||||
return fmt.Errorf("parse TCP address: %w", err)
|
||||
}
|
||||
conn = &config.MachineConnection{
|
||||
TCP: addrPort,
|
||||
}
|
||||
} else {
|
||||
dest := opts.connect
|
||||
if strings.HasPrefix(dest, "ssh://") {
|
||||
dest = dest[len("ssh://"):]
|
||||
}
|
||||
conn = &config.MachineConnection{
|
||||
SSH: config.SSHDestination(dest),
|
||||
}
|
||||
}
|
||||
configPath = strings.Replace(configPath, "~", home, 1)
|
||||
}
|
||||
|
||||
uncli, err := cli.New(configPath)
|
||||
configPath := fs.ExpandHomeDir(opts.configPath)
|
||||
uncli, err := cli.New(configPath, conn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("initialize CLI: %w", err)
|
||||
}
|
||||
@@ -37,9 +58,13 @@ func main() {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().StringVar(&opts.connect, "connect", "",
|
||||
"Connect to a remote cluster machine without using the Uncloud configuration file.\n"+
|
||||
"Format: [ssh://]user@host[:port] or tcp://host:port")
|
||||
// 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.PersistentFlags().StringVar(&opts.configPath, "uncloud-config", "~/.config/uncloud/config.toml",
|
||||
"Path to the Uncloud configuration file.")
|
||||
_ = cmd.MarkPersistentFlagFilename("uncloud-config", "toml")
|
||||
|
||||
cmd.AddCommand(
|
||||
|
||||
+28
-2
@@ -23,13 +23,22 @@ const defaultClusterName = "default"
|
||||
|
||||
type CLI struct {
|
||||
config *config.Config
|
||||
conn *config.MachineConnection
|
||||
}
|
||||
|
||||
func New(configPath string) (*CLI, error) {
|
||||
// New creates a new CLI instance with the given config path or remote machine connection.
|
||||
// If the connection is provided, the config is ignored for all operations which is useful for interacting with
|
||||
// a cluster without creating a config.
|
||||
func New(configPath string, conn *config.MachineConnection) (*CLI, error) {
|
||||
if conn != nil {
|
||||
return &CLI{conn: conn}, nil
|
||||
}
|
||||
|
||||
cfg, err := config.NewFromFile(configPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read Uncloud config: %w", err)
|
||||
}
|
||||
|
||||
return &CLI{
|
||||
config: cfg,
|
||||
}, nil
|
||||
@@ -53,7 +62,13 @@ func (cli *CLI) SetCurrentCluster(name string) error {
|
||||
return cli.config.Save()
|
||||
}
|
||||
|
||||
// ConnectCluster connects to a cluster using the given cluster name or the current cluster if not specified.
|
||||
// If the CLI was initialised with a machine connection, the config is ignored and the connection is used instead.
|
||||
func (cli *CLI) ConnectCluster(ctx context.Context, clusterName string) (*client.Client, error) {
|
||||
if cli.conn != nil {
|
||||
return connectCluster(ctx, *cli.conn)
|
||||
}
|
||||
|
||||
if len(cli.config.Clusters) == 0 {
|
||||
return nil, errors.New(
|
||||
"no clusters found in the Uncloud config. " +
|
||||
@@ -88,6 +103,16 @@ func (cli *CLI) ConnectCluster(ctx context.Context, clusterName string) (*client
|
||||
|
||||
// TODO: iterate over all connections and try to connect to the cluster using the first successful connection.
|
||||
conn := cfg.Connections[0]
|
||||
|
||||
c, err := connectCluster(ctx, conn)
|
||||
if err != nil {
|
||||
return nil, errors.New("no valid connection configuration found for the cluster")
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func connectCluster(ctx context.Context, conn config.MachineConnection) (*client.Client, error) {
|
||||
if conn.SSH != "" {
|
||||
user, host, port, err := conn.SSH.Parse()
|
||||
if err != nil {
|
||||
@@ -106,7 +131,8 @@ func (cli *CLI) ConnectCluster(ctx context.Context, clusterName string) (*client
|
||||
} else if conn.TCP.IsValid() {
|
||||
return client.New(ctx, connector.NewTCPConnector(conn.TCP))
|
||||
}
|
||||
return nil, errors.New("no valid connection configuration found for the cluster")
|
||||
|
||||
return nil, errors.New("connection configuration is invalid")
|
||||
}
|
||||
|
||||
// InitCluster initialises a new cluster on a remote machine and returns a client to interact with the cluster.
|
||||
|
||||
@@ -15,10 +15,10 @@ const (
|
||||
|
||||
type MachineConnection struct {
|
||||
SSH SSHDestination `toml:"ssh,omitempty"`
|
||||
SSHKeyFile string `toml:"ssh_key_file,omitempty"`
|
||||
TCP netip.AddrPort `toml:"tcp,omitempty"`
|
||||
Host string `toml:"host,omitempty"`
|
||||
PublicKey secret.Secret `toml:"public_key,omitempty"`
|
||||
SSHKeyFile string `toml:"ssh_key_file,omitempty"`
|
||||
}
|
||||
|
||||
// SSHDestination represents an SSH destination string in the canonical form of "user@host:port".
|
||||
|
||||
Reference in New Issue
Block a user