diff --git a/src/go/rpk/go.mod b/src/go/rpk/go.mod index bac30222d92fe..2919b0e1836da 100644 --- a/src/go/rpk/go.mod +++ b/src/go/rpk/go.mod @@ -37,7 +37,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/prometheus/client_model v0.6.1 github.com/prometheus/common v0.59.1 - github.com/redpanda-data/common-go/rpadmin v0.1.6 + github.com/redpanda-data/common-go/rpadmin v0.1.7 github.com/rs/xid v1.6.0 github.com/safchain/ethtool v0.4.1 github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 diff --git a/src/go/rpk/go.sum b/src/go/rpk/go.sum index d31083edbbd6e..0eb79316a5340 100644 --- a/src/go/rpk/go.sum +++ b/src/go/rpk/go.sum @@ -208,8 +208,8 @@ github.com/prometheus/common v0.59.1 h1:LXb1quJHWm1P6wq/U824uxYi4Sg0oGvNeUm1z5dJ github.com/prometheus/common v0.59.1/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0= github.com/redpanda-data/common-go/net v0.1.0 h1:JnJioRJuL961r1QXiJQ1tW9+yEaJfu8FpXnUmvQbwNM= github.com/redpanda-data/common-go/net v0.1.0/go.mod h1:iOdNkjxM7a1T8F3cYHTaKIPFCHzzp/ia6TN+Z+7Tt5w= -github.com/redpanda-data/common-go/rpadmin v0.1.6 h1:OpKO0h5unnZq8n1RJ3G6Hr8HT8ff/Ma0or5X1BNIMcM= -github.com/redpanda-data/common-go/rpadmin v0.1.6/go.mod h1:I7umqhnMhIOSEnIA3fvLtdQU7QO/SbWGCwFfFDs3De4= +github.com/redpanda-data/common-go/rpadmin v0.1.7 h1:zj3HiZuvAdOvOdi7oyTn4FYOPulO7BhvhLx9acOy810= +github.com/redpanda-data/common-go/rpadmin v0.1.7/go.mod h1:I7umqhnMhIOSEnIA3fvLtdQU7QO/SbWGCwFfFDs3De4= github.com/redpanda-data/go-avro/v2 v2.0.0-20240405204525-77b1144dc525 h1:vskZrV6q8W8flL0Ud23AJUYAd8ZgTadO45+loFnG2G0= github.com/redpanda-data/go-avro/v2 v2.0.0-20240405204525-77b1144dc525/go.mod h1:3YqAM7pgS5vW/EH7naCjFqnAajSgi0f0CfMe1HGhLxQ= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= diff --git a/src/go/rpk/pkg/cli/redpanda/admin/brokers/list.go b/src/go/rpk/pkg/cli/redpanda/admin/brokers/list.go index e58efc5ee0c43..0b0cf6367bb09 100644 --- a/src/go/rpk/pkg/cli/redpanda/admin/brokers/list.go +++ b/src/go/rpk/pkg/cli/redpanda/admin/brokers/list.go @@ -1,20 +1,44 @@ package brokers import ( + "fmt" + "strings" + "github.com/redpanda-data/common-go/rpadmin" "github.com/redpanda-data/redpanda/src/go/rpk/pkg/adminapi" "github.com/redpanda-data/redpanda/src/go/rpk/pkg/config" "github.com/redpanda-data/redpanda/src/go/rpk/pkg/out" + "github.com/redpanda-data/redpanda/src/go/rpk/pkg/redpanda" "github.com/spf13/afero" "github.com/spf13/cobra" ) func newListCommand(fs afero.Fs, p *config.Params) *cobra.Command { - return &cobra.Command{ + var decom bool + cmd := &cobra.Command{ Use: "list", Aliases: []string{"ls"}, Short: "List the brokers in your cluster", - Args: cobra.ExactArgs(0), + Long: `List the brokers in your cluster. + +This command lists all brokers in the cluster, active and inactive, unless they have been decommissioned. +Using the "--include-decommissioned" flag, it lists decommissioned brokers with associated UUIDs too. + +The output table contains the following columns: + +ID Node ID, an exclusive identifier for a broker +HOST Internal RPC address for communication between brokers +PORT Internal RPC port for communication between brokers +RACK Assigned rack ID +CORES Number of cores (shards) on a broker +MEMBERSHIP Whether a broker is decommissioned or not +IS-ALIVE Whether a broker is alive or offline +VERSION Broker version +UUID (Optional) Additional exclusive identifier for a broker + +NOTE: The UUID column is hidden when the cluster doesn't expose the UUID in the Admin API, or the API call fails to retrieve UUIDs. +`, + Args: cobra.ExactArgs(0), Run: func(cmd *cobra.Command, _ []string) { p, err := p.LoadVirtualProfile(fs) out.MaybeDie(err, "rpk unable to load config: %v", err) @@ -26,27 +50,82 @@ func newListCommand(fs afero.Fs, p *config.Params) *cobra.Command { bs, err := cl.Brokers(cmd.Context()) out.MaybeDie(err, "unable to request brokers: %v", err) - headers := []string{"Node-ID", "Num-Cores", "Membership-Status"} + headers := []string{"ID", "Host", "Port", "Rack", "Cores", "Membership", "Is-Alive", "Version"} args := func(b *rpadmin.Broker) []interface{} { - ret := []interface{}{b.NodeID, b.NumCores, b.MembershipStatus} + version, _ := redpanda.VersionFromString(b.Version) + ret := []interface{}{b.NodeID, b.InternalRPCAddress, b.InternalRPCPort, formatOutput(b.Rack), b.NumCores, b.MembershipStatus, *b.IsAlive, formatOutput(version.String())} return ret } - for _, b := range bs { - if b.IsAlive != nil { - headers = append(headers, "Is-Alive", "Broker-Version") - orig := args - args = func(b *rpadmin.Broker) []interface{} { - return append(orig(b), *b.IsAlive, b.Version) - } - break + + idUUIDMapping, err := cl.GetBrokerUuids(cmd.Context()) + if err != nil { + fmt.Printf("unable to retrieve node UUIDs: %v", err) + } + if idUUIDMapping != nil { + headers = append(headers, "UUID") + org := args + args = func(b *rpadmin.Broker) []interface{} { + return append(org(b), mapUUID(b.NodeID, idUUIDMapping)) } } + tw := out.NewTable(headers...) defer tw.Flush() for _, b := range bs { tw.Print(args(&b)...) } + + if decom && idUUIDMapping != nil { + decomNodes := extractDecomNodes(bs, idUUIDMapping) + for _, b := range decomNodes { + tw.Print(b.NodeID, "-", "-", "-", "-", "-", "-", "-", b.UUID) + } + } }, } + cmd.Flags().BoolVarP(&decom, "include-decommissioned", "d", false, "If true, include decommissioned brokers") + return cmd +} + +// mapUUID returns a UUID from "mapping" which node ID maps to "nodeID". +func mapUUID(nodeID int, mapping []rpadmin.BrokerUuids) string { + var UUIDs []string + for _, node := range mapping { + if nodeID == node.NodeID { + UUIDs = append(UUIDs, node.UUID) + } + } + if len(UUIDs) == 0 { + return "-" + } + return strings.Join(UUIDs, ", ") +} + +// extractDecomNodes compares and returns nodes in brokerUUIDs (with UUIDs) not in brokers. +func extractDecomNodes(brokers []rpadmin.Broker, brokerUUIDs []rpadmin.BrokerUuids) []rpadmin.BrokerUuids { + activeNodeMap := make(map[int]bool) + + for _, br := range brokers { + activeNodeMap[br.NodeID] = true + } + + var decomNodes []rpadmin.BrokerUuids + for _, bu := range brokerUUIDs { + if !activeNodeMap[bu.NodeID] { + decomNodes = append(decomNodes, rpadmin.BrokerUuids{ + NodeID: bu.NodeID, + UUID: bu.UUID, + }) + } + } + + return decomNodes +} + +func formatOutput(s string) string { + if s == "" || s == "0.0.0" { + return "-" + } + return s }