Skip to content

Commit

Permalink
rpk: add rpk debug remote bundle
Browse files Browse the repository at this point in the history
Fixes DEVEX-44

This commit introduces the rpk debug remote bundle
command, which allows the user to request a debug
bundle using the Admin API.
  • Loading branch information
r-vasquez committed Nov 1, 2024
1 parent 785fb1e commit 165b124
Show file tree
Hide file tree
Showing 11 changed files with 797 additions and 1 deletion.
1 change: 1 addition & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ use_repo(
"com_github_docker_go_connections",
"com_github_docker_go_units",
"com_github_fatih_color",
"com_github_google_uuid",
"com_github_hamba_avro",
"com_github_hamba_avro_v2",
"com_github_hashicorp_go_multierror",
Expand Down
2 changes: 1 addition & 1 deletion src/go/rpk/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ require (
github.com/docker/go-connections v0.5.0
github.com/docker/go-units v0.5.0
github.com/fatih/color v1.17.0
github.com/google/uuid v1.6.0
github.com/hamba/avro/v2 v2.25.2
github.com/hashicorp/go-multierror v1.1.1
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
Expand Down Expand Up @@ -94,7 +95,6 @@ require (
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
Expand Down
1 change: 1 addition & 0 deletions src/go/rpk/pkg/cli/debug/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//src/go/rpk/pkg/cli/debug/bundle",
"//src/go/rpk/pkg/cli/debug/remotebundle",
"//src/go/rpk/pkg/config",
"@com_github_spf13_afero//:afero",
"@com_github_spf13_cobra//:cobra",
Expand Down
9 changes: 9 additions & 0 deletions src/go/rpk/pkg/cli/debug/common/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
load("@rules_go//go:def.bzl", "go_library")

go_library(
name = "common",
srcs = ["common.go"],
importpath = "github.com/redpanda-data/redpanda/src/go/rpk/pkg/cli/debug/common",
visibility = ["//visibility:public"],
deps = ["@com_github_spf13_pflag//:pflag"],
)
2 changes: 2 additions & 0 deletions src/go/rpk/pkg/cli/debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package debug

import (
"github.com/redpanda-data/redpanda/src/go/rpk/pkg/cli/debug/bundle"
"github.com/redpanda-data/redpanda/src/go/rpk/pkg/cli/debug/remotebundle"
"github.com/redpanda-data/redpanda/src/go/rpk/pkg/config"
"github.com/spf13/afero"
"github.com/spf13/cobra"
Expand All @@ -25,6 +26,7 @@ func NewCommand(fs afero.Fs, p *config.Params) *cobra.Command {
cmd.AddCommand(
bundle.NewCommand(fs, p),
NewInfoCommand(),
remotebundle.NewCommand(fs, p),
)

return cmd
Expand Down
27 changes: 27 additions & 0 deletions src/go/rpk/pkg/cli/debug/remotebundle/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
load("@rules_go//go:def.bzl", "go_library")

go_library(
name = "remotebundle",
srcs = [
"cancel.go",
"download.go",
"remote.go",
"start.go",
"status.go",
],
importpath = "github.com/redpanda-data/redpanda/src/go/rpk/pkg/cli/debug/remotebundle",
visibility = ["//visibility:public"],
deps = [
"//src/go/rpk/pkg/adminapi",
"//src/go/rpk/pkg/cli/debug/common",
"//src/go/rpk/pkg/config",
"//src/go/rpk/pkg/out",
"@com_github_docker_go_units//:go-units",
"@com_github_google_uuid//:uuid",
"@com_github_redpanda_data_common_go_rpadmin//:rpadmin",
"@com_github_spf13_afero//:afero",
"@com_github_spf13_cobra//:cobra",
"@io_k8s_apimachinery//pkg/labels",
"@org_golang_x_sync//errgroup",
],
)
121 changes: 121 additions & 0 deletions src/go/rpk/pkg/cli/debug/remotebundle/cancel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright 2024 Redpanda Data, Inc.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.md
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0

package remotebundle

import (
"fmt"
"os"
"sync"

"github.com/redpanda-data/redpanda/src/go/rpk/pkg/config"
"github.com/redpanda-data/redpanda/src/go/rpk/pkg/out"
"github.com/spf13/afero"
"github.com/spf13/cobra"
)

type cancelResponse struct {
Broker string
Canceled bool
Error string
}

func newCancelCommand(fs afero.Fs, p *config.Params) *cobra.Command {
var noConfirm bool
var jobID string
cmd := &cobra.Command{
Use: "cancel",
Short: "Cancel a remote bundle execution",
Long: `Cancel a remote bundle execution.
This command will cancel the debug collection process in a remote cluster that
you have configured either in your rpk profile, flags or environment variables.
Alternatively, you may provide a --job-id to only cancel the debug bundle with
the given Job ID.
The flag '--no-confirm' can be used to avoid the confirmation prompt.
`,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, _ []string) {
p, err := p.LoadVirtualProfile(fs)
out.MaybeDie(err, "rpk unable to load config: %v", err)
config.CheckExitCloudAdmin(p)

status, anyErr, _, cache := executeBundleStatus(cmd.Context(), fs, p)
if jobID != "" {
status = filterStatusByJobID(status, jobID)
}
if !noConfirm {
printTextBundleStatus(status, anyErr)
confirmed, err := out.Confirm("Confirm debug bundle cancel from these brokers?")
out.MaybeDie(err, "unable to confirm cancel: %v; if you want to select a single broker, use -X admin.hosts=<brokers,to,cancel>", err)
if !confirmed {
out.Exit("operation canceled; if you want to select a single broker, use -X admin.hosts=<brokers,to,cancel>")
}
}
var (
wg sync.WaitGroup
rwMu sync.RWMutex // read from cache.
mu sync.Mutex // write to status.
response []cancelResponse
anyCancelErr bool
)
updateStatus := func(resp cancelResponse, err error) {
mu.Lock()
defer mu.Unlock()
anyCancelErr = anyCancelErr || err != nil
response = append(response, resp)
}
for _, s := range status {
wg.Add(1)
go func(addr, jobID string) {
defer wg.Done()
rwMu.RLock()
cl := cache[addr]
rwMu.RUnlock()
resp := cancelResponse{Broker: addr}
err := cl.CancelDebugBundleProcess(cmd.Context(), jobID)
if err != nil {
resp.Error = fmt.Sprintf("unable to cancel debug bundle: %s", tryMessageFromErr(err))
updateStatus(resp, err)
return
}
resp.Canceled = true
updateStatus(resp, nil)
}(s.Broker, s.JobID)
}
wg.Wait()
headers := []string{"broker", "canceled"}
if anyCancelErr {
headers = append(headers, "error")
defer os.Exit(1)
}

tw := out.NewTable(headers...)
defer tw.Flush()
for _, row := range response {
tw.PrintStructFields(row)
}
},
}
cmd.Flags().StringVar(&jobID, "job-id", "", "ID of the job to cancel the debug bundle")
cmd.Flags().BoolVar(&noConfirm, "no-confirm", false, "Disable confirmation prompt")
return cmd
}

func filterStatusByJobID(status []statusResponse, jobID string) []statusResponse {
var filtered []statusResponse
for _, s := range status {
if s.JobID == jobID {
filtered = append(filtered, s)
}
}
return filtered
}
Loading

0 comments on commit 165b124

Please sign in to comment.