Add undeploy command: full inverse of deploy

Implements `mcp undeploy <service>` which tears down all infrastructure
for a service: removes mc-proxy routes, DNS records, TLS certificates,
stops and removes containers, releases allocated ports, and marks the
service inactive.

This fills the gap between `stop` (temporary pause) and `purge` (registry
cleanup). Undeploy is the complete teardown that returns the node to the
state before the service was deployed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-27 21:45:42 -07:00
parent b2eaa69619
commit f932dd64cc
8 changed files with 610 additions and 150 deletions

View File

@@ -36,6 +36,7 @@ func main() {
root.AddCommand(loginCmd())
root.AddCommand(buildCmd())
root.AddCommand(deployCmd())
root.AddCommand(undeployCmd())
root.AddCommand(stopCmd())
root.AddCommand(startCmd())
root.AddCommand(restartCmd())

63
cmd/mcp/undeploy.go Normal file
View File

@@ -0,0 +1,63 @@
package main
import (
"context"
"fmt"
"path/filepath"
"github.com/spf13/cobra"
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
"git.wntrmute.dev/mc/mcp/internal/config"
"git.wntrmute.dev/mc/mcp/internal/servicedef"
)
func undeployCmd() *cobra.Command {
return &cobra.Command{
Use: "undeploy <service>",
Short: "Fully undeploy a service: remove routes, DNS, certs, and containers",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := config.LoadCLIConfig(cfgPath)
if err != nil {
return fmt.Errorf("load config: %w", err)
}
serviceName := args[0]
defPath := filepath.Join(cfg.Services.Dir, serviceName+".toml")
def, err := servicedef.Load(defPath)
if err != nil {
return fmt.Errorf("load service def: %w", err)
}
// Set active=false in the local file.
active := false
def.Active = &active
if err := servicedef.Write(defPath, def); err != nil {
return fmt.Errorf("write service def: %w", err)
}
address, err := findNodeAddress(cfg, def.Node)
if err != nil {
return err
}
client, conn, err := dialAgent(address, cfg)
if err != nil {
return fmt.Errorf("dial agent: %w", err)
}
defer func() { _ = conn.Close() }()
resp, err := client.UndeployService(context.Background(), &mcpv1.UndeployServiceRequest{
Name: serviceName,
})
if err != nil {
return fmt.Errorf("undeploy service: %w", err)
}
printComponentResults(resp.GetResults())
return nil
},
}
}