connect.go

99 lines
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
package commands

import (
	"fmt"
	"log"
	"os"
)

const connectUsage = `Connect to a deployed server via SSH

Usage:
  congo connect [name]
  congo connect [name] -- <command>

If only one instance exists, connects directly.
Otherwise, specify the instance name.

Use -- to run a command on the server instead of opening an interactive shell.

Examples:
  congo connect                      # interactive (auto-selects single instance)
  congo connect web-1                # interactive on web-1
  congo connect web-1 -- docker ps   # run command and exit
`

func Connect() {
	// Manual arg parsing to support -- separator for remote commands.
	args := os.Args[2:]

	if len(args) > 0 && (args[0] == "--help" || args[0] == "-h") {
		fmt.Print(connectUsage)
		return
	}

	cfg, err := loadInfraConfig(".")
	if err != nil {
		log.Fatalf("load infra.json: %v", err)
	}

	all := cfg.AllInstances()

	if len(all) == 0 {
		log.Fatal("No instances deployed. Use 'congo launch --new <server-type>' first.")
	}

	// Split args on "--" to get instance name and remote command.
	var name string
	var remoteCmd []string
	for i, arg := range args {
		if arg == "--" {
			remoteCmd = args[i+1:]
			break
		}
		if name == "" {
			name = arg
		}
	}

	var target *Instance
	if name != "" {
		for i := range all {
			if all[i].Name == name {
				target = &all[i]
				break
			}
		}
		if target == nil {
			fmt.Fprintf(os.Stderr, "Instance %q not found. Available instances:\n", name)
			for _, inst := range all {
				fmt.Fprintf(os.Stderr, "  %s (%s)\n", inst.Name, inst.IP)
			}
			os.Exit(1)
		}
	} else if len(all) == 1 {
		target = &all[0]
	} else {
		fmt.Println("Multiple instances found. Specify a name:")
		for _, inst := range all {
			fmt.Printf("  %s (%s)\n", inst.Name, inst.IP)
		}
		os.Exit(1)
	}

	server := target.toServer()

	if len(remoteCmd) > 0 {
		out, err := server.SSH(remoteCmd...)
		if err != nil {
			log.Fatalf("ssh: %v", err)
		}
		fmt.Print(out)
		return
	}

	fmt.Printf("Connecting to %s (%s)...\n", target.Name, target.IP)
	if err := server.Interactive(); err != nil {
		os.Exit(1)
	}
}