dataraces + skins

mine
derailed 2019-04-29 11:41:37 -06:00
parent 3e36512e19
commit 445b475c21
35 changed files with 1133 additions and 260 deletions

1
.gitignore vendored
View File

@ -7,7 +7,6 @@ k9s
/k8s
dist
notes
styles
vendor
go.mod1
popeye1.go

View File

@ -67,7 +67,8 @@ snapcraft:
By leveraging a terminal UI, you can easily traverse Kubernetes resources
and view the state of you clusters in a single powerful session.
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
publish: false
# publish: false
publish: true
replacements:
amd64: 64-bit
386: 32-bit
@ -76,16 +77,15 @@ snapcraft:
bit: Arm
bitv6: Arm6
bitv7: Arm7
grade: devel
confinement: devmode
# grade: devel
# confinement: devmode
grade: stable
confinement: strict
apps:
k9s:
plugs: ["home", "network"]
# plugs: ["home", "network", "personal-files"]
# plugs:
# personal-files:
# read:
# - $HOME/.k9s
# - $HOME/.kube
# write:
# - $HOME/.k9s
# plugs: ["home", "network"]
plugs: ["home", "network", "personal-files"]
plugs:
personal-files:
read:
- $HOME/.kube

221
README.md
View File

@ -249,6 +249,227 @@ roleRef:
---
## Skins
You can style K9s based on your own sense of style and look. This is very much an experimental feature at this time, more will be added/modified if this feature has legs so thread accordingly!
Skins are YAML files, that enable a user to change K9s presentation layer. K9s skins are loaded from `$HOME/.k9s/skin.yml`. If a skin file is detected then the skin would be loaded if not the current stock skin remains in effect.
Below is a sample skin file, more skins would be available in the skins directory, just simply copy any of these in your user's home dir as `skin.yml`.
```yaml
# InTheNavy Skin...
k9s:
# General K9s styles
fgColor: dodgerblue
bgColor: white
logoColor: blue
# ClusterInfoView styles.
info:
fgColor: lightskyblue
sectionColor: steelblue
# Borders styles.
border:
fgColor: dodgerblue
focusColor: aliceblue
# MenuView attributes and styles.
menu:
fgColor: darkblue
keyColor: cornflowerblue
# Used for favorite namespaces
numKeyColor: cadetblue
# CrumbView attributes for history navigation.
crumb:
fgColor: white
bgColor: steelblue
# Active view settings
activeColor: skyblue
# TableView attributes.
table:
fgColor: blue
bgColor: darkblue
cursorColor: aqua
# Header row styles.
header:
fgColor: white
bgColor: darkblue
sorterColor: orange
# Resource status and update styles
status:
newColor: blue
modifyColor: powderblue
addColor: lightskyblue
errorColor: indianred
highlightcolor: royalblue
killColor: slategray
completedColor: gray
# Border title styles.
title:
fgColor: aqua
bgColor: white
highlightColor: skyblue
counterColor: slateblue
filterColor: slategray
# YAML info styles.
yaml:
keyColor: steelblue
colonColor: blue
valueColor: royalblue
```
Available color names are defined below:
| Color Names |
|----------------------|
| black |
| maroon |
| green |
| olive |
| navy |
| purple |
| teal |
| silver |
| gray |
| red |
| lime |
| yellow |
| blue |
| fuchsia |
| aqua |
| white |
| aliceblue |
| antiquewhite |
| aquamarine |
| azure |
| beige |
| bisque |
| blanchedalmond |
| blueviolet |
| brown |
| burlywood |
| cadetblue |
| chartreuse |
| chocolate |
| coral |
| cornflowerblue |
| cornsilk |
| crimson |
| darkblue |
| darkcyan |
| darkgoldenrod |
| darkgray |
| darkgreen |
| darkkhaki |
| darkmagenta |
| darkolivegreen |
| darkorange |
| darkorchid |
| darkred |
| darksalmon |
| darkseagreen |
| darkslateblue |
| darkslategray |
| darkturquoise |
| darkviolet |
| deeppink |
| deepskyblue |
| dimgray |
| dodgerblue |
| firebrick |
| floralwhite |
| forestgreen |
| gainsboro |
| ghostwhite |
| gold |
| goldenrod |
| greenyellow |
| honeydew |
| hotpink |
| indianred |
| indigo |
| ivory |
| khaki |
| lavender |
| lavenderblush |
| lawngreen |
| lemonchiffon |
| lightblue |
| lightcoral |
| lightcyan |
| lightgoldenrodyellow |
| lightgray |
| lightgreen |
| lightpink |
| lightsalmon |
| lightseagreen |
| lightskyblue |
| lightslategray |
| lightsteelblue |
| lightyellow |
| limegreen |
| linen |
| mediumaquamarine |
| mediumblue |
| mediumorchid |
| mediumpurple |
| mediumseagreen |
| mediumslateblue |
| mediumspringgreen |
| mediumturquoise |
| mediumvioletred |
| midnightblue |
| mintcream |
| mistyrose |
| moccasin |
| navajowhite |
| oldlace |
| olivedrab |
| orange |
| orangered |
| orchid |
| palegoldenrod |
| palegreen |
| paleturquoise |
| palevioletred |
| papayawhip |
| peachpuff |
| peru |
| pink |
| plum |
| powderblue |
| rebeccapurple |
| rosybrown |
| royalblue |
| saddlebrown |
| salmon |
| sandybrown |
| seagreen |
| seashell |
| sienna |
| skyblue |
| slateblue |
| slategray |
| snow |
| springgreen |
| steelblue |
| tan |
| thistle |
| tomato |
| turquoise |
| violet |
| wheat |
| whitesmoke |
| yellowgreen |
| grey |
| dimgrey |
| darkgrey |
| darkslategrey |
| lightgrey |
| lightslategrey |
| slategrey |
---
## Disclaimer
This is still work in progress! If there is enough interest in the Kubernetes

View File

