diff --git a/go.mod b/go.mod index a6cea2ad17048..c7c85ab618a2e 100644 --- a/go.mod +++ b/go.mod @@ -70,9 +70,9 @@ require ( github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20240323062759-1fd604ae58de github.com/beevik/etree v1.3.0 github.com/buildkite/bintest/v3 v3.2.0 - github.com/charmbracelet/bubbles v0.18.0 - github.com/charmbracelet/bubbletea v0.26.1 - github.com/charmbracelet/lipgloss v0.10.0 + github.com/charmbracelet/bubbles v0.21.0 + github.com/charmbracelet/bubbletea v1.3.4 + github.com/charmbracelet/lipgloss v1.1.0 github.com/coreos/go-oidc v2.2.1+incompatible // replaced github.com/coreos/go-semver v0.3.1 github.com/coreos/go-systemd/v22 v22.5.0 @@ -268,6 +268,7 @@ require ( github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect github.com/apache/arrow/go/v15 v15.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/atotto/clipboard v0.1.4 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect @@ -292,6 +293,10 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect github.com/charlievieth/strcase v0.0.5 // indirect + github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect + github.com/charmbracelet/x/ansi v0.8.0 // indirect + github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect + github.com/charmbracelet/x/term v0.2.1 // indirect github.com/cloudflare/cfssl v1.6.4 // indirect github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3 // indirect github.com/containerd/containerd v1.7.27 // indirect @@ -423,7 +428,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mdlayher/socket v0.5.1 // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/mitchellh/copystructure v1.2.0 // indirect @@ -440,8 +445,7 @@ require ( github.com/mtibben/percent v0.2.1 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/reflow v0.3.0 // indirect - github.com/muesli/termenv v0.15.2 // indirect + github.com/muesli/termenv v0.16.0 // indirect 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 @@ -473,6 +477,7 @@ require ( github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sahilm/fuzzy v0.1.1 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/sassoftware/relic v7.2.1+incompatible // indirect github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect @@ -511,6 +516,7 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect github.com/xlab/treeprint v1.2.0 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect github.com/yuin/gopher-lua v1.1.1 // indirect github.com/zeebo/errs v1.3.0 // indirect diff --git a/go.sum b/go.sum index 99031bee1ace9..bc006a9eac76f 100644 --- a/go.sum +++ b/go.sum @@ -842,6 +842,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aws/aws-sdk-go v1.44.256/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go v1.44.263/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go v1.49.12/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= @@ -945,6 +947,8 @@ github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20240323062759- github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20240323062759-1fd604ae58de/go.mod h1:pJQomIo4A5X9k8E30Q4A2BAJckIwQhC1TAFN/WXCkdk= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= +github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/beevik/etree v1.3.0 h1:hQTc+pylzIKDb23yYprodCWWTt+ojFfUZyzU09a/hmU= github.com/beevik/etree v1.3.0/go.mod h1:aiPf89g/1k3AShMVAzriilpcE4R/Vuor90y83zVZWFc= @@ -1003,12 +1007,22 @@ github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNS github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/charlievieth/strcase v0.0.5 h1:gV4iXVyD6eI5KdfOV+/vIVCKXZwtCWOmDMcu7Uy00Rs= github.com/charlievieth/strcase v0.0.5/go.mod h1:FIOYY1aDBMSIOFqmVomHBpoK+bteGlESRsgsdWjrhx8= -github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= -github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= -github.com/charmbracelet/bubbletea v0.26.1 h1:xujcQeF73rh4jwu3+zhfQsvV18x+7zIjlw7/CYbzGJ0= -github.com/charmbracelet/bubbletea v0.26.1/go.mod h1:FzKr7sKoO8iFVcdIBM9J0sJOcQv5nDQaYwsee3kpbgo= -github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s= -github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE= +github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs= +github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= +github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI= +github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= +github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= +github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= +github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= +github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= +github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= +github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= +github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ= +github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 h1:krfRl01rzPzxSxyLyrChD+U+MzsBXbm0OwYYB67uF+4= github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589/go.mod h1:OuDyvmLnMCwa2ep4Jkm6nyA0ocJuZlGyk2gGseVzERM= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -1856,9 +1870,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D 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.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= @@ -1935,10 +1948,8 @@ github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= -github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= -github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= -github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= +github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -2080,7 +2091,6 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5X github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -2120,6 +2130,8 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= +github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A= @@ -2289,6 +2301,8 @@ github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtX github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg= diff --git a/integrations/event-handler/go.mod b/integrations/event-handler/go.mod index 17964606a662d..84639a57bfc56 100644 --- a/integrations/event-handler/go.mod +++ b/integrations/event-handler/go.mod @@ -218,7 +218,7 @@ require ( github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/miekg/pkcs11 v1.1.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect diff --git a/integrations/event-handler/go.sum b/integrations/event-handler/go.sum index e2d46ea4f0cd6..ac5ec2f0930f3 100644 --- a/integrations/event-handler/go.sum +++ b/integrations/event-handler/go.sum @@ -664,8 +664,8 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= diff --git a/integrations/terraform/go.mod b/integrations/terraform/go.mod index e77108ad763a3..1093c149d6c03 100644 --- a/integrations/terraform/go.mod +++ b/integrations/terraform/go.mod @@ -259,7 +259,7 @@ require ( github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/miekg/pkcs11 v1.1.1 // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect diff --git a/integrations/terraform/go.sum b/integrations/terraform/go.sum index ab7d7cfde700c..efc37828f30df 100644 --- a/integrations/terraform/go.sum +++ b/integrations/terraform/go.sum @@ -978,8 +978,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= diff --git a/tool/tctl/common/top/box.go b/tool/tctl/common/top/box.go index c4a803fe3395b..1526d556e8085 100644 --- a/tool/tctl/common/top/box.go +++ b/tool/tctl/common/top/box.go @@ -32,6 +32,10 @@ import ( // │ Hello │ // ╰────────────────────╯ func boxedView(title string, content string, width int) string { + return boxedViewWithStyle(title, content, width, lipgloss.NewStyle().Faint(true)) +} + +func boxedViewWithStyle(title string, content string, width int, style lipgloss.Style) string { rounderBorder := lipgloss.RoundedBorder() const borderCorners = 2 @@ -55,11 +59,10 @@ func boxedView(title string, content string, width int) string { rounderBorder.Top = "" rounderBorder.TopRight = "" - contentStyle := lipgloss.NewStyle(). + contentStyle := style. BorderStyle(rounderBorder). PaddingLeft(1). PaddingRight(1). - Faint(true). Width(width) return renderedTitle + contentStyle.Render(content) diff --git a/tool/tctl/common/top/keys.go b/tool/tctl/common/top/keys.go new file mode 100644 index 0000000000000..8c27571a7ca92 --- /dev/null +++ b/tool/tctl/common/top/keys.go @@ -0,0 +1,104 @@ +// 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 . + +package top + +import "github.com/charmbracelet/bubbles/key" + +type keyMap struct { + Quit key.Binding + + // Movement + Right key.Binding + Left key.Binding + Up key.Binding + Down key.Binding + Filter key.Binding + + // Tabs + Common key.Binding + Backend key.Binding + Cache key.Binding + Watcher key.Binding + Audit key.Binding + Raw key.Binding +} + +// newDefaultKeymap returns default keybinds used by top +func newDefaultKeymap() *keyMap { + return &keyMap{ + Quit: key.NewBinding( + key.WithKeys("q", "ctrl+c"), + key.WithHelp("q", "quit"), + ), + Left: key.NewBinding( + key.WithKeys("left", "esc", "shift+tab", "h"), + key.WithHelp("←", "previous"), + ), + Right: key.NewBinding( + key.WithKeys("right", "tab", "l"), + key.WithHelp("→", "next"), + ), + Up: key.NewBinding( + key.WithKeys("up", "k"), + key.WithHelp("↑", "up"), + ), + Down: key.NewBinding( + key.WithKeys("down", "j"), + key.WithHelp("↓", "down"), + ), + Common: key.NewBinding( + key.WithKeys("1"), + key.WithHelp("1", "common"), + ), + Backend: key.NewBinding( + key.WithKeys("2"), + key.WithHelp("2", "backend"), + ), + Cache: key.NewBinding( + key.WithKeys("3"), + key.WithHelp("3", "cache"), + ), + Watcher: key.NewBinding( + key.WithKeys("4"), + key.WithHelp("4", "watcher"), + ), + Audit: key.NewBinding( + key.WithKeys("5"), + key.WithHelp("5", "audit"), + ), + Raw: key.NewBinding( + key.WithKeys("6"), + key.WithHelp("6", "raw"), + ), + + Filter: key.NewBinding( + key.WithKeys("/"), + key.WithHelp("/", "filter"), + ), + } +} + +func (k keyMap) ShortHelp() []key.Binding { + return []key.Binding{k.Left, k.Right, k.Quit} +} + +func (k keyMap) FullHelp() [][]key.Binding { + return [][]key.Binding{ + {k.Left, k.Right}, + {k.Quit}, + } +} diff --git a/tool/tctl/common/top/metrics.go b/tool/tctl/common/top/metrics.go new file mode 100644 index 0000000000000..e727d7233d5f9 --- /dev/null +++ b/tool/tctl/common/top/metrics.go @@ -0,0 +1,115 @@ +// 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 . + +package top + +import ( + "cmp" + "fmt" + "slices" + + "github.com/charmbracelet/bubbles/list" + "github.com/dustin/go-humanize" + dto "github.com/prometheus/client_model/go" +) + +// metricItem implements [list.DefaultItem] interface to allow rendering via [list.DefaultDelegate] +type metricItem struct { + name string + value string +} + +func (m metricItem) Title() string { return m.name } +func (m metricItem) Description() string { return m.value } +func (m metricItem) FilterValue() string { return m.name } + +// getMetricLabelString formats prometheus labels into a readable string. +// +// Example for multiple labels: +// +// "{label_key1=\"value1\",labelkey2=\"value2\"}" +// +// Empty slice will return: +// +// "" +func getMetricLabelString(labels []*dto.LabelPair) string { + var out string + + if len(labels) == 0 { + return "" + } + + for i, label := range labels { + if i > 0 { + out += "," + } + out += label.GetName() + "=\"" + label.GetValue() + "\"" + } + + return "{" + out + "}" +} + +// metricItemFromPromMetric returns a single [list.Item] from a prometheus metric family and a single metric. +func metricItemFromPromMetric(mf *dto.MetricFamily, m *dto.Metric) list.Item { + value := "n/a" + + switch mf.GetType() { + case dto.MetricType_COUNTER: + value = humanize.FormatFloat("", m.GetCounter().GetValue()) + case dto.MetricType_GAUGE: + value = humanize.FormatFloat("", m.GetGauge().GetValue()) + case dto.MetricType_SUMMARY: + value = fmt.Sprintf("count: %d sum: %s", + m.GetSummary().GetSampleCount(), + humanize.FormatFloat("", m.GetSummary().GetSampleSum()), + ) + case dto.MetricType_HISTOGRAM: + // List view does not allow enough space to make historgram buckets meaningful, only show sum and count. + value = fmt.Sprintf("count: %d sum: %s", + m.GetHistogram().GetSampleCount(), + humanize.FormatFloat("", m.GetHistogram().GetSampleSum()), + ) + } + + return metricItem{ + name: mf.GetName() + getMetricLabelString(m.GetLabel()), + value: value, + } +} + +// convertMetricsToItems converts a [metricsMsg] into a sorted slice of [list.Item] to be used in a list view. +func convertMetricsToItems(msg metricsMsg) []list.Item { + + var itemCount int + for _, mf := range msg { + itemCount += len(mf.GetMetric()) + } + + items := make([]list.Item, 0, itemCount) + + for _, mf := range msg { + for _, m := range mf.GetMetric() { + items = append(items, metricItemFromPromMetric(mf, m)) + } + } + + // Sort the item list to keep the display order consistent. + slices.SortFunc(items, func(i, j list.Item) int { + return cmp.Compare(i.FilterValue(), j.FilterValue()) + }) + + return items +} diff --git a/tool/tctl/common/top/model.go b/tool/tctl/common/top/model.go index f6a443e94e5a0..06837478a05fc 100644 --- a/tool/tctl/common/top/model.go +++ b/tool/tctl/common/top/model.go @@ -27,11 +27,13 @@ import ( "github.com/charmbracelet/bubbles/help" "github.com/charmbracelet/bubbles/key" + "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/dustin/go-humanize" "github.com/gravitational/trace" "github.com/guptarohit/asciigraph" + dto "github.com/prometheus/client_model/go" "github.com/gravitational/teleport/api/constants" ) @@ -44,84 +46,168 @@ type topModel struct { height int selected int help help.Model + keys *keyMap refreshInterval time.Duration clt MetricsClient + metricsList list.Model report *Report reportError error addr string } func newTopModel(refreshInterval time.Duration, clt MetricsClient, addr string) *topModel { + // A delegate is used to implement custom styling for list items. + delegate := list.NewDefaultDelegate() + delegate.Styles.NormalDesc = lipgloss.NewStyle().Faint(true) + delegate.Styles.NormalTitle = lipgloss.NewStyle().Faint(true) + delegate.Styles.SelectedTitle = lipgloss.NewStyle().Faint(false).Foreground(selectedColor) + delegate.Styles.SelectedDesc = lipgloss.NewStyle().Faint(false) + + metricsList := list.New(nil, delegate, 0, 0) + metricsList.SetShowTitle(false) + metricsList.SetShowFilter(true) + metricsList.SetShowStatusBar(true) + metricsList.SetShowHelp(false) + return &topModel{ help: help.New(), clt: clt, refreshInterval: refreshInterval, addr: addr, + keys: newDefaultKeymap(), + metricsList: metricsList, } } -// refresh pulls metrics from Teleport and builds -// a [Report] according to the configured refresh -// interval. -func (m *topModel) refresh() tea.Cmd { - return func() tea.Msg { - if m.report != nil { - <-time.After(m.refreshInterval) - } +// tickMsg is dispached when the refresh period expires. +type tickMsg time.Time +// metricsMsg contains new prometheus metrics. +type metricsMsg map[string]*dto.MetricFamily + +// tick provides a ticker at a specified interval. +func (m *topModel) tick() tea.Cmd { + return tea.Tick(m.refreshInterval, func(t time.Time) tea.Msg { + return tickMsg(t) + }) +} + +// fetchMetricsCmd fetches metrics from target and returns [metricsMsg]. +func (m *topModel) fetchMetricsCmd() tea.Cmd { + return func() tea.Msg { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - report, err := fetchAndGenerateReport(ctx, m.clt, m.report, m.refreshInterval) + metrics, err := m.clt.GetMetrics(ctx) if err != nil { return err } + return metricsMsg(metrics) + } +} + +// generateReportCmd returns a command to generate a [Report] from given [metricsMsg] +func (m *topModel) generateReportCmd(metrics metricsMsg) tea.Cmd { + return func() tea.Msg { + report, err := generateReport(metrics, m.report, m.refreshInterval) + if err != nil { + return err + } return report } } -// Init is a noop but required to implement [tea.Model]. +// Init kickstarts the ticker to begin polling. func (m *topModel) Init() tea.Cmd { - return m.refresh() + return func() tea.Msg { + return tickMsg(time.Now()) + } +} + +// isMetricFilterFocused checks if the user is currently on the metric pane and filtering is in progress. +func (m *topModel) isMetricFilterFocused() bool { + return m.selected == 5 && m.metricsList.FilterState() == list.Filtering } // Update processes messages in order to updated the // view based on user input and new metrics data. func (m *topModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var cmds []tea.Cmd switch msg := msg.(type) { case tea.WindowSizeMsg: h, v := lipgloss.NewStyle().GetFrameSize() m.height = msg.Height - v m.width = msg.Width - h + m.metricsList.SetSize(m.width, m.height-6 /* account for UI height */) case tea.KeyMsg: - switch msg.String() { - case "q", "ctrl+c": - return m, tea.Quit - case "1": - m.selected = 0 - case "2": - m.selected = 1 - case "3": - m.selected = 2 - case "4": - m.selected = 3 - case "5": - m.selected = 4 - case "right": - m.selected = min(m.selected+1, len(tabs)-1) - case "left": - m.selected = max(m.selected-1, 0) + if m.isMetricFilterFocused() { + // Redirect all keybinds to the list until the user is done. + var cmd tea.Cmd + m.metricsList, cmd = m.metricsList.Update(msg) + cmds = append(cmds, cmd) + } else { + switch { + case key.Matches(msg, m.keys.Quit): + return m, tea.Quit + case key.Matches(msg, m.keys.Common): + m.selected = 0 + case key.Matches(msg, m.keys.Backend): + m.selected = 1 + case key.Matches(msg, m.keys.Cache): + m.selected = 2 + case key.Matches(msg, m.keys.Watcher): + m.selected = 3 + case key.Matches(msg, m.keys.Audit): + m.selected = 4 + case key.Matches(msg, m.keys.Raw): + m.selected = 5 + case key.Matches(msg, m.keys.Right): + m.selected = (m.selected + 1) % len(tabs) + case key.Matches(msg, m.keys.Left): + m.selected = (m.selected - 1 + len(tabs)) % len(tabs) + case key.Matches(msg, m.keys.Filter), + key.Matches(msg, m.keys.Up), + key.Matches(msg, m.keys.Down): + // Only a subset of keybinds are forwarded to the list view. + var cmd tea.Cmd + m.metricsList, cmd = m.metricsList.Update(msg) + cmds = append(cmds, cmd) + } + } + case tickMsg: + cmds = append(cmds, m.tick(), m.fetchMetricsCmd()) + case metricsMsg: + cmds = append(cmds, m.generateReportCmd(msg)) + + filterValue := m.metricsList.FilterInput.Value() + selected := m.metricsList.Index() + + var cmd tea.Cmd + cmd = m.metricsList.SetItems(convertMetricsToItems(msg)) + cmds = append(cmds, cmd) + + // There is a glitch in the list.Model view when a filter has been applied + // the pagination status is broken after replacing all items. Workaround this by + // manually resetting the filter and updating the selection. + if m.metricsList.FilterState() == list.FilterApplied { + m.metricsList.SetFilterText(filterValue) + m.metricsList.Select(selected) } + case *Report: m.report = msg m.reportError = nil - return m, m.refresh() case error: m.reportError = msg - return m, m.refresh() + default: + // Forward internal messages to the metrics list. + var cmd tea.Cmd + m.metricsList, cmd = m.metricsList.Update(msg) + cmds = append(cmds, cmd) } - return m, nil + + return m, tea.Batch(cmds...) } // View formats the metrics and draws them to @@ -194,7 +280,7 @@ func (m *topModel) footerView() string { Inline(true). Width(35). Align(lipgloss.Center). - Render(m.help.View(helpKeys)) + Render(m.help.View(m.keys)) center := lipgloss.NewStyle(). Inline(true). @@ -226,6 +312,8 @@ func (m *topModel) contentView() string { return renderWatcher(m.report, m.height, m.width) case 4: return renderAudit(m.report, m.height, m.width) + case 5: + return boxedViewWithStyle("Prometheus Metrics", m.metricsList.View(), m.width, lipgloss.NewStyle()) default: return "" } @@ -558,41 +646,7 @@ func tabView(selectedTab int) string { return output } -// keyMap is used to display the help text at -// the bottom of the screen. -type keyMap struct { - quit key.Binding - right key.Binding - left key.Binding -} - -func (k keyMap) ShortHelp() []key.Binding { - return []key.Binding{k.left, k.right, k.quit} -} - -func (k keyMap) FullHelp() [][]key.Binding { - return [][]key.Binding{ - {k.left, k.right}, - {k.quit}, - } -} - var ( - helpKeys = keyMap{ - quit: key.NewBinding( - key.WithKeys("q", "esc", "ctrl+c"), - key.WithHelp("q", "quit"), - ), - left: key.NewBinding( - key.WithKeys("left", "esc"), - key.WithHelp("left", "previous"), - ), - right: key.NewBinding( - key.WithKeys("right"), - key.WithHelp("right", "next"), - ), - } - statusBarStyle = lipgloss.NewStyle() separator = lipgloss.NewStyle(). @@ -604,5 +658,5 @@ var ( failedEventStyle = lipgloss.NewStyle().Foreground(lipgloss.Color(fmt.Sprintf("%d", asciigraph.Red))) trimmedEventStyle = lipgloss.NewStyle().Foreground(lipgloss.Color(fmt.Sprintf("%d", asciigraph.Goldenrod))) - tabs = []string{"Common", "Backend", "Cache", "Watcher", "Audit"} + tabs = []string{"Common", "Backend", "Cache", "Watcher", "Audit", "Raw Metrics"} ) diff --git a/tool/tctl/common/top/report.go b/tool/tctl/common/top/report.go index bec8f5ce4d56e..204d90af93c05 100644 --- a/tool/tctl/common/top/report.go +++ b/tool/tctl/common/top/report.go @@ -18,7 +18,6 @@ package top import ( "cmp" - "context" "fmt" "math" "os" @@ -26,7 +25,6 @@ import ( "strings" "time" - "github.com/gravitational/trace" "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" @@ -371,14 +369,6 @@ type Bucket struct { UpperBound float64 } -func fetchAndGenerateReport(ctx context.Context, client MetricsClient, prev *Report, period time.Duration) (*Report, error) { - metrics, err := client.GetMetrics(ctx) - if err != nil { - return nil, trace.Wrap(err) - } - return generateReport(metrics, prev, period) -} - func generateReport(metrics map[string]*dto.MetricFamily, prev *Report, period time.Duration) (*Report, error) { // format top backend requests hostname, _ := os.Hostname()