Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v17] Switch tctl top to use charmbracelet/bubbletea #51559

Merged
merged 1 commit into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ require (
github.com/fsouza/fake-gcs-server v1.49.3
github.com/fxamacker/cbor/v2 v2.7.0
github.com/ghodss/yaml v1.0.0
github.com/gizak/termui/v3 v3.1.0
github.com/go-git/go-git/v5 v5.13.1
github.com/go-jose/go-jose/v3 v3.0.3
github.com/go-ldap/ldap/v3 v3.4.8
Expand Down Expand Up @@ -459,7 +458,6 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect
github.com/nsf/termbox-go v1.1.1 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/ginkgo/v2 v2.19.0 // indirect
Expand Down
7 changes: 0 additions & 7 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1213,8 +1213,6 @@ github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uq
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gizak/termui/v3 v3.1.0 h1:ZZmVDgwHl7gR7elfKf1xc4IudXZ5qqfDh4wExk4Iajc=
github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY=
github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA=
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec=
Expand Down Expand Up @@ -1811,7 +1809,6 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
Expand Down Expand Up @@ -1843,7 +1840,6 @@ github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa1
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
Expand Down Expand Up @@ -1894,9 +1890,6 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 h1:Up6+btDp321ZG5/zdSLo48H9Iaq0UQGthrhWC6pCxzE=
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481/go.mod h1:yKZQO8QE2bHlgozqWDiRVqTFlLQSj30K/6SAK8EeYFw=
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
github.com/nsf/termbox-go v1.1.1 h1:nksUPLCb73Q++DwbYUBEglYBRPZyoXJdrj5L+TkjyZY=
github.com/nsf/termbox-go v1.1.1/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
Expand Down
3 changes: 2 additions & 1 deletion tool/tctl/common/cmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/gravitational/teleport/tool/tctl/common/accessmonitoring"
"github.com/gravitational/teleport/tool/tctl/common/loginrule"
"github.com/gravitational/teleport/tool/tctl/common/plugin"
"github.com/gravitational/teleport/tool/tctl/common/top"
"github.com/gravitational/teleport/tool/tctl/sso/configure"
"github.com/gravitational/teleport/tool/tctl/sso/tester"
)
Expand All @@ -35,7 +36,7 @@ func Commands() []CLICommand {
&TokensCommand{},
&AuthCommand{},
&StatusCommand{},
&TopCommand{},
&top.Command{},
&AccessRequestCommand{},
&AppsCommand{},
&DBCommand{},
Expand Down
66 changes: 66 additions & 0 deletions tool/tctl/common/top/box.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Teleport
// Copyright (C) 2025 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package top

import (
"strings"

"github.com/charmbracelet/lipgloss"
)

// boxedView wraps the provided content in a rounded border,
// with the title embedded in the top. For example, if the
// content was \t\t\tHello and the title was Some Heading the
// returned content would be:
//
// ╭Some Heading────────╮
// │ │
// │ Hello │
// ╰────────────────────╯
func boxedView(title string, content string, width int) string {
rounderBorder := lipgloss.RoundedBorder()

const borderCorners = 2
width = width - borderCorners
availableSpace := width - lipgloss.Width(title)

var filler string
if availableSpace > 0 {
filler = strings.Repeat(rounderBorder.Top, availableSpace)
}

titleContent := lipgloss.NewStyle().
Foreground(selectedColor).
Render(title)

renderedTitle := rounderBorder.TopLeft + titleContent + filler + rounderBorder.TopRight

// empty out the top border since it
// is already manually applied to the title.
rounderBorder.TopLeft = ""
rounderBorder.Top = ""
rounderBorder.TopRight = ""

contentStyle := lipgloss.NewStyle().
BorderStyle(rounderBorder).
PaddingLeft(1).
PaddingRight(1).
Faint(true).
Width(width)

return renderedTitle + contentStyle.Render(content)
}
70 changes: 70 additions & 0 deletions tool/tctl/common/top/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Teleport
// Copyright (C) 2025 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package top

import (
"context"
"time"

"github.com/alecthomas/kingpin/v2"
tea "github.com/charmbracelet/bubbletea"
"github.com/gravitational/roundtrip"
"github.com/gravitational/trace"

"github.com/gravitational/teleport/lib/service/servicecfg"
commonclient "github.com/gravitational/teleport/tool/tctl/common/client"
tctlcfg "github.com/gravitational/teleport/tool/tctl/common/config"
)

// Command is a debug command that consumes the
// Teleport /metrics endpoint and displays diagnostic
// information an easy to consume way.
type Command struct {
config *servicecfg.Config
top *kingpin.CmdClause
diagURL string
refreshPeriod time.Duration
}

// Initialize sets up the "tctl top" command.
func (c *Command) Initialize(app *kingpin.Application, _ *tctlcfg.GlobalCLIFlags, config *servicecfg.Config) {
c.config = config
c.top = app.Command("top", "Report diagnostic information.")
c.top.Arg("diag-addr", "Diagnostic HTTP URL").Default("http://127.0.0.1:3000").StringVar(&c.diagURL)
c.top.Arg("refresh", "Refresh period").Default("5s").DurationVar(&c.refreshPeriod)
}

// TryRun attempts to run subcommands.
func (c *Command) TryRun(ctx context.Context, cmd string, _ commonclient.InitFunc) (match bool, err error) {
if cmd != c.top.FullCommand() {
return false, nil
}

diagClient, err := roundtrip.NewClient(c.diagURL, "")
if err != nil {
return true, trace.Wrap(err)
}

p := tea.NewProgram(
newTopModel(c.refreshPeriod, diagClient),
tea.WithAltScreen(),
tea.WithContext(ctx),
)

_, err = p.Run()
return true, trace.Wrap(err)
}
Loading
Loading