diff --git a/app/app.go b/app/app.go index 2f2d26361..e132b0337 100644 --- a/app/app.go +++ b/app/app.go @@ -2,7 +2,6 @@ package app import ( "fmt" - "strconv" "github.com/abiosoft/colima/config" "github.com/abiosoft/colima/environment" @@ -75,9 +74,6 @@ func (c colimaApp) Start(conf config.Config) error { if err := c.setRuntime(conf.Runtime); err != nil { return fmt.Errorf("error setting current runtime: %w", err) } - if err := c.setSSHPort(conf.VM.SSHPort); err != nil { - return fmt.Errorf("error setting SSH port: %w", err) - } // persist kubernetes version for future reference. if err := c.setKubernetesVersion(conf.Kubernetes.Version); err != nil { return fmt.Errorf("error setting kubernetes version: %w", err) @@ -252,10 +248,6 @@ func (c colimaApp) setKubernetesVersion(version string) error { return c.guest.Set(environment.KubernetesVersionKey, version) } -func (c colimaApp) setSSHPort(sshPort int) error { - return c.guest.Set(environment.SSHPortKey, strconv.Itoa(sshPort)) -} - func (c colimaApp) currentContainerEnvironments() ([]environment.Container, error) { var containers []environment.Container diff --git a/cmd/delete.go b/cmd/delete.go index fbd3f925b..04a4d9f6d 100644 --- a/cmd/delete.go +++ b/cmd/delete.go @@ -13,7 +13,7 @@ var deleteCmdArgs struct { // deleteCmd represents the delete command var deleteCmd = &cobra.Command{ - Use: "delete", + Use: "delete [profile]", Short: "delete and teardown Colima", Long: `Delete and teardown Colima and all settings. @@ -21,7 +21,7 @@ Use with caution. This deletes everything and a startup afterwards is like the initial startup of Colima. If you simply want to reset the Kubernetes cluster, run 'colima kubernetes reset'.`, - Args: cobra.NoArgs, + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { if !deleteCmdArgs.force { y := cli.Prompt("are you sure you want to delete " + config.Profile().DisplayName + " and all settings") diff --git a/cmd/root/root.go b/cmd/root/root.go index c4830dfaa..fc32778bf 100644 --- a/cmd/root/root.go +++ b/cmd/root/root.go @@ -15,6 +15,17 @@ var rootCmd = &cobra.Command{ Short: "container runtimes on macOS with minimal setup", Long: `Colima provides container runtimes on macOS with minimal setup.`, PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + + switch cmd.Name() { + // special case handling for commands directly interacting with the VM + // start, stop, delete, status, version, ssh-config + case "start", "stop", "delete", "status", "version", "ssh-config": + // if an arg is passed, assume it to be the profile (provided --profile is unset) + // i.e. colima start docker == colima start --profile=docker + if len(args) > 0 && !cmd.Flag("profile").Changed { + rootCmdArgs.Profile = args[0] + } + } if rootCmdArgs.Profile != "" { config.SetProfile(rootCmdArgs.Profile) } diff --git a/cmd/ssh-config.go b/cmd/ssh-config.go new file mode 100644 index 000000000..71a8216dc --- /dev/null +++ b/cmd/ssh-config.go @@ -0,0 +1,29 @@ +package cmd + +import ( + "github.com/abiosoft/colima/cmd/root" + "github.com/abiosoft/colima/config" + "github.com/abiosoft/colima/environment/vm/lima" + "github.com/spf13/cobra" +) + +// statusCmd represents the status command +var sshConfigCmd = &cobra.Command{ + Use: "ssh-config [profile]", + Short: "show SSH connection config", + Long: `Show configuration of the SSH connection to the VM.`, + Args: cobra.MaximumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return lima.ShowSSH(config.Profile().ID, sshConfigCmdArgs.format) + }, +} + +var sshConfigCmdArgs struct { + format string +} + +func init() { + root.Cmd().AddCommand(sshConfigCmd) + + sshConfigCmd.Flags().StringVarP(&sshConfigCmdArgs.format, "format", "f", "config", "format (config, cmd)") +} diff --git a/cmd/start.go b/cmd/start.go index e558c7f6d..20fd73a67 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -2,7 +2,6 @@ package cmd import ( "fmt" - "net" "runtime" "strings" @@ -16,7 +15,7 @@ import ( // startCmd represents the start command var startCmd = &cobra.Command{ - Use: "start", + Use: "start [profile]", Short: "start Colima", Long: `Start Colima with the specified container runtime (and kubernetes if --with-kubernetes is passed). The --runtime, --disk and --arch flags are only used on initial start and ignored on subsequent starts. @@ -28,14 +27,11 @@ The --runtime, --disk and --arch flags are only used on initial start and ignore " colima start --cpu 4 --memory 8 --disk 100\n" + " colima start --arch aarch64\n" + " colima start --dns 1.1.1.1 --dns 8.8.8.8", - Args: cobra.NoArgs, + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return newApp().Start(startCmdArgs.Config) }, PreRunE: func(cmd *cobra.Command, args []string) error { - // set port - startCmdArgs.VM.SSHPort = randomAvailablePort() - current, err := config.Load() if err != nil { // not fatal, will proceed with defaults @@ -69,9 +65,6 @@ The --runtime, --disk and --arch flags are only used on initial start and ignore if !cmd.Flag("mount").Changed { startCmdArgs.VM.Mounts = current.VM.Mounts } - if !cmd.Flag("port-interface").Changed { - startCmdArgs.PortInterface = current.PortInterface - } if !cmd.Flag("ssh-agent").Changed { startCmdArgs.VM.ForwardAgent = current.VM.ForwardAgent } @@ -100,23 +93,9 @@ var startCmdArgs struct { config.Config } -func randomAvailablePort() int { - listener, err := net.Listen("tcp", ":0") - if err != nil { - log.Fatal(fmt.Errorf("error picking an available port: %w", err)) - } - - if err := listener.Close(); err != nil { - log.Fatal(fmt.Errorf("error closing temporary port listener: %w", err)) - } - - return listener.Addr().(*net.TCPAddr).Port -} - func init() { runtimes := strings.Join(environment.ContainerRuntimes(), ", ") defaultArch := string(environment.Arch(runtime.GOARCH).Value()) - defaultPortInterface := net.ParseIP("0.0.0.0") root.Cmd().AddCommand(startCmd) startCmd.Flags().StringVarP(&startCmdArgs.Runtime, "runtime", "r", docker.Name, "container runtime ("+runtimes+")") @@ -128,9 +107,6 @@ func init() { // mounts startCmd.Flags().StringSliceVarP(&startCmdArgs.VM.Mounts, "mount", "v", nil, "directories to mount, suffix ':w' for writable") - // port forwarding - startCmd.Flags().IPVarP(&startCmdArgs.PortInterface, "port-interface", "i", defaultPortInterface, "interface to use for forwarded ports") - // ssh agent startCmd.Flags().BoolVarP(&startCmdArgs.VM.ForwardAgent, "ssh-agent", "s", false, "forward SSH agent to the VM") diff --git a/cmd/status.go b/cmd/status.go index 4729146ff..c3d11c622 100644 --- a/cmd/status.go +++ b/cmd/status.go @@ -7,10 +7,10 @@ import ( // statusCmd represents the status command var statusCmd = &cobra.Command{ - Use: "status", + Use: "status [profile]", Short: "show the status of Colima", Long: `Show the status of Colima`, - Args: cobra.NoArgs, + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return newApp().Status() }, diff --git a/cmd/stop.go b/cmd/stop.go index b13d16011..2e6736d5e 100644 --- a/cmd/stop.go +++ b/cmd/stop.go @@ -7,13 +7,13 @@ import ( // stopCmd represents the stop command var stopCmd = &cobra.Command{ - Use: "stop", + Use: "stop [profile]", Short: "stop Colima", Long: `Stop stops Colima to free up resources. The state of the VM is persisted at stop. A start afterwards should return it back to its previous state.`, - Args: cobra.NoArgs, + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return newApp().Stop() }, diff --git a/cmd/version.go b/cmd/version.go index 118b58d4f..39a842357 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -11,10 +11,10 @@ import ( // versionCmd represents the version command var versionCmd = &cobra.Command{ - Use: "version", + Use: "version [profile]", Short: "print the version of Colima", Long: `Print the version of Colima`, - Args: cobra.NoArgs, + Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { version := config.AppVersion() fmt.Println(config.AppName, "version", version.Version) diff --git a/config/config.go b/config/config.go index a9e3f4d07..5e415a438 100644 --- a/config/config.go +++ b/config/config.go @@ -156,9 +156,6 @@ type Config struct { // Kubernetes sets if kubernetes should be enabled. Kubernetes Kubernetes `yaml:"kubernetes"` - - // Network address to forward VM ports to. - PortInterface net.IP `yaml:"port_interface"` } // Kubernetes is kubernetes configuration @@ -174,8 +171,6 @@ type VM struct { Memory int `yaml:"memory"` Arch string `yaml:"arch"` - // auto generated - SSHPort int `yaml:"-"` ForwardAgent bool `yaml:"forward_agent"` // volume mounts diff --git a/environment/vm.go b/environment/vm.go index 664dba517..812866876 100644 --- a/environment/vm.go +++ b/environment/vm.go @@ -16,8 +16,6 @@ const ( ContainerRuntimeKey = "runtime" // KubernetesVersionKey is the settings key for kubernetes version. KubernetesVersionKey = "kubernetes_version" - // SSHPortKey is the settings for the VM SSH port. - SSHPortKey = "ssh_port" // BinfmtTarFile is the path in the VM to the binfmt oci image tar. // TODO: decide if this should reside somewhere else. diff --git a/environment/vm/lima/lima.go b/environment/vm/lima/lima.go index 78e8afb1c..e3e2f14a2 100644 --- a/environment/vm/lima/lima.go +++ b/environment/vm/lima/lima.go @@ -412,13 +412,37 @@ func Instances() ([]InstanceInfo, error) { } // rename to local friendly names - if i.Name == "colima" { - i.Name = "default" - } - i.Name = strings.TrimPrefix(i.Name, "colima-") + i.Name = toUserFriendlyName(i.Name) instances = append(instances, i) } return instances, nil } + +// ShowSSH runs the show-ssh command in Lima. +func ShowSSH(name, format string) error { + var buf bytes.Buffer + cmd := cli.Command("limactl", "show-ssh", "--format", format, name) + cmd.Stdout = &buf + cmd.Stderr = os.Stderr + + if err := cmd.Run(); err != nil { + return fmt.Errorf("error retrieving ssh config: %w", err) + } + + // TODO: this is a lazy approach, edge cases may not be covered + from := "lima-" + name + to := name + out := strings.ReplaceAll(buf.String(), from, to) + + fmt.Println(out) + return nil +} + +func toUserFriendlyName(name string) string { + if name == "colima" { + name = "default" + } + return strings.TrimPrefix(name, "colima-") +} diff --git a/environment/vm/lima/yaml.go b/environment/vm/lima/yaml.go index e2fcc2d1f..c2d9bbb21 100644 --- a/environment/vm/lima/yaml.go +++ b/environment/vm/lima/yaml.go @@ -25,7 +25,7 @@ func newConf(conf config.Config) (l Config, err error) { l.Memory = fmt.Sprintf("%dGiB", conf.VM.Memory) l.Disk = fmt.Sprintf("%dGiB", conf.VM.Disk) - l.SSH = SSH{LocalPort: conf.VM.SSHPort, LoadDotSSHPubKeys: false, ForwardAgent: conf.VM.ForwardAgent} + l.SSH = SSH{LocalPort: 0, LoadDotSSHPubKeys: false, ForwardAgent: conf.VM.ForwardAgent} l.Containerd = Containerd{System: false, User: false} l.Firmware.LegacyBIOS = false @@ -121,7 +121,7 @@ type Config struct { Memory string `yaml:"memory,omitempty"` Disk string `yaml:"disk,omitempty"` Mounts []Mount `yaml:"mounts,omitempty"` - SSH SSH `yaml:"ssh,omitempty"` + SSH SSH `yaml:"ssh"` Containerd Containerd `yaml:"containerd"` Env map[string]string `yaml:"env,omitempty"` DNS []net.IP `yaml:"-"` // will be handled manually by colima @@ -142,11 +142,11 @@ type Mount struct { } type SSH struct { - LocalPort int `yaml:"localPort,omitempty"` // REQUIRED + LocalPort int `yaml:"localPort"` // LoadDotSSHPubKeys loads ~/.ssh/*.pub in addition to $LIMA_HOME/_config/user.pub . // Default: true LoadDotSSHPubKeys bool `yaml:"loadDotSSHPubKeys"` - ForwardAgent bool `yaml:"forwardAgent,omitempty"` // default: false + ForwardAgent bool `yaml:"forwardAgent"` // default: false } type Containerd struct {