From f2c372259eed58d32e8e4f462918ba7c527596d4 Mon Sep 17 00:00:00 2001 From: mgoulish Date: Fri, 8 May 2026 02:55:16 -0400 Subject: [PATCH] Add a new debug command: "skupper debug mentat DEBUG_DUMP_FILE" Integrates the mentat-go-2 library for connectivity analysis --- go.mod | 8 ++- go.sum | 2 + internal/cmd/skupper/debug/debug.go | 20 ++++++ internal/cmd/skupper/debug/kube/mentat.go | 68 ++++++++++++++++++++ internal/cmd/skupper/debug/nonkube/mentat.go | 68 ++++++++++++++++++++ 5 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 internal/cmd/skupper/debug/kube/mentat.go create mode 100644 internal/cmd/skupper/debug/nonkube/mentat.go diff --git a/go.mod b/go.mod index b3af25de2..2cd55fe54 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/skupperproject/skupper -go 1.25.0 +go 1.25.9 require ( github.com/Azure/go-amqp v1.0.5 @@ -37,6 +37,8 @@ require ( sigs.k8s.io/yaml v1.4.0 ) +require github.com/goccy/go-yaml v1.19.2 // indirect + replace github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2 require ( @@ -75,6 +77,7 @@ require ( github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mgoulish/mentat-go-2 v0.0.0 github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/moby/spdystream v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -114,3 +117,6 @@ require ( sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect ) + +// for local development only: +//replace github.com/mgoulish/mentat-go-2 => /home/mick/mentat-go-2 diff --git a/go.sum b/go.sum index 739107298..5fde45ea2 100644 --- a/go.sum +++ b/go.sum @@ -192,6 +192,8 @@ github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWe github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= diff --git a/internal/cmd/skupper/debug/debug.go b/internal/cmd/skupper/debug/debug.go index 146192041..a4c60bbc3 100644 --- a/internal/cmd/skupper/debug/debug.go +++ b/internal/cmd/skupper/debug/debug.go @@ -18,6 +18,7 @@ func NewCmdDebug() *cobra.Command { } platform := common.Platform(config.GetPlatform()) cmd.AddCommand(CmdDebugDumpFactory(platform)) + cmd.AddCommand(CmdDebugMentatFactory(platform)) return cmd } @@ -46,3 +47,22 @@ func CmdDebugDumpFactory(configuredPlatform common.Platform) *cobra.Command { return cmd } + +func CmdDebugMentatFactory(configuredPlatform common.Platform) *cobra.Command { + kubeCommand := kube.NewCmdDebugMentat() + nonKubeCommand := nonkube.NewCmdDebugMentat() + + desc := common.SkupperCmdDescription{ + Use: "mentat [dumpfile]", + Short: "Analyze connectivity from a debug dump using mentat", + Long: "Processes a skupper debug dump and prints connectivity report.", + Example: "skupper debug mentat my-dump.tar.gz\nskupper debug mentat my-dump.tar.gz --time \"2025-05-11 14:30:00\"", + } + + cmd := common.ConfigureCobraCommand(configuredPlatform, desc, kubeCommand, nonKubeCommand) + + // Add the --time flag + cmd.Flags().StringP("time", "t", "", "Check connectivity at a specific time (format: YYYY-MM-DD HH:MM:SS)") + + return cmd +} diff --git a/internal/cmd/skupper/debug/kube/mentat.go b/internal/cmd/skupper/debug/kube/mentat.go new file mode 100644 index 000000000..8543fb871 --- /dev/null +++ b/internal/cmd/skupper/debug/kube/mentat.go @@ -0,0 +1,68 @@ +package kube + +import ( + "fmt" + "os" + + "github.com/mgoulish/mentat-go-2/pkg/mentat" + "github.com/spf13/cobra" +) + +type CmdDebugMentat struct { + CobraCmd *cobra.Command + dumpFile string + timeFlag string +} + +func NewCmdDebugMentat() *CmdDebugMentat { + return &CmdDebugMentat{} +} + +func (cmd *CmdDebugMentat) NewClient(cobraCommand *cobra.Command, args []string) { + cmd.CobraCmd = cobraCommand + + // Handle filename argument + if len(args) > 0 { + cmd.dumpFile = args[0] + } + + // Read the --time flag + cmd.timeFlag, _ = cobraCommand.Flags().GetString("time") +} + +func (cmd *CmdDebugMentat) ValidateInput(args []string) error { + return nil +} + +func (cmd *CmdDebugMentat) InputToOptions() {} + +func (cmd *CmdDebugMentat) Run() error { + if cmd.dumpFile == "" { + fmt.Println("No dump file specified.") + fmt.Println("Usage:") + fmt.Println(" skupper debug mentat ") + fmt.Println(" skupper debug mentat --time \"2025-05-11 14:30:00\"") + fmt.Println("\nFirst create a dump:") + fmt.Println(" skupper debug dump my-dump.tar.gz") + return nil + } + + if _, err := os.Stat(cmd.dumpFile); os.IsNotExist(err) { + return fmt.Errorf("dump file not found: %s", cmd.dumpFile) + } + + fmt.Printf("🔍 Running mentat analysis on %s...\n", cmd.dumpFile) + + // === This is the key logic you asked for === + if cmd.timeFlag != "" { + fmt.Printf("⏰ Checking connectivity at specific time: %s\n", cmd.timeFlag) + return mentat.CheckAtTime(cmd.dumpFile, cmd.timeFlag) // ← you will implement this + } + + // Default behavior + return mentat.DoEverything(cmd.dumpFile) +} + +func (cmd *CmdDebugMentat) WaitUntil() error { + return nil +} diff --git a/internal/cmd/skupper/debug/nonkube/mentat.go b/internal/cmd/skupper/debug/nonkube/mentat.go new file mode 100644 index 000000000..59099e1ba --- /dev/null +++ b/internal/cmd/skupper/debug/nonkube/mentat.go @@ -0,0 +1,68 @@ +package nonkube + +import ( + "fmt" + "os" + + "github.com/mgoulish/mentat-go-2/pkg/mentat" + "github.com/spf13/cobra" +) + +type CmdDebugMentat struct { + CobraCmd *cobra.Command + dumpFile string + timeFlag string +} + +func NewCmdDebugMentat() *CmdDebugMentat { + return &CmdDebugMentat{} +} + +func (cmd *CmdDebugMentat) NewClient(cobraCommand *cobra.Command, args []string) { + cmd.CobraCmd = cobraCommand + + // Handle filename argument + if len(args) > 0 { + cmd.dumpFile = args[0] + } + + // Read the --time flag + cmd.timeFlag, _ = cobraCommand.Flags().GetString("time") +} + +func (cmd *CmdDebugMentat) ValidateInput(args []string) error { + return nil +} + +func (cmd *CmdDebugMentat) InputToOptions() {} + +func (cmd *CmdDebugMentat) Run() error { + if cmd.dumpFile == "" { + fmt.Println("No dump file specified.") + fmt.Println("Usage:") + fmt.Println(" skupper debug mentat ") + fmt.Println(" skupper debug mentat --time \"2025-05-11 14:30:00\"") + fmt.Println("\nFirst create a dump:") + fmt.Println(" skupper debug dump my-dump.tar.gz") + return nil + } + + if _, err := os.Stat(cmd.dumpFile); os.IsNotExist(err) { + return fmt.Errorf("dump file not found: %s", cmd.dumpFile) + } + + fmt.Printf("🔍 Running mentat analysis on %s...\n", cmd.dumpFile) + + // === This is the key logic you asked for === + if cmd.timeFlag != "" { + fmt.Printf("⏰ Checking connectivity at specific time: %s\n", cmd.timeFlag) + return mentat.CheckAtTime(cmd.dumpFile, cmd.timeFlag) // ← you will implement this + } + + // Default behavior + return mentat.DoEverything(cmd.dumpFile) +} + +func (cmd *CmdDebugMentat) WaitUntil() error { + return nil +}