Add status command, deployment infrastructure, and fix proto paths
Rename proto/gen directories from mc-proxy to mc_proxy for valid protobuf package naming. Add CLI status subcommand for querying running instance health via gRPC. Add systemd backup service/timer and backup pruning script. Add buf.yaml and proto-lint Makefile target. Add shutdown_timeout config field. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ func rootCmd() *cobra.Command {
|
||||
}
|
||||
|
||||
cmd.AddCommand(serverCmd())
|
||||
cmd.AddCommand(statusCmd())
|
||||
cmd.AddCommand(snapshotCmd())
|
||||
|
||||
return cmd
|
||||
|
||||
101
cmd/mc-proxy/status.go
Normal file
101
cmd/mc-proxy/status.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
|
||||
pb "git.wntrmute.dev/kyle/mc-proxy/gen/mc_proxy/v1"
|
||||
"git.wntrmute.dev/kyle/mc-proxy/internal/config"
|
||||
)
|
||||
|
||||
func statusCmd() *cobra.Command {
|
||||
var configPath string
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "Query a running instance's health and status",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := config.Load(configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cfg.GRPC.Addr == "" {
|
||||
return fmt.Errorf("gRPC admin API is not configured (grpc.addr is empty)")
|
||||
}
|
||||
|
||||
conn, err := dialGRPC(cfg.GRPC)
|
||||
if err != nil {
|
||||
return fmt.Errorf("connecting to gRPC API: %w", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
client := pb.NewProxyAdminServiceClient(conn)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
resp, err := client.GetStatus(ctx, &pb.GetStatusRequest{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting status: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("mc-proxy %s\n", resp.Version)
|
||||
if resp.StartedAt != nil {
|
||||
uptime := time.Since(resp.StartedAt.AsTime()).Truncate(time.Second)
|
||||
fmt.Printf("uptime: %s\n", uptime)
|
||||
}
|
||||
fmt.Printf("connections: %d\n", resp.TotalConnections)
|
||||
fmt.Println()
|
||||
|
||||
for _, ls := range resp.Listeners {
|
||||
fmt.Printf(" %s routes=%d active=%d\n", ls.Addr, ls.RouteCount, ls.ActiveConnections)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&configPath, "config", "c", "mc-proxy.toml", "path to configuration file")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func dialGRPC(cfg config.GRPC) (*grpc.ClientConn, error) {
|
||||
tlsConfig := &tls.Config{
|
||||
MinVersion: tls.VersionTLS13,
|
||||
}
|
||||
|
||||
// Load CA cert for verifying the server.
|
||||
if cfg.CACert != "" {
|
||||
caCert, err := os.ReadFile(cfg.CACert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading CA cert: %w", err)
|
||||
}
|
||||
pool := x509.NewCertPool()
|
||||
if !pool.AppendCertsFromPEM(caCert) {
|
||||
return nil, fmt.Errorf("failed to parse CA certificate")
|
||||
}
|
||||
tlsConfig.RootCAs = pool
|
||||
}
|
||||
|
||||
// Load client cert for mTLS.
|
||||
if cfg.TLSCert != "" && cfg.TLSKey != "" {
|
||||
cert, err := tls.LoadX509KeyPair(cfg.TLSCert, cfg.TLSKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("loading client cert: %w", err)
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
|
||||
creds := credentials.NewTLS(tlsConfig)
|
||||
return grpc.NewClient(cfg.Addr, grpc.WithTransportCredentials(creds))
|
||||
}
|
||||
Reference in New Issue
Block a user