package main import ( "context" "fmt" "time" "github.com/spf13/cobra" mcproxy "git.wntrmute.dev/kyle/mc-proxy/client/mcproxy" ) func routesCmd() *cobra.Command { cmd := &cobra.Command{ Use: "routes", Short: "Manage routes", Long: "Manage routes for mc-proxy listeners.", } cmd.AddCommand(routesListCmd()) cmd.AddCommand(routesAddCmd()) cmd.AddCommand(routesRemoveCmd()) return cmd } func routesListCmd() *cobra.Command { return &cobra.Command{ Use: "list LISTENER", Short: "List routes for a listener", Long: "List all routes configured for the specified listener address.", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { listenerAddr := args[0] client := clientFromContext(cmd.Context()) ctx, cancel := context.WithTimeout(cmd.Context(), 5*time.Second) defer cancel() routes, err := client.ListRoutes(ctx, listenerAddr) if err != nil { return fmt.Errorf("listing routes: %w", err) } if len(routes) == 0 { fmt.Printf("No routes for %s\n", listenerAddr) return nil } // Find max hostname length for alignment. maxHostLen := 0 for _, r := range routes { if len(r.Hostname) > maxHostLen { maxHostLen = len(r.Hostname) } } fmt.Printf("Routes for %s:\n", listenerAddr) for _, r := range routes { mode := r.Mode if mode == "" { mode = "l4" } extra := "" if r.SendProxyProtocol { extra += " [proxy-protocol]" } if r.BackendTLS { extra += " [backend-tls]" } fmt.Printf(" %-*s -> %-25s [%s]%s\n", maxHostLen, r.Hostname, r.Backend, mode, extra) } return nil }, } } func routesAddCmd() *cobra.Command { var ( mode string tlsCert string tlsKey string backendTLS bool sendProxyProtocol bool ) cmd := &cobra.Command{ Use: "add LISTENER HOSTNAME BACKEND", Short: "Add a route", Long: "Add a route mapping a hostname to a backend for the specified listener.", Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { listenerAddr := args[0] hostname := args[1] backend := args[2] client := clientFromContext(cmd.Context()) ctx, cancel := context.WithTimeout(cmd.Context(), 5*time.Second) defer cancel() route := mcproxy.Route{ Hostname: hostname, Backend: backend, Mode: mode, TLSCert: tlsCert, TLSKey: tlsKey, BackendTLS: backendTLS, SendProxyProtocol: sendProxyProtocol, } if err := client.AddRoute(ctx, listenerAddr, route); err != nil { return fmt.Errorf("adding route: %w", err) } fmt.Printf("Added route: %s -> %s on %s [%s]\n", hostname, backend, listenerAddr, mode) return nil }, } cmd.Flags().StringVar(&mode, "mode", "l4", "route mode: l4 (passthrough) or l7 (TLS-terminating)") cmd.Flags().StringVar(&tlsCert, "tls-cert", "", "TLS certificate path (L7 only)") cmd.Flags().StringVar(&tlsKey, "tls-key", "", "TLS private key path (L7 only)") cmd.Flags().BoolVar(&backendTLS, "backend-tls", false, "re-encrypt to backend (L7 only)") cmd.Flags().BoolVar(&sendProxyProtocol, "send-proxy-protocol", false, "send PROXY v2 header to backend") return cmd } func routesRemoveCmd() *cobra.Command { return &cobra.Command{ Use: "remove LISTENER HOSTNAME", Short: "Remove a route", Long: "Remove a route for the specified hostname from the listener.", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { listenerAddr := args[0] hostname := args[1] client := clientFromContext(cmd.Context()) ctx, cancel := context.WithTimeout(cmd.Context(), 5*time.Second) defer cancel() if err := client.RemoveRoute(ctx, listenerAddr, hostname); err != nil { return fmt.Errorf("removing route: %w", err) } fmt.Printf("Removed route: %s from %s\n", hostname, listenerAddr) return nil }, } }