@ -0,0 +1,34 @@
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s_small.png" align="right" width="200" height="auto"/>
# Release v0.6.0
## Notes
Thank you to all that contributed with flushing out issues with K9s! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes!
If you've filed an issue please help me verify and close.
Thank you so much for your support and awesome suggestions to make K9s better!!
Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
---
## Change Logs
### Skins
You can now skin K9s based on your own sense of style. Skinning, is currently limited to color variations and is still very much experimental. More details on how to achieve this is provided in the README and skins directory in this repo. This could be a prime opportunity for someone to contribute to this project and help us come up with some cooler LNF and share with all K9s folks. Any schemes contributed will be added and featured on this repo.
---
## Resolved Bugs
+ [Issue #169](https://github.com/derailed/k9s/issues/169)
+ [Issue #171](https://github.com/derailed/k9s/issues/171)
+ [Issue #172](https://github.com/derailed/k9s/issues/172)
+ [Issue #175](https://github.com/derailed/k9s/issues/175)
---
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)

5
go.mod
View File

@ -12,10 +12,12 @@ replace (
require (
github.com/Azure/go-autorest/autorest v0.1.0 // indirect
github.com/derailed/tview v0.1.4
github.com/derailed/tview v0.1.6
github.com/evanphx/json-patch v4.1.0+incompatible // indirect
github.com/fatih/camelcase v1.0.0 // indirect
github.com/fsnotify/fsnotify v1.4.7
github.com/gdamore/tcell v1.1.1
github.com/go-fsnotify/fsnotify v0.0.0-20180321022601-755488143dae // indirect
github.com/gogo/protobuf v1.2.1 // indirect
github.com/google/btree v1.0.0 // indirect
github.com/google/gofuzz v1.0.0 // indirect
@ -30,6 +32,7 @@ require (
github.com/onsi/gomega v1.5.0 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/petergtz/pegomock v0.0.0-20181206220228-b113d17a7e81
github.com/pkg/profile v1.3.0
github.com/rs/zerolog v1.14.3
github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.3 // indirect

10
go.sum
View File

@ -47,6 +47,10 @@ 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=
github.com/derailed/tview v0.1.4 h1:6ZtMtb5+2bbGNH7SldHGcVB8GnSTXKIQwKxWRNb6DxY=
github.com/derailed/tview v0.1.4/go.mod h1:oLBQyhVeXqeUYWDZk7/5NJVbbq/JFXm3W7oEoEtpmSc=
github.com/derailed/tview v0.1.5 h1:Gj6K73V9d+zsex208KX8YsAw68+nWVNr7oM9qfTWbXQ=
github.com/derailed/tview v0.1.5/go.mod h1:g+ZyIsV5osK+lQ6LajiGQeLW10BQLJ6aMvy8Ldt2oa0=
github.com/derailed/tview v0.1.6 h1:mmp6Yg78IgbdHapV9wFoVYrbtZbMXilCdAIHadm2uqc=
github.com/derailed/tview v0.1.6/go.mod h1:g+ZyIsV5osK+lQ6LajiGQeLW10BQLJ6aMvy8Ldt2oa0=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
@ -71,6 +75,8 @@ github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2H
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-fsnotify/fsnotify v0.0.0-20180321022601-755488143dae h1:PeVNzgTRtWGm6fVic5i21t+n5ptPGCZuMcSPVMyTWjs=
github.com/go-fsnotify/fsnotify v0.0.0-20180321022601-755488143dae/go.mod h1:BbhqyaehKPCLD83cqfRYdm177Ylm1cdGHu3txjbQSQI=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
@ -193,6 +199,8 @@ github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.3.0 h1:OQIvuDgm00gWVWGTf4m4mCt6W1/0YqU7Ntg0mySWgaI=
github.com/pkg/profile v1.3.0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -211,6 +219,8 @@ github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
github.com/rivo/tview v0.0.0-20190213202703-b373355e9db4/go.mod h1:J4W+hErFfITUbyFAEXizpmkuxX7ZN56dopxHB4XQhMw=
github.com/rivo/uniseg v0.0.0-20190313204849-f699dde9c340 h1:nOZbL5f2xmBAHWYrrHbHV1xatzZirN++oOQ3g83Ypgs=
github.com/rivo/uniseg v0.0.0-20190313204849-f699dde9c340/go.mod h1:SOLvOL4ybwgLJ6TYoX/rtaJ8EGOulH4XU7E9/TLrTCE=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.14.3 h1:4EGfSkR2hJDB0s3oFfrlPqjU1e4WLncergLil3nEKW0=

View File

@ -50,7 +50,9 @@ func (n *Namespace) Validate(c Connection, ks KubeSettings) {
func (n *Namespace) SetActive(ns string, ks KubeSettings) error {
log.Debug().Msgf("Setting active ns %s", ns)
n.Active = ns
n.addFavNS(ns)
if ns != "" {
n.addFavNS(ns)
}
return nil
}

302
internal/config/style.go Normal file
View File

@ -0,0 +1,302 @@
package config
import (
"io/ioutil"
"path/filepath"
"github.com/derailed/tview"
"github.com/gdamore/tcell"
"gopkg.in/yaml.v2"
)
var (
// K9sStylesFile represents K9s config file location.
K9sStylesFile = filepath.Join(K9sHome, "skin.yml")
)
type (
// Styles tracks K9s styling options.
Styles struct {
Style *Style `yaml:"k9s"`
}
// Style tracks K9s styles.
Style struct {
FgColor string `yaml:"fgColor"`
BgColor string `yaml:"bgColor"`
LogoColor string `yaml:"logoColor"`
Info *Info `yaml:"info"`
Border *Border `yaml:"border"`
Menu *Menu `yaml:"menu"`
Crumb *Crumb `yaml:"crumb"`
Table *Table `yaml:"table"`
Status *Status `yaml:"status"`
Title *Title `yaml:"title"`
Yaml *Yaml `yaml:"yaml"`
}
// Status tracks resource status styles.
Status struct {
NewColor string `yaml:"newColor"`
ModifyColor string `yaml:"modifyColor"`
AddColor string `yaml:"addColor"`
ErrorColor string `yaml:"errorColor"`
HighlightColor string `yaml:"highlightColor"`
KillColor string `yaml:"killColor"`
CompletedColor string `yaml:"completedColor"`
}
// Yaml tracks yaml styles.
Yaml struct {
KeyColor string `yaml:"keyColor"`
ValueColor string `yaml:"valueColor"`
ColonColor string `yaml:"colonColor"`
}
// Title tracks title styles.
Title struct {
FgColor string `yaml:"fgColor"`
BgColor string `yaml:"bgColor"`
HighlightColor string `yaml:"highlightColor"`
CounterColor string `yaml:"counterColor"`
FilterColor string `yaml:"filterColor"`
}
// Info tracks info styles.
Info struct {
SectionColor string `yaml:"sectionColor"`
FgColor string `yaml:"fgColor"`
}
// Border tracks border styles.
Border struct {
FgColor string `yaml:"fgColor"`
FocusColor string `yaml:"focusColor"`
}
// Crumb tracks crumbs styles.
Crumb struct {
FgColor string `yaml:"fgColor"`
BgColor string `yaml:"bgColor"`
ActiveColor string `yaml:"activeColor"`
}
// Table tracks table styles.
Table struct {
FgColor string `yaml:"fgColor"`
BgColor string `yaml:"bgColor"`
CursorColor string `yaml:"cursorColor"`
Header *TableHeader `yaml:"header"`
}
// TableHeader tracks table header styles.
TableHeader struct {
FgColor string `yaml:"fgColor"`
BgColor string `yaml:"bgColor"`
SorterColor string `yaml:"sorterColor"`
}
// Menu tracks menu styles.
Menu struct {
FgColor string `yaml:"fgColor"`
KeyColor string `yaml:"keyColor"`
NumKeyColor string `yaml:"numKeyColor"`
}
)
func newStyle() *Style {
return &Style{
FgColor: "cadetblue",
BgColor: "black",
LogoColor: "orange",
Info: newInfo(),
Border: newBorder(),
Menu: newMenu(),
Crumb: newCrumb(),
Table: newTable(),
Status: newStatus(),
Title: newTitle(),
Yaml: newYaml(),
}
}
func newStatus() *Status {
return &Status{
NewColor: "lightskyblue",
ModifyColor: "greenyellow",
AddColor: "white",
ErrorColor: "orangered",
HighlightColor: "aqua",
KillColor: "mediumpurple",
CompletedColor: "gray",
}
}
// NewYaml returns a new yaml style.
func newYaml() *Yaml {
return &Yaml{
KeyColor: "steelblue",
ColonColor: "white",
ValueColor: "papayawhip",
}
}
// NewTitle returns a new title style.
func newTitle() *Title {
return &Title{
FgColor: "aqua",
BgColor: "black",
HighlightColor: "fuchsia",
CounterColor: "papayawhip",
FilterColor: "steelblue",
}
}
// NewInfo returns a new info style.
func newInfo() *Info {
return &Info{
SectionColor: "white",
FgColor: "orange",
}
}
// NewTable returns a new table style.
func newTable() *Table {
return &Table{
FgColor: "aqua",
BgColor: "black",
CursorColor: "aqua",
Header: newTableHeader(),
}
}
// NewTableHeader returns a new table header style.
func newTableHeader() *TableHeader {
return &TableHeader{
FgColor: "white",
BgColor: "black",
SorterColor: "orange",
}
}
// NewCrumb returns a new crumbs style.
func newCrumb() *Crumb {
return &Crumb{
FgColor: "black",
BgColor: "aqua",
ActiveColor: "orange",
}
}
// NewBorder returns a new border style.
func newBorder() *Border {
return &Border{
FgColor: "dodgerblue",
FocusColor: "lightskyblue",
}
}
// NewMenu returns a new menu style.
func newMenu() *Menu {
return &Menu{
FgColor: "white",
KeyColor: "dodgerblue",
NumKeyColor: "fuchsia",
}
}
// NewStyles creates a new default config.
func NewStyles() (*Styles, error) {
s := &Styles{Style: newStyle()}
err := s.load(K9sStylesFile)
return s, err
}
// Ensure default styles are applied in not in stylesheet.
func (s *Styles) ensure() {
if s.Style == nil {
s.Style = newStyle()
}
if s.Style.Info == nil {
s.Style.Info = newInfo()
}
if s.Style.Border == nil {
s.Style.Border = newBorder()
}
if s.Style.Table == nil {
s.Style.Table = newTable()
}
if s.Style.Menu == nil {
s.Style.Menu = newMenu()
}
if s.Style.Crumb == nil {
s.Style.Crumb = newCrumb()
}
if s.Style.Status == nil {
s.Style.Status = newStatus()
}
if s.Style.Title == nil {
s.Style.Title = newTitle()
}
if s.Style.Yaml == nil {
s.Style.Yaml = newYaml()
}
}
// FgColor returns the foreground color.
func (s *Styles) FgColor() tcell.Color {
return AsColor(s.Style.FgColor)
}
// BgColor returns the background color.
func (s *Styles) BgColor() tcell.Color {
return AsColor(s.Style.BgColor)
}
// Load K9s configuration from file
func (s *Styles) load(path string) error {
f, err := ioutil.ReadFile(path)
if err != nil {
return err
}
var cfg Styles
if err := yaml.Unmarshal(f, &cfg); err != nil {
return err
}
if cfg.Style != nil {
s.Style = cfg.Style
}
s.ensure()
return nil
}
// Update apply terminal colors based on styles.
func (s *Styles) Update() {
tview.Styles.PrimitiveBackgroundColor = AsColor(s.Style.BgColor)
tview.Styles.ContrastBackgroundColor = AsColor(s.Style.BgColor)
tview.Styles.PrimaryTextColor = AsColor(s.Style.FgColor)
tview.Styles.BorderColor = AsColor(s.Style.Border.FgColor)
tview.Styles.FocusColor = AsColor(s.Style.Border.FocusColor)
}
// AsColor checks color index, if match return color otherwise pink it is.
func AsColor(c string) tcell.Color {
if color, ok := tcell.ColorNames[c]; ok {
return color
}
return tcell.ColorPink
}

View File

@ -3,8 +3,8 @@ package resource
import (
"bufio"
"context"
"fmt"
"strconv"
"sync"
"time"
"github.com/derailed/k9s/internal/k8s"
@ -22,6 +22,7 @@ type (
instance v1.Container
MetricsServer MetricsServer
metrics k8s.PodMetrics
mx sync.RWMutex
}
)
@ -82,30 +83,49 @@ func (r *Container) Logs(c chan<- string, ns, n, co string, lines int64, prev bo
go func() {
select {
case <-time.After(defaultTimeout):
if blocked {
var closes bool
r.mx.RLock()
{
closes = blocked
}
r.mx.RUnlock()
if closes {
log.Debug().Msg(">>Closing Channel<<")
close(c)
cancel()
}
}
}()
// This call will block if nothing is in the stream!!
stream, err := req.Stream()
blocked = false
if err != nil {
log.Error().Msgf("Tail logs failed `%s/%s:%s -- %v", ns, n, co, err)
return cancel, fmt.Errorf("%v", err)
return cancel, err
}
r.mx.Lock()
{
blocked = false
}
r.mx.Unlock()
go func() {
defer func() {
log.Debug().Msg("!!!Closing Stream!!!")
close(c)
stream.Close()
cancel()
close(c)
}()
scanner := bufio.NewScanner(stream)
for scanner.Scan() {
c <- scanner.Text()
select {
case <-ctx.Done():
return
default:
}
}
}()

View File

@ -5,6 +5,7 @@ import (
"context"
"fmt"
"strconv"
"sync"
"time"
"github.com/derailed/k9s/internal/k8s"
@ -40,6 +41,7 @@ type (
instance *v1.Pod
MetricsServer MetricsServer
metrics k8s.PodMetrics
mx sync.RWMutex
}
)
@ -55,7 +57,11 @@ func NewPodList(c Connection, mx MetricsServer, ns string) List {
// NewPod instantiates a new Pod.
func NewPod(c Connection, mx MetricsServer) *Pod {
p := &Pod{&Base{Connection: c, Resource: k8s.NewPod(c)}, nil, mx, k8s.PodMetrics{}}
p := &Pod{
Base: &Base{Connection: c, Resource: k8s.NewPod(c)},
MetricsServer: mx,
metrics: k8s.PodMetrics{},
}
p.Factory = p
return p
@ -123,30 +129,49 @@ func (r *Pod) Logs(c chan<- string, ns, n, co string, lines int64, prev bool) (c
go func() {
select {
case <-time.After(defaultTimeout):
if blocked {
var closes bool
r.mx.RLock()
{
closes = blocked
}
r.mx.RUnlock()
if closes {
log.Debug().Msg(">>Closing Channel<<")
close(c)
cancel()
}
}
}()
// This call will block if nothing is in the stream!!
stream, err := req.Stream()
blocked = false
if err != nil {
log.Error().Msgf("Tail logs failed `%s/%s:%s -- %v", ns, n, co, err)
return cancel, fmt.Errorf("%v", err)
return cancel, err
}
r.mx.Lock()
{
blocked = false
}
r.mx.Unlock()
go func() {
defer func() {
log.Debug().Msg("!!!Closing Stream!!!")
close(c)
stream.Close()
cancel()
close(c)
}()
scanner := bufio.NewScanner(stream)
for scanner.Scan() {
c <- scanner.Text()
select {
case <-ctx.Done():
return
default:
}
}
}()

View File

@ -3,18 +3,22 @@ package views
import (
"context"
"fmt"
"sync"
"runtime"
"time"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/k8s"
"github.com/derailed/tview"
"github.com/fsnotify/fsnotify"
"github.com/gdamore/tcell"
"github.com/rs/zerolog/log"
"k8s.io/cli-runtime/pkg/genericclioptions"
)
const splashTime = 1
const (
splashTime = 1
devMode = "dev"
)
type (
focusHandler func(tview.Primitive)
@ -45,6 +49,7 @@ type (
*tview.Application
config *config.Config
styles *config.Styles
version string
flags *genericclioptions.ConfigFlags
pages *tview.Pages
@ -61,7 +66,6 @@ type (
cmdBuff *cmdBuff
cmdView *cmdView
actions keyActions
mx sync.Mutex
}
)
@ -69,15 +73,16 @@ type (
func NewApp(cfg *config.Config) *appView {
v := appView{Application: tview.NewApplication(), config: cfg}
{
v.refreshStyles()
v.pages = tview.NewPages()
v.actions = make(keyActions)
v.menuView = newMenuView()
v.menuView = newMenuView(&v)
v.content = tview.NewPages()
v.cmdBuff = newCmdBuff(':')
v.cmdView = newCmdView('🐶')
v.cmdView = newCmdView(&v, '🐶')
v.command = newCommand(&v)
v.flashView = newFlashView(v.Application, "Initializing...")
v.crumbsView = newCrumbsView(v.Application)
v.flashView = newFlashView(&v, "Initializing...")
v.crumbsView = newCrumbsView(&v)
v.clusterInfoView = newClusterInfoView(&v, k8s.NewMetricsServer(cfg.GetConnection()))
v.focusChanged = v.changedFocus
v.SetInputCapture(v.keyboard)
@ -108,21 +113,21 @@ func (a *appView) Init(v string, rate int, flags *genericclioptions.ConfigFlags)
header.SetDirection(tview.FlexColumn)
header.AddItem(a.clusterInfoView, 35, 1, false)
header.AddItem(a.menuView, 0, 1, false)
header.AddItem(logoView(), 26, 1, false)
header.AddItem(a.logoView(), 26, 1, false)
}
main := tview.NewFlex()
{
main.SetDirection(tview.FlexRow)
main.AddItem(header, 7, 1, false)
main.AddItem(a.cmdView, 1, 1, false)
main.AddItem(a.cmdView, 3, 1, false)
main.AddItem(a.content, 0, 10, true)
main.AddItem(a.crumbsView, 2, 1, false)
main.AddItem(a.flashView, 1, 1, false)
}
a.pages.AddPage("main", main, true, false)
a.pages.AddPage("splash", newSplash(a.version), true, true)
a.pages.AddPage("splash", newSplash(a), true, true)
a.SetRoot(a.pages, true)
}
@ -130,12 +135,52 @@ func (a *appView) conn() k8s.Connection {
return a.config.GetConnection()
}
func (a *appView) stylesUpdater() (*fsnotify.Watcher, error) {
w, err := fsnotify.NewWatcher()
if err != nil {
log.Error().Err(err).Msg("File notifier failed")
return w, err
}
go func() {
for {
select {
case evt := <-w.Events:
log.Debug().Msgf("Evt %#v", evt)
a.QueueUpdateDraw(func() {
a.refreshStyles()
})
case err := <-w.Errors:
log.Error().Err(err).Msg("Watcher failed")
}
}
}()
if err := w.Add(config.K9sStylesFile); err != nil {
log.Error().Err(err).Msg("Styles file watch failed")
return w, err
}
return w, nil
}
// Run starts the application loop
func (a *appView) Run() {
// Only enable updater while in dev mode.
if a.version == devMode {
w, err := a.stylesUpdater()
defer func() {
if err != nil {
w.Close()
}
}()
}
go func() {
<-time.After(splashTime * time.Second)
a.showPage("main")
a.Draw()
a.QueueUpdateDraw(func() {
a.showPage("main")
})
}()
a.command.defaultCmd()
@ -145,9 +190,6 @@ func (a *appView) Run() {
}
func (a *appView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
a.mx.Lock()
defer a.mx.Unlock()
key := evt.Key()
if key == tcell.KeyRune {
if a.cmdBuff.isActive() {
@ -156,10 +198,12 @@ func (a *appView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
}
key = tcell.Key(evt.Rune())
}
if a, ok := a.actions[key]; ok {
log.Debug().Msgf(">> AppView handled key: %s", tcell.KeyNames[key])
log.Debug().Msgf(">> AppView handled key: %s -- %d", tcell.KeyNames[key], runtime.NumGoroutine())
return a.action(evt)
}
return evt
}
@ -216,8 +260,8 @@ func (a *appView) activateCmd(evt *tcell.EventKey) *tcell.EventKey {
if a.cmdView.inCmdMode() {
return evt
}
a.flash(flashInfo, "Entering command mode...")
log.Debug().Msg("Entering app command mode...")
a.flash(flashInfo, "Command mode activated.")
log.Debug().Msg("Entering command mode...")
a.cmdBuff.setActive(true)
a.cmdBuff.clear()
return nil
@ -290,10 +334,6 @@ func (a *appView) cmdMode() bool {
return a.cmdView.inCmdMode()
}
func (a *appView) refresh() {
a.clusterInfoView.refresh()
}
func (a *appView) flash(level flashLevel, m ...string) {
a.flashView.setMessage(level, m...)
}
@ -302,14 +342,14 @@ func (a *appView) setHints(h hints) {
a.menuView.populateMenu(h)
}
func logoView() tview.Primitive {
func (a *appView) logoView() tview.Primitive {
v := tview.NewTextView()
{
v.SetWordWrap(false)
v.SetWrap(false)
v.SetDynamicColors(true)
for i, s := range LogoSmall {
fmt.Fprintf(v, "[orange::b]%s", s)
fmt.Fprintf(v, "[%s::b]%s", a.styles.Style.LogoColor, s)
if i+1 < len(LogoSmall) {
fmt.Fprintf(v, "\n")
}
@ -348,9 +388,17 @@ func (a *appView) nextFocus() {
return
}
func initStyles() {
tview.Styles.PrimitiveBackgroundColor = tcell.ColorBlack
tview.Styles.ContrastBackgroundColor = tcell.ColorBlack
tview.Styles.FocusColor = tcell.ColorLightSkyBlue
tview.Styles.BorderColor = tcell.ColorDodgerBlue
func (a *appView) refreshStyles() {
var err error
if a.styles, err = config.NewStyles(); err != nil {
log.Error().Err(err).Msg("No skin file found. Loading defaults.")
}
a.styles.Update()
stdColor = config.AsColor(a.styles.Style.Status.NewColor)
addColor = config.AsColor(a.styles.Style.Status.AddColor)
modColor = config.AsColor(a.styles.Style.Status.ModifyColor)
errColor = config.AsColor(a.styles.Style.Status.ErrorColor)
highlightColor = config.AsColor(a.styles.Style.Status.HighlightColor)
completedColor = config.AsColor(a.styles.Style.Status.CompletedColor)
}

View File

@ -3,6 +3,7 @@ package views
import (
"strings"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/resource"
"github.com/derailed/tview"
"github.com/gdamore/tcell"
@ -62,20 +63,25 @@ func (v *clusterInfoView) init() {
v.SetCell(row, 1, v.infoCell("n/a"))
v.SetCell(row+1, 0, v.sectionCell("MEM"))
v.SetCell(row+1, 1, v.infoCell("n/a"))
v.refresh()
}
func (*clusterInfoView) sectionCell(t string) *tview.TableCell {
func (v *clusterInfoView) sectionCell(t string) *tview.TableCell {
c := tview.NewTableCell(t + ":")
c.SetAlign(tview.AlignLeft)
var s tcell.Style
c.SetStyle(s.Bold(true).Foreground(config.AsColor(v.app.styles.Style.Info.SectionColor)))
c.SetBackgroundColor(v.app.styles.BgColor())
return c
}
func (*clusterInfoView) infoCell(t string) *tview.TableCell {
func (v *clusterInfoView) infoCell(t string) *tview.TableCell {
c := tview.NewTableCell(t)
c.SetExpansion(2)
c.SetTextColor(tcell.ColorOrange)
c.SetTextColor(config.AsColor(v.app.styles.Style.Info.FgColor))
c.SetBackgroundColor(v.app.styles.BgColor())
return c
}

View File

@ -3,9 +3,8 @@ package views
import (
"fmt"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/tview"
"github.com/gdamore/tcell"
"github.com/rs/zerolog/log"
)
const defaultPrompt = "%c> %s"
@ -16,16 +15,20 @@ type cmdView struct {
activated bool
icon rune
text string
app *appView
}
func newCmdView(ic rune) *cmdView {
v := cmdView{icon: ic, TextView: tview.NewTextView()}
func newCmdView(app *appView, ic rune) *cmdView {
v := cmdView{app: app, icon: ic, TextView: tview.NewTextView()}
{
v.SetWordWrap(true)
v.SetWrap(true)
v.SetDynamicColors(true)
v.SetBorder(true)
v.SetBorderPadding(0, 0, 1, 1)
v.SetTextColor(tcell.ColorAqua)
v.SetBackgroundColor(app.styles.BgColor())
v.SetBorderColor(config.AsColor(app.styles.Style.Border.FocusColor))
v.SetTextColor(app.styles.FgColor())
}
return &v
}
@ -62,11 +65,12 @@ func (v *cmdView) changed(s string) {
func (v *cmdView) active(f bool) {
v.activated = f
if f {
log.Debug().Msg("CmdView was activated...")
v.SetBackgroundColor(tcell.ColorDodgerBlue)
v.SetBorder(true)
v.SetTextColor(v.app.styles.FgColor())
v.activate()
} else {
v.SetBackgroundColor(tcell.ColorDefault)
v.SetBorder(false)
v.SetBackgroundColor(v.app.styles.BgColor())
v.Clear()
}
}

View File

@ -1,5 +1,9 @@
package views
import (
"github.com/rs/zerolog/log"
)
const maxBuff = 10
type buffWatcher interface {
@ -37,6 +41,7 @@ func (c *cmdBuff) String() string {
}
func (c *cmdBuff) add(r rune) {
log.Debug().Msgf("Add %s", string(r))
c.buff = append(c.buff, r)
c.fireChanged()
}

View File

@ -8,14 +8,14 @@ import (
"k8s.io/apimachinery/pkg/watch"
)
const (
modColor = tcell.ColorGreenYellow
addColor = tcell.ColorLightSkyBlue
errColor = tcell.ColorOrangeRed
stdColor = tcell.ColorWhite
highlightColor = tcell.ColorAqua
killColor = tcell.ColorMediumPurple
completedColor = tcell.ColorDodgerBlue
var (
modColor tcell.Color
addColor tcell.Color
errColor tcell.Color
stdColor tcell.Color
highlightColor tcell.Color
killColor tcell.Color
completedColor tcell.Color
)
func defaultColorer(ns string, r *resource.RowEvent) tcell.Color {

View File

@ -22,6 +22,7 @@ func newContainerView(t string, app *appView, list resource.List, path string) r
}
v.AddPage("logs", newLogsView(list.GetName(), &v), true, false)
v.switchPage("co")
v.selChanged(1, 0)
return &v
}

View File

@ -4,33 +4,36 @@ import (
"fmt"
"github.com/derailed/tview"
"github.com/gdamore/tcell"
)
type crumbsView struct {
*tview.TextView
app *tview.Application
app *appView
}
func newCrumbsView(app *tview.Application) *crumbsView {
func newCrumbsView(app *appView) *crumbsView {
v := crumbsView{app: app, TextView: tview.NewTextView()}
{
v.SetTextColor(tcell.ColorAqua)
v.SetBackgroundColor(app.styles.BgColor())
v.SetTextAlign(tview.AlignLeft)
v.SetBorderPadding(0, 0, 1, 1)
v.SetDynamicColors(true)
}
return &v
}
func (v *crumbsView) update(crumbs []string) {
v.Clear()
last, bgColor := len(crumbs)-1, "aqua"
last, bgColor := len(crumbs)-1, v.app.styles.Style.Crumb.BgColor
for i, c := range crumbs {
if i == last {
bgColor = "orange"
bgColor = v.app.styles.Style.Crumb.ActiveColor
}
fmt.Fprintf(v, "[black:%s:b] <%s> [-:-:-] ", bgColor, c)
fmt.Fprintf(v, "[%s:%s:b] <%s> [-:%s:-] ",
v.app.styles.Style.Crumb.FgColor,
bgColor, c,
v.app.styles.Style.BgColor)
}
}

View File

@ -5,14 +5,14 @@ import (
"regexp"
"strconv"
"strings"
"sync"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/tview"
"github.com/gdamore/tcell"
"github.com/rs/zerolog/log"
)
const detailsTitleFmt = " [aqua::b]%s([fuchsia::b]%s[aqua::-])[aqua::-] "
const detailsTitleFmt = "[fg:bg:b] %s([hilite:bg:b]%s[fg:bg:-])[fg:bg:-] "
// detailsView displays text output.
type detailsView struct {
@ -25,7 +25,6 @@ type detailsView struct {
cmdBuff *cmdBuff
backFn actionHandler
numSelections int
mx sync.Mutex
}
func newDetailsView(app *appView, backFn actionHandler) *detailsView {
@ -37,6 +36,7 @@ func newDetailsView(app *appView, backFn actionHandler) *detailsView {
v.SetDynamicColors(true)
v.SetRegions(true)
v.SetBorder(true)
v.SetBorderFocusColor(config.AsColor(v.app.styles.Style.Border.FocusColor))
v.SetHighlightColor(tcell.ColorOrange)
v.SetTitleColor(tcell.ColorAqua)
v.SetInputCapture(v.keyboard)
@ -65,9 +65,6 @@ func (v *detailsView) setCategory(n string) {
}
func (v *detailsView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
v.mx.Lock()
defer v.mx.Unlock()
key := evt.Key()
if key == tcell.KeyRune {
if v.cmdBuff.isActive() {
@ -198,9 +195,16 @@ func (v *detailsView) refreshTitle() {
func (v *detailsView) setTitle(t string) {
v.title = t
title := fmt.Sprintf(detailsTitleFmt, v.category, t)
fmat := strings.Replace(detailsTitleFmt, "[fg", "["+v.app.styles.Style.Title.FgColor, -1)
fmat = strings.Replace(fmat, ":bg:", ":"+v.app.styles.Style.Title.BgColor+":", -1)
fmat = strings.Replace(fmat, "[hilite", "["+v.app.styles.Style.Title.CounterColor, 1)
title := fmt.Sprintf(fmat, v.category, t)
if !v.cmdBuff.empty() {
title += fmt.Sprintf(searchFmt, v.cmdBuff.String())
fmat := strings.Replace(searchFmt, "[fg", "["+v.app.styles.Style.Title.FgColor, -1)
fmat = strings.Replace(fmat, ":bg:", ":"+v.app.styles.Style.Title.BgColor+":", -1)
fmat = strings.Replace(fmat, "[filter", "["+v.app.styles.Style.Title.FilterColor, 1)
title += fmt.Sprintf(fmat, v.cmdBuff.String())
}
v.SetTitle(title)
}

View File

@ -30,42 +30,44 @@ type (
*tview.TextView
cancel context.CancelFunc
app *tview.Application
app *appView
}
)
func newFlashView(app *tview.Application, m string) *flashView {
func newFlashView(app *appView, m string) *flashView {
f := flashView{app: app, TextView: tview.NewTextView()}
{
f.SetTextColor(tcell.ColorAqua)
f.SetTextAlign(tview.AlignLeft)
f.SetBorderPadding(0, 0, 1, 1)
f.SetText("")
}
f.SetTextColor(tcell.ColorAqua)
f.SetTextAlign(tview.AlignLeft)
f.SetBorderPadding(0, 0, 1, 1)
f.SetText("")
return &f
}
func (f *flashView) setMessage(level flashLevel, msg ...string) {
if f.cancel != nil {
f.cancel()
func (v *flashView) setMessage(level flashLevel, msg ...string) {
if v.cancel != nil {
v.cancel()
}
_, _, width, _ := v.GetRect()
if width <= 15 {
width = 100
}
m := strings.Join(msg, " ")
v.SetTextColor(flashColor(level))
v.SetText(resource.Truncate(flashEmoji(level)+" "+m, width-3))
var ctx context.Context
{
ctx, f.cancel = context.WithTimeout(context.TODO(), flashDelay*time.Second)
ctx, v.cancel = context.WithTimeout(context.TODO(), flashDelay*time.Second)
go func(ctx context.Context) {
_, _, width, _ := f.GetRect()
if width <= 15 {
width = 100
}
m := strings.Join(msg, " ")
f.SetTextColor(flashColor(level))
f.SetText(resource.Truncate(flashEmoji(level)+" "+m, width-3))
f.app.Draw()
for {
select {
case <-ctx.Done():
f.Clear()
f.app.Draw()
v.app.QueueUpdateDraw(func() {
v.Clear()
// v.app.Draw()
})
return
}
}

View File

@ -3,6 +3,7 @@ package views
import (
"fmt"
"io"
"strings"
"github.com/derailed/tview"
)
@ -28,14 +29,23 @@ func newLogView(title string, parent loggable) *logView {
return &v
}
func (l *logView) logLine(line string, scroll bool) {
func (l *logView) logLine(line string) {
fmt.Fprintln(l.ansiWriter, tview.Escape(line))
if scroll {
l.ScrollToEnd()
}
}
func (l *logView) log(lines fmt.Stringer) {
l.Clear()
fmt.Fprintln(l.ansiWriter, lines.String())
}
func (l *logView) flush(index int, buff []string, scroll bool) {
if index > 0 {
l.logLine(strings.Join(buff[:index], "\n"))
if scroll {
l.app.QueueUpdate(func() {
l.ScrollToEnd()
})
}
index = 0
}
}

View File

@ -3,8 +3,8 @@ package views
import (
"context"
"fmt"
"runtime"
"strconv"
"sync"
"time"
"github.com/derailed/k9s/internal/resource"
@ -17,6 +17,7 @@ const (
maxBuff1 int64 = 200
refreshRate = 200 * time.Millisecond
maxCleanse = 100
logBuffSize = 100
)
type logsView struct {
@ -29,7 +30,6 @@ type logsView struct {
cancelFunc context.CancelFunc
autoScroll bool
showPrevious bool
mx sync.Mutex
}
func newLogsView(pview string, parent loggable) *logsView {
@ -130,8 +130,9 @@ func (v *logsView) stop() {
if v.cancelFunc == nil {
return
}
log.Debug().Msg("Canceling logs...")
v.cancelFunc()
log.Debug().Msgf("Canceling logs... %d", runtime.NumGoroutine())
v.cancelFunc = nil
}
@ -139,47 +140,57 @@ func (v *logsView) load(i int) {
if i < 0 || i > len(v.containers)-1 {
return
}
v.SwitchToPage(v.containers[i])
if err := v.doLoad(v.parent.getSelection(), v.containers[i]); err != nil {
v.parent.appView().flash(flashErr, err.Error())
l := v.CurrentPage().Item.(*logView)
l.logLine("😂 Doh! No logs are available at this time. Check again later on...", false)
l.logLine("😂 Doh! No logs are available at this time. Check again later on...")
return
}
v.parent.appView().SetFocus(v)
}
func (v *logsView) doLoad(path, co string) error {
v.mx.Lock()
defer v.mx.Unlock()
v.stop()
c := make(chan string)
go func() {
l := v.CurrentPage().Item.(*logView)
l.Clear()
l.setTitle(path + ":" + co)
maxBuff := int64(v.parent.appView().config.K9s.LogRequestSize)
l := v.CurrentPage().Item.(*logView)
l.Clear()
l.setTitle(path + ":" + co)
c := make(chan string, 10)
go func(l *logView) {
buff, index := make([]string, logBuffSize), 0
for {
select {
case line, ok := <-c:
if !ok {
if v.autoScroll {
l.ScrollToEnd()
}
l.flush(index, buff, v.autoScroll)
index = 0
return
}
l.logLine(line, v.autoScroll)
if index < logBuffSize {
buff[index] = line
index++
continue
}
l.flush(index, buff, v.autoScroll)
index = 0
buff[index] = line
case <-time.After(1 * time.Second):
l.flush(index, buff, v.autoScroll)
index = 0
}
}
}()
}(l)
ns, po := namespaced(path)
res, ok := v.parent.getList().Resource().(resource.Tailable)
if !ok {
return fmt.Errorf("Resource %T is not tailable", v.parent.getList().Resource)
}
maxBuff := int64(v.parent.appView().config.K9s.LogRequestSize)
cancelFn, err := res.Logs(c, ns, po, co, maxBuff, v.showPrevious)
if err != nil {
cancelFn()
@ -196,9 +207,9 @@ func (v *logsView) doLoad(path, co string) error {
func (v *logsView) toggleScrollCmd(evt *tcell.EventKey) *tcell.EventKey {
v.autoScroll = !v.autoScroll
if v.autoScroll {
v.parent.appView().flash(flashInfo, "Autoscroll is on")
v.parent.appView().flash(flashInfo, "Autoscroll is on.")
} else {
v.parent.appView().flash(flashInfo, "Autoscroll is off")
v.parent.appView().flash(flashInfo, "Autoscroll is off.")
}
return nil
@ -208,7 +219,7 @@ func (v *logsView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
v.stop()
v.parent.switchPage(v.parentView)
return nil
return evt
}
func (v *logsView) topCmd(evt *tcell.EventKey) *tcell.EventKey {

View File

@ -6,7 +6,6 @@ import (
"sort"
"strconv"
"strings"
"sync"
"github.com/derailed/k9s/internal/resource"
"github.com/derailed/tview"
@ -16,12 +15,10 @@ import (
func init() {
initKeys()
initStyles()
}
const (
menuSepFmt = " [dodgerblue::b]%-%ds [white::d]%s "
menuIndexFmt = " [fuchsia::b]<%d> [white::d]%s "
menuIndexFmt = " [key:bg:b]<%d> [fg:bg:d]%s "
maxRows = 7
)
@ -77,11 +74,6 @@ func newKeyAction(d string, a actionHandler, display bool) keyAction {
return keyAction{description: d, action: a, visible: display}
}
func newMenuView() *menuView {
v := menuView{Table: tview.NewTable()}
return &v
}
func (a keyActions) toHints() hints {
kk := make([]int, 0, len(a))
for k, v := range a {
@ -108,16 +100,19 @@ func (a keyActions) toHints() hints {
type menuView struct {
*tview.Table
mx sync.Mutex
app *appView
}
func newMenuView(app *appView) *menuView {
v := menuView{Table: tview.NewTable(), app: app}
v.SetBackgroundColor(app.styles.BgColor())
return &v
}
func (v *menuView) populateMenu(hh hints) {
v.mx.Lock()
defer v.mx.Unlock()
v.Clear()
sort.Sort(hh)
t := v.buildMenuTable(hh)
for row := 0; row < len(t); row++ {
for col := 0; col < len(t[row]); col++ {
@ -125,6 +120,7 @@ func (v *menuView) populateMenu(hh hints) {
continue
}
c := tview.NewTableCell(t[row][col])
c.SetBackgroundColor(v.app.styles.BgColor())
v.SetCell(row, col, c)
}
}
@ -143,9 +139,6 @@ func (v *menuView) buildMenuTable(hh hints) [][]string {
maxKeys := make([]int, colCount+1)
for _, h := range hh {
isDigit := menuRX.MatchString(h.mnemonic)
// if isDigit && firstNS {
// row, col, firstNS = 0, 2, false
// }
if !isDigit && firstCmd {
row, col, firstCmd = 0, col+1, false
}
@ -184,11 +177,17 @@ func (*menuView) toMnemonic(s string) string {
func (v *menuView) formatMenu(h hint, size int) string {
i, err := strconv.Atoi(h.mnemonic)
if err == nil {
return fmt.Sprintf(menuIndexFmt, i, resource.Truncate(h.description, 14))
fmat := strings.Replace(menuIndexFmt, "[key", "["+v.app.styles.Style.Menu.NumKeyColor, 1)
fmat = strings.Replace(fmat, ":bg:", ":"+v.app.styles.Style.Title.BgColor+":", -1)
fmat = strings.Replace(fmat, "[fg", "["+v.app.styles.Style.Menu.FgColor, 1)
return fmt.Sprintf(fmat, i, resource.Truncate(h.description, 14))
}
menuFmt := " [dodgerblue::b]%-" + strconv.Itoa(size+2) + "s [white::d]%s "
return fmt.Sprintf(menuFmt, v.toMnemonic(h.mnemonic), h.description)
menuFmt := " [key:bg:b]%-" + strconv.Itoa(size+2) + "s [fg:bg:d]%s "
fmat := strings.Replace(menuFmt, "[key", "["+v.app.styles.Style.Menu.KeyColor, 1)
fmat = strings.Replace(fmat, "[fg", "["+v.app.styles.Style.Menu.FgColor, 1)
fmat = strings.Replace(fmat, ":bg:", ":"+v.app.styles.Style.Title.BgColor+":", -1)
return fmt.Sprintf(fmat, v.toMnemonic(h.mnemonic), h.description)
}
// -----------------------------------------------------------------------------

View File

@ -63,8 +63,11 @@ func showPods(app *appView, ns, res, selected, labelSel, fieldSel string, b acti
title := fmt.Sprintf("%s:%s Pods", res, selected)
pv := newPodView(title, app, list)
pv.setColorerFn(podColorer)
pv.setExtraActionsFn(func(aa keyActions) {
aa[tcell.KeyEsc] = newKeyAction("Back", b, true)
})
// Reset active namespace to all.
app.config.SetActiveNamespace("")
app.inject(pv)
}

View File

@ -2,6 +2,7 @@ package views
import (
"fmt"
"strings"
"github.com/derailed/k9s/internal/k8s"
"github.com/derailed/k9s/internal/resource"
@ -10,7 +11,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const fmat = "[aqua::b]%s([fuchsia::b]%s[aqua::-])"
const containerFmt = "[fg:bg:b]%s([hilite:bg:b]%s[fg:bg:-])"
type podView struct {
*resourceView
@ -61,7 +62,17 @@ func (v *podView) listContainers(app *appView, _, res, sel string) {
mx := k8s.NewMetricsServer(app.conn())
list := resource.NewContainerList(app.conn(), mx, po)
app.inject(newContainerView(fmt.Sprintf(fmat, "Containers", sel), app, list, namespacedName(po.Namespace, po.Name)))
fmat := strings.Replace(containerFmt, "[fg", "["+v.app.styles.Style.Title.FgColor, -1)
fmat = strings.Replace(containerFmt, ":bg:", ":"+v.app.styles.Style.Title.BgColor+":", -1)
fmat = strings.Replace(fmat, "[hilite", "["+v.app.styles.Style.Title.CounterColor, 1)
app.inject(newContainerView(
fmt.Sprintf(fmat, "Containers", sel),
app,
list,
namespacedName(po.Namespace, po.Name),
))
}
// Protocol...
@ -88,6 +99,7 @@ func (v *podView) logsCmd(evt *tcell.EventKey) *tcell.EventKey {
if v.viewLogs(false) {
return nil
}
return evt
}
@ -95,6 +107,7 @@ func (v *podView) prevLogsCmd(evt *tcell.EventKey) *tcell.EventKey {
if v.viewLogs(true) {
return nil
}
return evt
}

View File

@ -19,7 +19,7 @@ const (
all = "*"
rbacTitle = "RBAC"
rbacTitleFmt = " [aqua::b]%s([fuchsia::b]%s[aqua::-])"
rbacTitleFmt = " [fg:bg:b]%s([hilite:bg:b]%s[fg:bg:-])"
)
type (
@ -28,6 +28,7 @@ type (
rbacView struct {
*tableView
app *appView
current igniter
cancel context.CancelFunc
roleType roleKind
@ -78,7 +79,7 @@ var (
)
func newRBACView(app *appView, ns, name string, kind roleKind) *rbacView {
v := rbacView{}
v := rbacView{app: app}
{
v.roleName, v.roleType = name, kind
v.tableView = newTableView(app, v.getTitle())
@ -125,7 +126,11 @@ func (v *rbacView) bindKeys() {
}
func (v *rbacView) getTitle() string {
return fmt.Sprintf(rbacTitleFmt, rbacTitle, v.roleName)
fmat := strings.Replace(rbacTitleFmt, "[fg", "["+v.app.styles.Style.Title.FgColor, -1)
fmat = strings.Replace(fmat, ":bg:", ":"+v.app.styles.Style.Title.BgColor+":", -1)
fmat = strings.Replace(fmat, "[hilite", "["+v.app.styles.Style.Title.HighlightColor, 1)
return fmt.Sprintf(fmat, rbacTitle, v.roleName)
}
func (v *rbacView) hints() hints {

View File

@ -6,7 +6,6 @@ import (
"path"
"strconv"
"strings"
"sync"
"time"
"github.com/derailed/k9s/internal/config"
@ -17,10 +16,7 @@ import (
"github.com/rs/zerolog/log"
)
const (
refreshDelay = 0.1
noSelection = ""
)
const noSelection = ""
type (
details interface {
@ -40,7 +36,6 @@ type (
selectedRow int
namespaces map[int]string
selectedNS string
update sync.Mutex
list resource.List
enterFn enterFn
extraActionsFn func(keyActions)
@ -90,13 +85,16 @@ func (v *resourceView) init(ctx context.Context, ns string) {
log.Debug().Msgf("%s watcher canceled!", v.title)
return
case <-time.After(time.Duration(v.app.config.K9s.RefreshRate) * time.Second):
v.refresh()
v.app.QueueUpdate(func() {
v.refresh()
})
}
}
}(ctx)
v.refresh()
if tv, ok := v.CurrentPage().Item.(*tableView); ok {
tv.Select(0, 0)
tv.Select(1, 0)
v.selChanged(1, 0)
}
}
@ -118,6 +116,7 @@ func (v *resourceView) getSelectedItem() string {
if v.selectedFn != nil {
return v.selectedFn()
}
return v.selectedItem
}
@ -220,7 +219,7 @@ func (v *resourceView) defaultEnter(app *appView, ns, resource, selection string
details.setCategory("Describe")
details.setTitle(sel)
details.SetTextColor(tcell.ColorAqua)
details.SetText(colorizeYAML(yaml))
details.SetText(colorizeYAML(v.app.styles.Style, yaml))
details.ScrollToBeginning()
}
v.switchPage("details")
@ -239,6 +238,7 @@ func (v *resourceView) viewCmd(evt *tcell.EventKey) *tcell.EventKey {
if !v.rowSelected() {
return evt
}
sel := v.getSelectedItem()
raw, err := v.list.Resource().Marshal(sel)
if err != nil {
@ -251,10 +251,11 @@ func (v *resourceView) viewCmd(evt *tcell.EventKey) *tcell.EventKey {
details.setCategory("View")
details.setTitle(sel)
details.SetTextColor(tcell.ColorMediumAquamarine)
details.SetText(colorizeYAML(raw))
details.SetText(colorizeYAML(v.app.styles.Style, raw))
details.ScrollToBeginning()
}
v.switchPage("details")
return nil
}
@ -262,6 +263,7 @@ func (v *resourceView) editCmd(evt *tcell.EventKey) *tcell.EventKey {
if !v.rowSelected() {
return evt
}
ns, po := namespaced(v.selectedItem)
args := make([]string, 0, 10)
args = append(args, "edit")
@ -270,6 +272,7 @@ func (v *resourceView) editCmd(evt *tcell.EventKey) *tcell.EventKey {
args = append(args, "--context", v.app.config.K9s.CurrentContext)
args = append(args, po)
runK(true, v.app, args...)
return evt
}
@ -282,21 +285,17 @@ func (v *resourceView) switchNamespaceCmd(evt *tcell.EventKey) *tcell.EventKey {
}
func (v *resourceView) doSwitchNamespace(ns string) {
v.update.Lock()
{
if ns == noSelection {
ns = resource.AllNamespace
}
v.selectedNS = ns
v.app.flash(flashInfo, fmt.Sprintf("Viewing `%s namespace...", ns))
v.list.SetNamespace(v.selectedNS)
if ns == "" {
ns = resource.AllNamespace
}
v.update.Unlock()
v.selectedNS = ns
v.app.flash(flashInfo, fmt.Sprintf("Viewing `%s namespace...", ns))
v.list.SetNamespace(v.selectedNS)
v.refresh()
v.selectItem(0, 0)
v.getTV().resetTitle()
v.getTV().Select(0, 0)
v.getTV().Select(1, 0)
v.selectItem(1, 0)
v.app.cmdBuff.reset()
v.app.config.SetActiveNamespace(v.selectedNS)
v.app.config.Save()
@ -307,32 +306,32 @@ func (v *resourceView) refresh() {
return
}
v.update.Lock()
{
if v.list.Namespaced() {
v.list.SetNamespace(v.selectedNS)
}
if err := v.list.Reconcile(); err != nil {
log.Error().Err(err).Msg("Reconciliation failed")
v.app.flash(flashErr, err.Error())
}
data := v.list.Data()
if v.decorateFn != nil {
data = v.decorateFn(data)
}
v.getTV().update(data)
v.selectItem(v.selectedRow, 0)
v.refreshActions()
v.app.clusterInfoView.refresh()
v.app.Draw()
if v.list.Namespaced() {
v.list.SetNamespace(v.selectedNS)
}
v.update.Unlock()
v.refreshActions()
v.app.clusterInfoView.refresh()
if err := v.list.Reconcile(); err != nil {
log.Error().Err(err).Msg("Reconciliation failed")
v.app.flash(flashErr, err.Error())
}
data := v.list.Data()
if v.decorateFn != nil {
data = v.decorateFn(data)
}
v.getTV().update(data)
v.selectItem(v.selectedRow, 0)
v.app.Draw()
}
func (v *resourceView) getTV() *tableView {
if tv, ok := v.GetPrimitive(v.list.GetName()).(*tableView); ok {
return tv
}
return nil
}
@ -360,18 +359,11 @@ func (v *resourceView) selectItem(r, c int) {
}
func (v *resourceView) switchPage(p string) {
v.update.Lock()
{
v.SwitchToPage(p)
v.selectedNS = v.list.GetNamespace()
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.SwitchToPage(p)
v.selectedNS = v.list.GetNamespace()
if h, ok := v.GetPrimitive(p).(hinter); ok {
v.app.setHints(h.hints())
}
v.update.Unlock()
}
func (v *resourceView) rowSelected() bool {
@ -389,7 +381,7 @@ func (v *resourceView) refreshActions() {
}
var nn []interface{}
if !v.list.HasSelectors() && k8s.CanIAccess(v.app.conn().Config(), log.Logger, "", "list", "namespaces", "namespace.v1") {
if k8s.CanIAccess(v.app.conn().Config(), log.Logger, "", "list", "namespaces", "namespace.v1") {
var err error
nn, err = k8s.NewNamespace(v.app.conn()).List(resource.AllNamespaces)
if err != nil {
@ -413,7 +405,6 @@ func (v *resourceView) refreshActions() {
}
v.actions[tcell.KeyEnter] = newKeyAction("Enter", v.enterCmd, false)
v.actions[tcell.KeyCtrlR] = newKeyAction("Refresh", v.refreshCmd, false)
v.actions[KeyHelp] = newKeyAction("Help", v.app.noopCmd, false)
v.actions[KeyP] = newKeyAction("Previous", v.app.prevCmd, false)

View File

@ -56,7 +56,7 @@ func (v *secretView) decodeCmd(evt *tcell.EventKey) *tcell.EventKey {
details.setCategory("Decoder")
details.setTitle(sel)
details.SetTextColor(tcell.ColorMediumAquamarine)
details.SetText(colorizeYAML(string(raw)))
details.SetText(colorizeYAML(v.app.styles.Style, string(raw)))
details.ScrollToBeginning()
}
v.switchPage("details")

View File

@ -36,11 +36,13 @@ var Logo = []string{
// Splash screen definition
type splashView struct {
*tview.Flex
app *appView
}
// NewSplash instantiates a new splash screen with product and company info.
func newSplash(rev string) *splashView {
v := splashView{tview.NewFlex()}
func newSplash(app *appView) *splashView {
v := splashView{Flex: tview.NewFlex(), app: app}
logo := tview.NewTextView()
{
@ -56,7 +58,7 @@ func newSplash(rev string) *splashView {
vers.SetBackgroundColor(tcell.ColorDefault)
vers.SetTextAlign(tview.AlignCenter)
}
v.layoutRev(vers, rev)
v.layoutRev(vers, app.version)
v.SetDirection(tview.FlexRow)
v.AddItem(logo, 10, 1, false)
@ -65,10 +67,13 @@ func newSplash(rev string) *splashView {
}
func (v *splashView) layoutLogo(t *tview.TextView) {
logo := strings.Join(Logo, "\n[orange::b]")
fmt.Fprintf(t, "%s[orange::b]%s\n", strings.Repeat("\n", 2), logo)
logo := strings.Join(Logo, fmt.Sprintf("\n[%s::b]", v.app.styles.Style.LogoColor))
fmt.Fprintf(t, "%s[%s::b]%s\n",
strings.Repeat("\n", 2),
v.app.styles.Style.LogoColor,
logo)
}
func (v *splashView) layoutRev(t *tview.TextView, rev string) {
fmt.Fprintf(t, "[white::b]Revision [red::b]%s", rev)
fmt.Fprintf(t, "[%s::b]Revision [red::b]%s", v.app.styles.Style.FgColor, rev)
}

View File

@ -14,8 +14,6 @@ import (
"k8s.io/apimachinery/pkg/watch"
)
const subjectTitleFmt = " [aqua::b]%s([fuchsia::b]%s[aqua::-])"
var subjectHeader = resource.Row{"NAME", "KIND", "FIRST LOCATION"}
type (
@ -177,13 +175,11 @@ func (v *subjectView) reconcile() (resource.TableData, error) {
if err != nil {
return table, err
}
log.Debug().Msgf("Cluster evts %d", len(evts))
nevts, err := v.namespacedSubjects()
if err != nil {
return table, err
}
log.Debug().Msgf("NS evts %d", len(nevts))
for k, v := range nevts {
evts[k] = v
}

View File

@ -6,9 +6,9 @@ import (
"runtime"
"sort"
"strings"
"sync"
"time"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/resource"
"github.com/derailed/tview"
"github.com/gdamore/tcell"
@ -17,9 +17,9 @@ import (
)
const (
titleFmt = " [aqua::b]%s[aqua::-][[fuchsia::b]%d[aqua::-]] "
searchFmt = "<[green::b]/%s[aqua::]> "
nsTitleFmt = " [aqua::b]%s([fuchsia::b]%s[aqua::-])[aqua::-][[aqua::b]%d[aqua::-]][aqua::-] "
titleFmt = "[fg:bg:b] %s[fg:bg:-][[count:bg:b]%d[fg:bg:-]] "
searchFmt = "<[filter:bg:b]/%s[fg:bg:]> "
nsTitleFmt = "[fg:bg:b] %s([hilite:bg:b]%s[fg:bg:-])[fg:bg:-][[count:bg:b]%d[fg:bg:-]][fg:bg:-] "
)
type (
@ -38,7 +38,6 @@ type (
app *appView
baseTitle string
currentNS string
refreshMX sync.Mutex
actions keyActions
colorerFn colorerFn
sortFn sortFn
@ -46,7 +45,6 @@ type (
data resource.TableData
cmdBuff *cmdBuff
sortBuff *cmdBuff
tableMX sync.Mutex
sortCol sortColumn
}
)
@ -58,15 +56,20 @@ func newTableView(app *appView, title string) *tableView {
v.actions = make(keyActions)
v.SetFixed(1, 0)
v.SetBorder(true)
v.SetFixed(1, 0)
v.SetBorderColor(tcell.ColorDodgerBlue)
v.SetBackgroundColor(config.AsColor(app.styles.Style.Table.BgColor))
v.SetBorderColor(config.AsColor(app.styles.Style.Table.FgColor))
v.SetBorderFocusColor(config.AsColor(app.styles.Style.Border.FocusColor))
v.SetBorderAttributes(tcell.AttrBold)
v.SetBorderPadding(0, 0, 1, 1)
v.cmdBuff = newCmdBuff('/')
v.cmdBuff.addListener(app.cmdView)
v.cmdBuff.reset()
v.SetSelectable(true, false)
v.SetSelectedStyle(tcell.ColorBlack, tcell.ColorAqua, tcell.AttrBold)
v.SetSelectedStyle(
tcell.ColorBlack,
config.AsColor(app.styles.Style.Table.CursorColor),
tcell.AttrBold,
)
v.SetInputCapture(v.keyboard)
v.bindKeys()
}
@ -205,7 +208,7 @@ func (v *tableView) activateCmd(evt *tcell.EventKey) *tcell.EventKey {
return evt
}
v.app.flash(flashInfo, "Filtering...")
v.app.flash(flashInfo, "Filter mode activated.")
v.cmdBuff.reset()
v.cmdBuff.setActive(true)
@ -227,13 +230,13 @@ func (v *tableView) setColorer(f colorerFn) {
// SetActions sets up keyboard action listener.
func (v *tableView) setActions(aa keyActions) {
v.tableMX.Lock()
{
for k, a := range aa {
v.actions[k] = a
}
// v.mx.Lock()
// {
for k, a := range aa {
v.actions[k] = a
}
v.tableMX.Unlock()
// }
// v.mx.Unlock()
}
// Hints options
@ -251,16 +254,12 @@ func (v *tableView) refresh() {
// Update table content
func (v *tableView) update(data resource.TableData) {
v.refreshMX.Lock()
{
v.data = data
if !v.cmdBuff.empty() {
v.doUpdate(v.filtered())
} else {
v.doUpdate(data)
}
v.data = data
if !v.cmdBuff.empty() {
v.doUpdate(v.filtered())
} else {
v.doUpdate(v.data)
}
v.refreshMX.Unlock()
v.resetTitle()
}
@ -300,7 +299,7 @@ func (v *tableView) sortIndicator(index int, name string) string {
if v.sortCol.asc {
order = "↑"
}
return fmt.Sprintf("%s [green::]%s[::]", name, order)
return fmt.Sprintf("%s [%s::]%s[::]", name, v.app.styles.Style.Table.Header.SorterColor, order)
}
func (v *tableView) doUpdate(data resource.TableData) {
@ -326,7 +325,11 @@ func (v *tableView) doUpdate(data resource.TableData) {
}
pads := make(maxyPad, len(data.Header))
// v.mx.Lock()
// {
computeMaxColumns(pads, v.sortCol.index, data)
// }
// v.mx.Unlock()
var row int
for col, h := range data.Header {
v.addHeaderCell(col, h, pads)
@ -340,7 +343,7 @@ func (v *tableView) doUpdate(data resource.TableData) {
prim, sec := v.sortAllRows(data.Rows, sortFn)
for _, pk := range prim {
for _, sk := range sec[pk] {
fgColor := tcell.ColorGray
fgColor := config.AsColor(v.app.styles.Style.Table.FgColor)
if v.colorerFn != nil {
fgColor = v.colorerFn(data.Namespace, data.Rows[sk])
}
@ -381,7 +384,8 @@ func (v *tableView) addHeaderCell(col int, name string, pads maxyPad) {
c := tview.NewTableCell(v.sortIndicator(col, name))
{
c.SetExpansion(1)
c.SetTextColor(tcell.ColorAntiqueWhite)
c.SetTextColor(config.AsColor(v.app.styles.Style.Table.Header.FgColor))
c.SetBackgroundColor(config.AsColor(v.app.styles.Style.Table.Header.BgColor))
}
v.SetCell(0, col, c)
}
@ -438,17 +442,28 @@ func (v *tableView) resetTitle() {
}
switch v.currentNS {
case resource.NotNamespaced, "*":
title = fmt.Sprintf(titleFmt, v.baseTitle, rc)
fmat := strings.Replace(titleFmt, "[fg", "["+v.app.styles.Style.Title.FgColor, -1)
fmat = strings.Replace(fmat, ":bg:", ":"+v.app.styles.Style.Title.BgColor+":", -1)
fmat = strings.Replace(fmat, "[count", "["+v.app.styles.Style.Title.CounterColor, 1)
title = fmt.Sprintf(fmat, v.baseTitle, rc)
default:
ns := v.currentNS
if v.currentNS == resource.AllNamespaces {
ns = resource.AllNamespace
}
title = fmt.Sprintf(nsTitleFmt, v.baseTitle, ns, rc)
fmat := strings.Replace(nsTitleFmt, "[fg", "["+v.app.styles.Style.Title.FgColor, -1)
fmat = strings.Replace(fmat, ":bg:", ":"+v.app.styles.Style.Title.BgColor+":", -1)
fmat = strings.Replace(fmat, "[hilite", "["+v.app.styles.Style.Title.HighlightColor, 1)
fmat = strings.Replace(fmat, "[count", "["+v.app.styles.Style.Title.CounterColor, 1)
title = fmt.Sprintf(fmat, v.baseTitle, ns, rc)
}
if !v.cmdBuff.isActive() && !v.cmdBuff.empty() {
title += fmt.Sprintf(searchFmt, v.cmdBuff)
fmat := strings.Replace(searchFmt, "[fg", "["+v.app.styles.Style.Title.FgColor, 1)
fmat = strings.Replace(fmat, ":bg:", ":"+v.app.styles.Style.Title.BgColor+":", -1)
fmat = strings.Replace(fmat, "[filter", "["+v.app.styles.Style.Title.FilterColor, 1)
title += fmt.Sprintf(fmat, v.cmdBuff)
}
v.SetTitle(title)
}

View File

@ -4,6 +4,8 @@ import (
"fmt"
"regexp"
"strings"
"github.com/derailed/k9s/internal/config"
)
var (
@ -11,24 +13,39 @@ var (
keyRX = regexp.MustCompile(`\A(\s*)([\w|\-|\.|\s]+):\s*\z`)
)
func colorizeYAML(raw string) string {
const (
yamlFullFmt = "%s[key::b]%s[colon::-]: [val::]%s"
yamlKeyFmt = "%s[key::b]%s[colon::-]:"
yamlValueFmt = "[val::]%s"
)
func colorizeYAML(style *config.Style, raw string) string {
lines := strings.Split(raw, "\n")
fullFmt := strings.Replace(yamlFullFmt, "[key", "["+style.Yaml.KeyColor, 1)
fullFmt = strings.Replace(fullFmt, "[colon", "["+style.Yaml.ColonColor, 1)
fullFmt = strings.Replace(fullFmt, "[val", "["+style.Yaml.ValueColor, 1)
keyFmt := strings.Replace(yamlKeyFmt, "[key", "["+style.Yaml.KeyColor, 1)
keyFmt = strings.Replace(keyFmt, "[colon", "["+style.Yaml.ColonColor, 1)
valFmt := strings.Replace(yamlValueFmt, "[val", "["+style.Yaml.ValueColor, 1)
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]))
buff = append(buff, fmt.Sprintf(fullFmt, 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]))
buff = append(buff, fmt.Sprintf(keyFmt, res[1], res[2]))
continue
}
buff = append(buff, fmt.Sprintf("[papayawhip::]%s", l))
buff = append(buff, fmt.Sprintf(valFmt, l))
}
return strings.Join(buff, "\n")

View File

@ -3,6 +3,7 @@ package views
import (
"testing"
"github.com/derailed/k9s/internal/config"
"github.com/stretchr/testify/assert"
)
@ -40,7 +41,8 @@ func TestYaml(t *testing.T) {
},
}
s, _ := config.NewStyles()
for _, u := range uu {
assert.Equal(t, u.e, colorizeYAML(u.s))
assert.Equal(t, u.e, colorizeYAML(s.Style, u.s))
}
}

43
skins/black_and_wtf.yml Normal file
View File

@ -0,0 +1,43 @@
k9s:
fgColor: white
bgColor: black
logoColor: white
info:
fgColor: navajowhite
sectionColor: white
border:
fgColor: white
focusColor: white
menu:
fgColor: white
keyColor: white
numKeyColor: navajowhite
crumb:
fgColor: black
bgColor: navajowhite
activeColor: whitesmoke
table:
fgColor: white
bgColor: black
cursorColor: white
header:
fgColor: darkgray
bgColor: black
sorterColor: white
status:
newColor: ghostwhite
modifyColor: navajowhite
addColor: darkslategray
errorColor: whitesmoke
highlightcolor: dimgray
killColor: slategray
completedColor: gray
title:
fgColor: ghostwhite
highlightColor: navajowhite
counterColor: navajowhite
filterColor: slategray
yaml:
keyColor: ghostwhite
colorColor: slategray
valueColor: navajowhite

44
skins/in_the_navy.yml Normal file
View File

@ -0,0 +1,44 @@
k9s:
fgColor: dodgerblue
bgColor: white
logoColor: blue
info:
fgColor: lightskyblue
sectionColor: steelblue
border:
fgColor: dodgerblue
focusColor: aliceblue
menu:
fgColor: darkblue
keyColor: cornflowerblue
numKeyColor: cadetblue
crumb:
fgColor: white
bgColor: steelblue
activeColor: skyblue
table:
fgColor: blue
bgColor: darkblue
cursorColor: aqua
header:
fgColor: white
bgColor: darkblue
sorterColor: orange
status:
newColor: blue
modifyColor: powderblue
addColor: lightskyblue
errorColor: indianred
highlightcolor: royalblue
killColor: slategray
completedColor: gray
title:
fgColor: aqua
bgColor: white
highlightColor: skyblue
counterColor: slateblue
filterColor: slategray
yaml:
keyColor: steelblue
colorColor: blue
valueColor: royalblue

30
skins/stock.yml Normal file
View File

@ -0,0 +1,30 @@
k9s:
fgColor: dodgerblue
bgColor: black
logoColor: orange
info:
fgColor: white
sectionColor: dodgerblue
border:
fgColor: dodgerblue
focusColor: aqua
menu:
fgColor: white
keyColor: dodgerblue
numKeyColor: fuchsia
crumb:
fgColor: black
bgColor: steelblue
activeColor: orange
table:
fgColor: blue
bgColor: black
cursorColor: aqua
header:
fgColor: aqua
bgColor: black
sorterColor: orange
yaml:
keyColor: steelblue
colonColor: white
valueColor: papayawhip