destroy.go

127 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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
package commands

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"os"
	"strings"
)

const destroyUsage = `Remove a deployed server instance

Usage:
  congo destroy <name> [flags]

Flags:
  --force    Skip confirmation prompt

Stops all services, deletes the server from the cloud provider,
and removes the instance from infra.json.

Examples:
  congo destroy web-1           # with confirmation
  congo destroy web-1 --force   # skip confirmation
`

func Destroy() {
	fs := flag.NewFlagSet("destroy", flag.ExitOnError)
	fs.Usage = func() { fmt.Print(destroyUsage) }

	force := fs.Bool("force", false, "Skip confirmation prompt")
	fs.Parse(os.Args[2:])

	name := fs.Arg(0)
	if name == "" {
		fs.Usage()
		os.Exit(1)
	}

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

	// Find the instance.
	var target *Instance
	var serverType string
	var instanceIdx int
	for st, instances := range cfg.Instances {
		found := false
		for i := range instances {
			if instances[i].Name == name {
				target = &instances[i]
				serverType = st
				instanceIdx = i
				found = true
				break
			}
		}
		if found {
			break
		}
	}

	if target == nil {
		var names []string
		for _, instances := range cfg.Instances {
			for _, inst := range instances {
				names = append(names, inst.Name)
			}
		}
		if len(names) == 0 {
			log.Fatal("No instances deployed.")
		}
		fmt.Fprintf(os.Stderr, "Instance %q not found. Available instances:\n", name)
		for _, n := range names {
			fmt.Fprintf(os.Stderr, "  %s\n", n)
		}
		os.Exit(1)
	}

	// Confirmation.
	if !*force {
		fmt.Printf("This will permanently delete %s (%s).\n", target.Name, target.IP)
		fmt.Print("Type the instance name to confirm: ")

		scanner := bufio.NewScanner(os.Stdin)
		scanner.Scan()
		if strings.TrimSpace(scanner.Text()) != name {
			fmt.Println("Aborted.")
			return
		}
	}

	p, err := initProvider(cfg.Platform)
	if err != nil {
		log.Fatal(err)
	}

	// Stop containers on the server before deleting.
	if target.IP != "" {
		server := target.toServer()
		fmt.Printf("Stopping services on %s...\n", target.Name)
		server.SSH("bash", "-c", "docker stop $(docker ps -q) 2>/dev/null")
	}

	// Delete from cloud provider.
	if target.ID != "" {
		fmt.Printf("Deleting server from %s...\n", cfg.Platform.Provider)
		if err := p.DeleteServer(target.ID); err != nil {
			log.Fatalf("delete server: %v", err)
		}
	}

	// Remove from infra.json.
	instances := cfg.Instances[serverType]
	cfg.Instances[serverType] = append(instances[:instanceIdx], instances[instanceIdx+1:]...)
	if len(cfg.Instances[serverType]) == 0 {
		delete(cfg.Instances, serverType)
	}
	if err := cfg.save("."); err != nil {
		log.Fatalf("save infra.json: %v", err)
	}

	fmt.Printf("Destroyed %s.\n", name)
}