mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 11:03: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(
|
cmd.Flags().StringVarP(
|
||||||
&opts.sshKey, "ssh-key", "i", "",
|
&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(
|
cmd.Flags().StringVarP(
|
||||||
&opts.cluster, "cluster", "c", "",
|
&opts.cluster, "cluster", "c", "",
|
||||||
|
|||||||
+35
-10
@@ -4,32 +4,53 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"os"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
"uncloud/cmd/uncloud/caddy"
|
"uncloud/cmd/uncloud/caddy"
|
||||||
"uncloud/cmd/uncloud/dns"
|
"uncloud/cmd/uncloud/dns"
|
||||||
"uncloud/cmd/uncloud/machine"
|
"uncloud/cmd/uncloud/machine"
|
||||||
"uncloud/cmd/uncloud/service"
|
"uncloud/cmd/uncloud/service"
|
||||||
"uncloud/internal/cli"
|
"uncloud/internal/cli"
|
||||||
|
"uncloud/internal/cli/config"
|
||||||
|
"uncloud/internal/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type globalOptions struct {
|
||||||
|
configPath string
|
||||||
|
connect string
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var configPath string
|
opts := globalOptions{}
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "uncloud",
|
Use: "uncloud",
|
||||||
Short: "A CLI tool for managing Uncloud resources such as clusters, machines, and services.",
|
Short: "A CLI tool for managing Uncloud resources such as clusters, machines, and services.",
|
||||||
SilenceUsage: true,
|
SilenceUsage: true,
|
||||||
SilenceErrors: true,
|
SilenceErrors: true,
|
||||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if strings.HasPrefix(configPath, "~/") {
|
var conn *config.MachineConnection
|
||||||
home, err := os.UserHomeDir()
|
if opts.connect != "" {
|
||||||
if err != nil {
|
if strings.HasPrefix(opts.connect, "tcp://") {
|
||||||
return fmt.Errorf("get user home directory to resolve %q: %w", configPath, err)
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("initialize CLI: %w", err)
|
return fmt.Errorf("initialize CLI: %w", err)
|
||||||
}
|
}
|
||||||
@@ -37,9 +58,13 @@ func main() {
|
|||||||
return nil
|
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.
|
// TODO: allow to override using UNCLOUD_CONFIG env var.
|
||||||
cmd.PersistentFlags().StringVar(&configPath, "uncloud-config", "~/.config/uncloud/config.toml",
|
cmd.PersistentFlags().StringVar(&opts.configPath, "uncloud-config", "~/.config/uncloud/config.toml",
|
||||||
"path to the Uncloud configuration file.")
|
"Path to the Uncloud configuration file.")
|
||||||
_ = cmd.MarkPersistentFlagFilename("uncloud-config", "toml")
|
_ = cmd.MarkPersistentFlagFilename("uncloud-config", "toml")
|
||||||
|
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
|
|||||||
+28
-2
@@ -23,13 +23,22 @@ const defaultClusterName = "default"
|
|||||||
|
|
||||||
type CLI struct {
|
type CLI struct {
|
||||||
config *config.Config
|
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)
|
cfg, err := config.NewFromFile(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("read Uncloud config: %w", err)
|
return nil, fmt.Errorf("read Uncloud config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &CLI{
|
return &CLI{
|
||||||
config: cfg,
|
config: cfg,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -53,7 +62,13 @@ func (cli *CLI) SetCurrentCluster(name string) error {
|
|||||||
return cli.config.Save()
|
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) {
|
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 {
|
if len(cli.config.Clusters) == 0 {
|
||||||
return nil, errors.New(
|
return nil, errors.New(
|
||||||
"no clusters found in the Uncloud config. " +
|
"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.
|
// TODO: iterate over all connections and try to connect to the cluster using the first successful connection.
|
||||||
conn := cfg.Connections[0]
|
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 != "" {
|
if conn.SSH != "" {
|
||||||
user, host, port, err := conn.SSH.Parse()
|
user, host, port, err := conn.SSH.Parse()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -106,7 +131,8 @@ func (cli *CLI) ConnectCluster(ctx context.Context, clusterName string) (*client
|
|||||||
} else if conn.TCP.IsValid() {
|
} else if conn.TCP.IsValid() {
|
||||||
return client.New(ctx, connector.NewTCPConnector(conn.TCP))
|
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.
|
// 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 {
|
type MachineConnection struct {
|
||||||
SSH SSHDestination `toml:"ssh,omitempty"`
|
SSH SSHDestination `toml:"ssh,omitempty"`
|
||||||
|
SSHKeyFile string `toml:"ssh_key_file,omitempty"`
|
||||||
TCP netip.AddrPort `toml:"tcp,omitempty"`
|
TCP netip.AddrPort `toml:"tcp,omitempty"`
|
||||||
Host string `toml:"host,omitempty"`
|
Host string `toml:"host,omitempty"`
|
||||||
PublicKey secret.Secret `toml:"public_key,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".
|
// SSHDestination represents an SSH destination string in the canonical form of "user@host:port".
|
||||||
|
|||||||
Reference in New Issue
Block a user