diff --git a/go.mod b/go.mod index 289cb216..ba48b968 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,18 @@ module github.com/derailed/k9s require ( contrib.go.opencensus.io/exporter/ocagent v0.4.3 // indirect github.com/Azure/go-autorest v11.4.0+incompatible // indirect + github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e // indirect + github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 // indirect github.com/derailed/tview v0.1.4 github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect github.com/docker/distribution v2.7.1+incompatible // indirect + github.com/docker/docker v1.13.1 // indirect + github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect github.com/evanphx/json-patch v4.1.0+incompatible // indirect + github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/fatih/camelcase v1.0.0 // indirect github.com/gdamore/tcell v1.1.1 + github.com/go-openapi/spec v0.19.0 // indirect github.com/gogo/protobuf v1.1.1 // indirect github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c // indirect @@ -21,6 +27,7 @@ require ( github.com/json-iterator/go v1.1.5 // indirect github.com/kr/pretty v0.1.0 // indirect github.com/mattn/go-runewidth v0.0.4 + github.com/mitchellh/go-wordwrap v1.0.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect github.com/onsi/gomega v1.4.3 // indirect @@ -29,6 +36,8 @@ require ( github.com/petergtz/pegomock v0.0.0-20181206220228-b113d17a7e81 github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967 // indirect github.com/rs/zerolog v1.12.0 + github.com/russross/blackfriday v2.0.0+incompatible // indirect + github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/sirupsen/logrus v1.4.0 // indirect github.com/spf13/cobra v0.0.3 github.com/spf13/pflag v1.0.3 // indirect @@ -51,5 +60,5 @@ require ( k8s.io/metrics v0.0.0-20181121073115-d8618695b08f k8s.io/utils v0.0.0-20190212002617-cdba02414f76 // indirect sigs.k8s.io/structured-merge-diff v0.0.0-20190130003954-e5e029740eb8 // indirect - sigs.k8s.io/yaml v1.1.0 // indirect + sigs.k8s.io/yaml v1.1.0 ) diff --git a/go.sum b/go.sum index 70c107d9..bfd3d993 100644 --- a/go.sum +++ b/go.sum @@ -7,10 +7,18 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy git.apache.org/thrift.git v0.0.0-20181218151757-9b75e4fe745a/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/Azure/go-autorest v11.4.0+incompatible h1:z3Yr6KYqs0nhSNwqGXEBpWK977hxVqsLv2n9PVYcixY= github.com/Azure/go-autorest v11.4.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e h1:eb0Pzkt15Bm7f2FFYv7sjY7NPFi3cPkS3tv1CcrFBWA= +github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= +github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/census-instrumentation/opencensus-proto v0.1.0-0.20181214143942-ba49f56771b8 h1:gUqsFVdUKoRHNg8fkFd8gB5OOEa/g5EwlAHznb4zjbI= github.com/census-instrumentation/opencensus-proto v0.1.0-0.20181214143942-ba49f56771b8/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 h1:HD4PLRzjuCVW79mQ0/pdsalOLHJ+FaEoqJLxfltpb2U= +github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -22,8 +30,14 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo= +github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s= +github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/evanphx/json-patch v4.1.0+incompatible h1:K1MDoo4AZ4wU0GIU/fPmtZg7VpzLjCxu+UwBD1FvwOc= github.com/evanphx/json-patch v4.1.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= @@ -33,6 +47,14 @@ github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo github.com/gdamore/tcell v1.1.1 h1:U73YL+jMem2XfhvaIUfPO6MpJawaG92B2funXVb9qLs= github.com/gdamore/tcell v1.1.1/go.mod h1:K1udHkiR3cOtlpKG5tZPD5XxrF7v2y7lDq7Whcj+xkQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonreference v0.17.0 h1:yJW3HCkTHg7NOA+gZ83IPHzUSnUzGXhGmsdiCcMexbA= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4= +github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= @@ -77,10 +99,14 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lucasb-eyer/go-colorful v0.0.0-20181028223441-12d3b2882a08 h1:5MnxBC15uMxFv5FY/J/8vzyaBiArCOkMdFT9Jsw78iY= github.com/lucasb-eyer/go-colorful v0.0.0-20181028223441-12d3b2882a08/go.mod h1:NXg0ArsFk0Y01623LgUqoqcouGDB+PwCCQlrwrG6xJ4= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= @@ -117,6 +143,10 @@ github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967 h1:x7xEyJDP7Hv3LVgvWhz github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/rs/zerolog v1.12.0 h1:aqZ1XRadoS8IBknR5IDFvGzbHly1X9ApIqOroooQF/c= github.com/rs/zerolog v1.12.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/russross/blackfriday v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk= +github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.0 h1:yKenngtzGh+cUSSh6GWbxW2abRqhYUSR/t/6+2QqNvE= github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -139,6 +169,7 @@ golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181217023233-e147a9138326 h1:iCzOf0xz39Tstp+Tu/WwyGjUXCk34QhQORRxBeXXTA4= diff --git a/internal/views/details.go b/internal/views/details.go index 0f733138..07977962 100644 --- a/internal/views/details.go +++ b/internal/views/details.go @@ -26,11 +26,6 @@ type detailsView struct { numSelections int } -var ( - regionRX = regexp.MustCompile(`\["([a-zA-Z0-9_,;: \-\.]*)"\]`) - escapeRX = regexp.MustCompile(`\[([a-zA-Z0-9_,;: \-\."#]+)\[(\[*)\]`) -) - func newDetailsView(app *appView, backFn actionHandler) *detailsView { v := detailsView{TextView: tview.NewTextView(), app: app, actions: make(keyActions)} { @@ -53,8 +48,6 @@ func newDetailsView(app *appView, backFn actionHandler) *detailsView { }) } - // v.actions[KeySlash] = newKeyAction("Search", v.activateCmd) - // v.actions[tcell.KeyEnter] = newKeyAction("Search", v.searchCmd) v.actions[tcell.KeyBackspace2] = newKeyAction("Erase", v.eraseCmd, false) v.actions[tcell.KeyBackspace] = newKeyAction("Erase", v.eraseCmd, false) v.actions[tcell.KeyDelete] = newKeyAction("Erase", v.eraseCmd, false) @@ -207,6 +200,11 @@ func (v *detailsView) setTitle(t string) { v.SetTitle(title) } +var ( + regionRX = regexp.MustCompile(`\["([a-zA-Z0-9_,;: \-\.]*)"\]`) + escapeRX = regexp.MustCompile(`\[([a-zA-Z0-9_,;: \-\."#]+)\[(\[*)\]`) +) + func (v *detailsView) decorateLines(buff, q string) string { rx := regexp.MustCompile(`(?i)` + q) lines := strings.Split(buff, "\n") diff --git a/internal/views/log.go b/internal/views/log.go index c526acc0..00694bff 100644 --- a/internal/views/log.go +++ b/internal/views/log.go @@ -27,7 +27,7 @@ func newLogView(title string, parent loggable) *logView { } func (l *logView) logLine(line string) { - fmt.Fprintln(l.ansiWriter, line) + fmt.Fprintln(l.ansiWriter, tview.Escape(line)) } func (l *logView) log(lines fmt.Stringer) { diff --git a/internal/views/log_test.go b/internal/views/log_test.go new file mode 100644 index 00000000..36aea07a --- /dev/null +++ b/internal/views/log_test.go @@ -0,0 +1,24 @@ +package views + +import ( + "bytes" + "fmt" + "testing" + + "github.com/derailed/tview" + "github.com/stretchr/testify/assert" +) + +func TestAnsi(t *testing.T) { + buff := bytes.NewBufferString("") + w := tview.ANSIWriter(buff) + fmt.Fprintf(w, "[YELLOW] ok") + assert.Equal(t, "[YELLOW] ok", buff.String()) + + v := tview.NewTextView() + v.SetDynamicColors(true) + aw := tview.ANSIWriter(v) + s := "[2019-03-27T15:05:15,246][INFO ][o.e.c.r.a.AllocationService] [es-0] Cluster health status changed from [YELLOW] to [GREEN] (reason: [shards started [[.monitoring-es-6-2019.03.27][0]]" + fmt.Fprintf(aw, s) + assert.Equal(t, s+"\n", v.GetText(false)) +} diff --git a/internal/views/registrar.go b/internal/views/registrar.go index 69621669..1752562e 100644 --- a/internal/views/registrar.go +++ b/internal/views/registrar.go @@ -270,7 +270,7 @@ func resourceViews(c k8s.Connection) map[string]resCmd { "sec": { title: "Secrets", api: "", - viewFn: newResourceView, + viewFn: newSecretView, listFn: resource.NewSecretList, }, "sts": { diff --git a/internal/views/resource.go b/internal/views/resource.go index d9bcb21f..6d844ea7 100644 --- a/internal/views/resource.go +++ b/internal/views/resource.go @@ -190,7 +190,7 @@ func (v *resourceView) deleteCmd(evt *tcell.EventKey) *tcell.EventKey { func (v *resourceView) defaultEnter(app *appView, ns, resource, selection string) { sel := v.getSelectedItem() - raw, err := v.list.Resource().Describe(v.title, sel, v.app.flags) + yaml, err := v.list.Resource().Describe(v.title, sel, v.app.flags) if err != nil { v.app.flash(flashErr, err.Error()) log.Warn().Msgf("Describe %v", err.Error()) @@ -202,7 +202,7 @@ func (v *resourceView) defaultEnter(app *appView, ns, resource, selection string details.setCategory("Describe") details.setTitle(sel) details.SetTextColor(tcell.ColorAqua) - details.SetText(string(raw)) + details.SetText(colorizeYAML(yaml)) details.ScrollToBeginning() } v.switchPage("details") @@ -212,7 +212,6 @@ func (v *resourceView) describeCmd(evt *tcell.EventKey) *tcell.EventKey { if !v.rowSelected() { return evt } - v.defaultEnter(v.app, v.list.GetNamespace(), v.list.GetName(), v.selectedItem) return nil @@ -234,7 +233,7 @@ func (v *resourceView) viewCmd(evt *tcell.EventKey) *tcell.EventKey { details.setCategory("View") details.setTitle(sel) details.SetTextColor(tcell.ColorMediumAquamarine) - details.SetText(string(raw)) + details.SetText(colorizeYAML(raw)) details.ScrollToBeginning() } v.switchPage("details") @@ -345,9 +344,12 @@ func (v *resourceView) switchPage(p string) { { v.SwitchToPage(p) v.selectedNS = v.list.GetNamespace() - h := v.GetPrimitive(p).(hinter) - v.app.setHints(h.hints()) - v.app.SetFocus(v.CurrentPage().Item) + if h, ok := v.GetPrimitive(p).(hinter); ok { + v.app.setHints(h.hints()) + v.app.SetFocus(v.CurrentPage().Item) + } else { + log.Error().Msgf("Hinter not implemented on %s", p) + } } v.update.Unlock() } diff --git a/internal/views/secret.go b/internal/views/secret.go new file mode 100644 index 00000000..758532cc --- /dev/null +++ b/internal/views/secret.go @@ -0,0 +1,65 @@ +package views + +import ( + "sigs.k8s.io/yaml" + + "github.com/derailed/k9s/internal/resource" + "github.com/gdamore/tcell" + "github.com/rs/zerolog/log" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type secretView struct { + *resourceView +} + +func newSecretView(t string, app *appView, list resource.List) resourceViewer { + v := secretView{newResourceView(t, app, list).(*resourceView)} + { + v.extraActionsFn = v.extraActions + v.switchPage("secret") + } + + return &v +} + +func (v *secretView) extraActions(aa keyActions) { + aa[tcell.KeyCtrlX] = newKeyAction("Decode", v.decodeCmd, true) +} + +func (v *secretView) decodeCmd(evt *tcell.EventKey) *tcell.EventKey { + if !v.rowSelected() { + return evt + } + + sel := v.getSelectedItem() + ns, n := namespaced(sel) + sec, err := v.app.conn().DialOrDie().CoreV1().Secrets(ns).Get(n, metav1.GetOptions{}) + if err != nil { + v.app.flash(flashErr, "Unable to retrieve secret", sel) + return evt + } + + d := make(map[string]string, len(sec.Data)) + for k, val := range sec.Data { + d[k] = string(val) + } + raw, err := yaml.Marshal(d) + if err != nil { + v.app.flash(flashErr, "Error decoding secret for `", sel) + log.Error().Err(err).Msgf("Marshal error getting secret %s", sel) + return nil + } + + details := v.GetPrimitive("details").(*detailsView) + { + details.setCategory("Decoder") + details.setTitle(sel) + details.SetTextColor(tcell.ColorMediumAquamarine) + details.SetText(colorizeYAML(string(raw))) + details.ScrollToBeginning() + } + v.switchPage("details") + + return nil +} diff --git a/internal/views/sorters.go b/internal/views/sorter.go similarity index 71% rename from internal/views/sorters.go rename to internal/views/sorter.go index b628b206..c917eb65 100644 --- a/internal/views/sorters.go +++ b/internal/views/sorter.go @@ -4,8 +4,10 @@ import ( "regexp" "strconv" "strings" + "time" "github.com/derailed/k9s/internal/resource" + res "k8s.io/apimachinery/pkg/api/resource" ) type rowSorter struct { @@ -53,6 +55,10 @@ func less(asc bool, c1, c2 string) bool { return o } + if o, ok := isDurationSort(asc, c1, c2); ok { + return o + } + if o, ok := isIntegerSort(asc, c1, c2); ok { return o } @@ -64,18 +70,30 @@ func less(asc bool, c1, c2 string) bool { return c > 0 } -func isMetricSort(asc bool, c1, c2 string) (bool, bool) { - m1, ok := isMetric(c1) - if !ok { +func isDurationSort(asc bool, c1, c2 string) (bool, bool) { + d1, ok1 := isDuration(c1) + d2, ok2 := isDuration(c2) + if !ok1 || !ok2 { return false, false } - m2, _ := isMetric(c2) - i1, _ := strconv.Atoi(m1) - i2, _ := strconv.Atoi(m2) + if asc { - return i1 < i2, true + return d1 < d2, true } - return i1 > i2, true + return d1 > d2, true +} + +func isMetricSort(asc bool, c1, c2 string) (bool, bool) { + q1, err1 := res.ParseQuantity(c1) + q2, err2 := res.ParseQuantity(c2) + if err1 != nil || err2 != nil { + return false, false + } + + if asc { + return q1.Cmp(q2) <= 0, true + } + return q1.Cmp(q2) > 0, true } func isIntegerSort(asc bool, c1, c2 string) (bool, bool) { @@ -98,3 +116,11 @@ func isMetric(s string) (string, bool) { } return s, false } + +func isDuration(s string) (time.Duration, bool) { + d, err := time.ParseDuration(s) + if err != nil { + return d, false + } + return d, true +} diff --git a/internal/views/sorter_test.go b/internal/views/sorter_test.go index 0a262f6b..b87a5558 100644 --- a/internal/views/sorter_test.go +++ b/internal/views/sorter_test.go @@ -22,6 +22,8 @@ func TestGroupSort(t *testing.T) { {false, []string{"100Mi", "10Mi"}, []string{"100Mi", "10Mi"}}, {true, []string{"xyz", "abc"}, []string{"abc", "xyz"}}, {false, []string{"xyz", "abc"}, []string{"xyz", "abc"}}, + {true, []string{"2m30s", "1m10s"}, []string{"1m10s", "2m30s"}}, + {true, []string{"3d", "1d"}, []string{"1d", "3d"}}, } for _, u := range uu { @@ -33,9 +35,8 @@ func TestGroupSort(t *testing.T) { func TestRowSort(t *testing.T) { uu := []struct { - order bool - rows resource.Rows - expect resource.Rows + order bool + rows, expect resource.Rows }{ { true, diff --git a/internal/views/yaml.go b/internal/views/yaml.go new file mode 100644 index 00000000..6337c12a --- /dev/null +++ b/internal/views/yaml.go @@ -0,0 +1,35 @@ +package views + +import ( + "fmt" + "regexp" + "strings" +) + +var ( + keyValRX = regexp.MustCompile(`\A(\s*)([\w|\-|\.|\s]+):\s(.+)\z`) + keyRX = regexp.MustCompile(`\A(\s*)([\w|\-|\.|\s]+):\s*\z`) +) + +func colorizeYAML(raw string) string { + lines := strings.Split(raw, "\n") + + buff := make([]string, 0, len(lines)) + for _, l := range lines { + res := keyValRX.FindStringSubmatch(l) + if len(res) == 4 { + buff = append(buff, fmt.Sprintf("%s[steelblue::b]%s[white::-]: [papayawhip::]%s", res[1], res[2], res[3])) + continue + } + + res = keyRX.FindStringSubmatch(l) + if len(res) == 3 { + buff = append(buff, fmt.Sprintf("%s[steelblue::b]%s[white::-]:", res[1], res[2])) + continue + } + + buff = append(buff, fmt.Sprintf("[papayawhip::]%s", l)) + } + + return strings.Join(buff, "\n") +} diff --git a/internal/views/yaml_test.go b/internal/views/yaml_test.go new file mode 100644 index 00000000..1dd54d5c --- /dev/null +++ b/internal/views/yaml_test.go @@ -0,0 +1,46 @@ +package views + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestYaml(t *testing.T) { + uu := []struct { + s, e string + }{ + { + `api: fred + version: v1`, + `[steelblue::b]api[white::-]: [papayawhip::]fred + [steelblue::b]version[white::-]: [papayawhip::]v1`, + }, + { + `api: + version: v1`, + `[steelblue::b]api[white::-]: + [steelblue::b]version[white::-]: [papayawhip::]v1`, + }, + { + " fred:blee", + "[papayawhip::] fred:blee", + }, + { + "fred blee: blee", + "[steelblue::b]fred blee[white::-]: [papayawhip::]blee", + }, + { + "Node-Selectors: ", + "[steelblue::b]Node-Selectors[white::-]: [papayawhip::] ", + }, + { + "fred.blee: ", + "[steelblue::b]fred.blee[white::-]: [papayawhip::] ", + }, + } + + for _, u := range uu { + assert.Equal(t, u.e, colorizeYAML(u.s)) + } +}