parent
f0b56964ea
commit
56a520c2d1
|
|
@ -45,7 +45,7 @@ type APIClient struct {
|
||||||
config *Config
|
config *Config
|
||||||
mx sync.Mutex
|
mx sync.Mutex
|
||||||
cache *cache.LRUExpireCache
|
cache *cache.LRUExpireCache
|
||||||
metricsAPI bool
|
connOK bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTestClient for testing ONLY!!
|
// NewTestClient for testing ONLY!!
|
||||||
|
|
@ -63,7 +63,7 @@ func InitConnectionOrDie(config *Config) *APIClient {
|
||||||
config: config,
|
config: config,
|
||||||
cache: cache.NewLRUExpireCache(cacheSize),
|
cache: cache.NewLRUExpireCache(cacheSize),
|
||||||
}
|
}
|
||||||
a.metricsAPI = a.supportsMetricsResources()
|
_ = a.supportsMetricsResources()
|
||||||
return &a
|
return &a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,12 +168,21 @@ func (a *APIClient) ServerVersion() (*version.Info, error) {
|
||||||
|
|
||||||
// ValidNamespaces returns all available namespaces.
|
// ValidNamespaces returns all available namespaces.
|
||||||
func (a *APIClient) ValidNamespaces() ([]v1.Namespace, error) {
|
func (a *APIClient) ValidNamespaces() ([]v1.Namespace, error) {
|
||||||
|
if nn, ok := a.cache.Get("validNamespaces"); ok {
|
||||||
|
if nss, ok := nn.([]v1.Namespace); ok {
|
||||||
|
return nss, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Debug().Msgf(">>>>> Loading all namespaces")
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), CallTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), CallTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
nn, err := a.DialOrDie().CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
|
nn, err := a.DialOrDie().CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.cache.Add("validNamespaces", nn.Items, cacheExpiry)
|
||||||
|
|
||||||
return nn.Items, nil
|
return nn.Items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,6 +195,7 @@ func (a *APIClient) CheckConnectivity() (status bool) {
|
||||||
if !status {
|
if !status {
|
||||||
a.clearCache()
|
a.clearCache()
|
||||||
}
|
}
|
||||||
|
a.connOK = status
|
||||||
}()
|
}()
|
||||||
|
|
||||||
cfg, err := a.config.flags.ToRESTConfig()
|
cfg, err := a.config.flags.ToRESTConfig()
|
||||||
|
|
@ -201,7 +211,10 @@ func (a *APIClient) CheckConnectivity() (status bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := client.ServerVersion(); err == nil {
|
if _, err := client.ServerVersion(); err == nil {
|
||||||
a.reset()
|
if !a.connOK {
|
||||||
|
log.Debug().Msgf("RESETING CON!!")
|
||||||
|
a.reset()
|
||||||
|
}
|
||||||
status = true
|
status = true
|
||||||
} else {
|
} else {
|
||||||
log.Error().Err(err).Msgf("K9s can't connect to cluster")
|
log.Error().Err(err).Msgf("K9s can't connect to cluster")
|
||||||
|
|
@ -332,7 +345,7 @@ func (a *APIClient) SwitchContext(ctx string) error {
|
||||||
}
|
}
|
||||||
a.clearCache()
|
a.clearCache()
|
||||||
a.reset()
|
a.reset()
|
||||||
a.metricsAPI = a.supportsMetricsResources()
|
_ = a.supportsMetricsResources()
|
||||||
ResetMetrics()
|
ResetMetrics()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import "github.com/derailed/k9s/internal/client"
|
import (
|
||||||
|
"github.com/derailed/k9s/internal/client"
|
||||||
|
)
|
||||||
|
|
||||||
// Cluster tracks K9s cluster configuration.
|
// Cluster tracks K9s cluster configuration.
|
||||||
type Cluster struct {
|
type Cluster struct {
|
||||||
|
|
@ -23,7 +25,6 @@ func (c *Cluster) Validate(conn client.Connection, ks KubeSettings) {
|
||||||
if c.Namespace == nil {
|
if c.Namespace == nil {
|
||||||
c.Namespace = NewNamespace()
|
c.Namespace = NewNamespace()
|
||||||
}
|
}
|
||||||
c.Namespace.Validate(conn, ks)
|
|
||||||
|
|
||||||
if c.FeatureGates == nil {
|
if c.FeatureGates == nil {
|
||||||
c.FeatureGates = NewFeatureGates()
|
c.FeatureGates = NewFeatureGates()
|
||||||
|
|
|
||||||
|
|
@ -132,13 +132,22 @@ func (c *Config) ActiveNamespace() string {
|
||||||
return "default"
|
return "default"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Config) ValidateFavorites() {
|
||||||
|
cl := c.K9s.ActiveCluster()
|
||||||
|
if cl == nil {
|
||||||
|
cl = NewCluster()
|
||||||
|
}
|
||||||
|
cl.Validate(c.client, c.settings)
|
||||||
|
cl.Namespace.Validate(c.client, c.settings)
|
||||||
|
}
|
||||||
|
|
||||||
// FavNamespaces returns fav namespaces in the current cluster.
|
// FavNamespaces returns fav namespaces in the current cluster.
|
||||||
func (c *Config) FavNamespaces() []string {
|
func (c *Config) FavNamespaces() []string {
|
||||||
cl := c.K9s.ActiveCluster()
|
cl := c.K9s.ActiveCluster()
|
||||||
if cl != nil {
|
if cl == nil {
|
||||||
return c.K9s.ActiveCluster().Namespace.Favorites
|
return nil
|
||||||
}
|
}
|
||||||
return []string{}
|
return c.K9s.ActiveCluster().Namespace.Favorites
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetActiveNamespace set the active namespace in the current cluster.
|
// SetActiveNamespace set the active namespace in the current cluster.
|
||||||
|
|
|
||||||
|
|
@ -288,8 +288,10 @@ var expectedConfig = `k9s:
|
||||||
active: default
|
active: default
|
||||||
favorites:
|
favorites:
|
||||||
- default
|
- default
|
||||||
|
- kube-public
|
||||||
- istio-system
|
- istio-system
|
||||||
- all
|
- all
|
||||||
|
- kube-system
|
||||||
view:
|
view:
|
||||||
active: po
|
active: po
|
||||||
featureGates:
|
featureGates:
|
||||||
|
|
@ -299,8 +301,10 @@ var expectedConfig = `k9s:
|
||||||
active: kube-system
|
active: kube-system
|
||||||
favorites:
|
favorites:
|
||||||
- default
|
- default
|
||||||
|
- kube-public
|
||||||
- istio-system
|
- istio-system
|
||||||
- all
|
- all
|
||||||
|
- kube-system
|
||||||
view:
|
view:
|
||||||
active: ctx
|
active: ctx
|
||||||
featureGates:
|
featureGates:
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ func (k *K9s) validateDefaults() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *K9s) checkClusters(c client.Connection, ks KubeSettings) {
|
func (k *K9s) validateClusters(c client.Connection, ks KubeSettings) {
|
||||||
cc, err := ks.ClusterNames()
|
cc, err := ks.ClusterNames()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
@ -123,7 +123,7 @@ func (k *K9s) Validate(c client.Connection, ks KubeSettings) {
|
||||||
if k.Clusters == nil {
|
if k.Clusters == nil {
|
||||||
k.Clusters = map[string]*Cluster{}
|
k.Clusters = map[string]*Cluster{}
|
||||||
}
|
}
|
||||||
k.checkClusters(c, ks)
|
k.validateClusters(c, ks)
|
||||||
|
|
||||||
if k.Logger == nil {
|
if k.Logger == nil {
|
||||||
k.Logger = NewLogger()
|
k.Logger = NewLogger()
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,6 @@ func (c *Container) List(ctx context.Context, _ string) ([]runtime.Object, error
|
||||||
|
|
||||||
// TailLogs tails a given container logs
|
// TailLogs tails a given container logs
|
||||||
func (c *Container) TailLogs(ctx context.Context, logChan LogChan, opts LogOptions) error {
|
func (c *Container) TailLogs(ctx context.Context, logChan LogChan, opts LogOptions) error {
|
||||||
log.Debug().Msgf("CONTAINER-LOGS")
|
|
||||||
po := Pod{}
|
po := Pod{}
|
||||||
po.Init(c.Factory, client.NewGVR("v1/pods"))
|
po.Init(c.Factory, client.NewGVR("v1/pods"))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ type LogChan chan *LogItem
|
||||||
// LogItem represents a container log line.
|
// LogItem represents a container log line.
|
||||||
type LogItem struct {
|
type LogItem struct {
|
||||||
Pod, Container, Timestamp string
|
Pod, Container, Timestamp string
|
||||||
|
SingleContainer bool
|
||||||
Bytes []byte
|
Bytes []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,7 +79,7 @@ func (l *LogItem) Render(c int, showTime bool) []byte {
|
||||||
bb = append(bb, []byte(colorize(l.Pod, c))...)
|
bb = append(bb, []byte(colorize(l.Pod, c))...)
|
||||||
bb = append(bb, ':')
|
bb = append(bb, ':')
|
||||||
}
|
}
|
||||||
if l.Container != "" {
|
if !l.SingleContainer && l.Container != "" {
|
||||||
bb = append(bb, []byte(colorize(l.Container, c))...)
|
bb = append(bb, []byte(colorize(l.Container, c))...)
|
||||||
bb = append(bb, ' ')
|
bb = append(bb, ' ')
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,10 @@ func (o LogOptions) DecorateLog(bytes []byte) *LogItem {
|
||||||
if len(bytes) == 0 {
|
if len(bytes) == 0 {
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
item.SingleContainer = o.SingleContainer
|
||||||
|
if item.SingleContainer {
|
||||||
|
item.Container = o.Container
|
||||||
|
}
|
||||||
if o.MultiPods {
|
if o.MultiPods {
|
||||||
_, pod := client.Namespaced(o.Path)
|
_, pod := client.Namespaced(o.Path)
|
||||||
item.Pod, item.Container = pod, o.Container
|
item.Pod, item.Container = pod, o.Container
|
||||||
|
|
|
||||||
|
|
@ -187,10 +187,7 @@ func (p *Pod) TailLogs(ctx context.Context, c LogChan, opts LogOptions) error {
|
||||||
|
|
||||||
if opts.HasContainer() {
|
if opts.HasContainer() {
|
||||||
opts.SingleContainer = true
|
opts.SingleContainer = true
|
||||||
if err := tailLogs(ctx, p, c, opts); err != nil {
|
return tailLogs(ctx, p, c, opts)
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
if len(po.Spec.InitContainers)+len(po.Spec.Containers) == 1 {
|
if len(po.Spec.InitContainers)+len(po.Spec.Containers) == 1 {
|
||||||
opts.SingleContainer = true
|
opts.SingleContainer = true
|
||||||
|
|
|
||||||
|
|
@ -74,10 +74,14 @@ func (l *Log) Configure(opts *config.Logger) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPath returns resource path.
|
// GetPath returns resource path.
|
||||||
func (l *Log) GetPath() string { return l.logOptions.Path }
|
func (l *Log) GetPath() string {
|
||||||
|
return l.logOptions.Path
|
||||||
|
}
|
||||||
|
|
||||||
// GetContainer returns the resource container if any or "" otherwise.
|
// GetContainer returns the resource container if any or "" otherwise.
|
||||||
func (l *Log) GetContainer() string { return l.logOptions.Container }
|
func (l *Log) GetContainer() string {
|
||||||
|
return l.logOptions.Container
|
||||||
|
}
|
||||||
|
|
||||||
// Init initializes the model.
|
// Init initializes the model.
|
||||||
func (l *Log) Init(f dao.Factory) {
|
func (l *Log) Init(f dao.Factory) {
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ func (a *App) initSignals() {
|
||||||
|
|
||||||
go func(sig chan os.Signal) {
|
go func(sig chan os.Signal) {
|
||||||
<-sig
|
<-sig
|
||||||
a.BailOut()
|
nukeK9sShell(a.Conn())
|
||||||
}(sig)
|
}(sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -296,17 +296,35 @@ func (a *App) refreshCluster() {
|
||||||
a.clusterModel.Refresh()
|
a.clusterModel.Refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) switchNS(ns string) bool {
|
func (a *App) switchNS(ns string) error {
|
||||||
if ns == client.ClusterScope {
|
if ns == client.ClusterScope {
|
||||||
ns = client.AllNamespaces
|
ns = client.AllNamespaces
|
||||||
}
|
}
|
||||||
|
if !a.isValidNS(ns) {
|
||||||
|
return fmt.Errorf("Invalid namespace %q", ns)
|
||||||
|
}
|
||||||
if err := a.Config.SetActiveNamespace(ns); err != nil {
|
if err := a.Config.SetActiveNamespace(ns); err != nil {
|
||||||
log.Error().Err(err).Msg("Config Set NS failed!")
|
return fmt.Errorf("Unable to save active namespace in config")
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
a.factory.SetActiveNS(ns)
|
a.factory.SetActiveNS(ns)
|
||||||
|
|
||||||
return true
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) isValidNS(ns string) bool {
|
||||||
|
if ns == client.AllNamespaces || ns == client.NamespaceAll {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
nn, err := a.Conn().ValidNamespaces()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, n := range nn {
|
||||||
|
if n.Name == ns {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) switchCtx(name string, loadPods bool) error {
|
func (a *App) switchCtx(name string, loadPods bool) error {
|
||||||
|
|
@ -323,10 +341,11 @@ func (a *App) switchCtx(name string, loadPods bool) error {
|
||||||
if err := a.command.Reset(true); err != nil {
|
if err := a.command.Reset(true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a.Config.Reset()
|
|
||||||
if err := a.Config.Save(); err != nil {
|
if err := a.Config.Save(); err != nil {
|
||||||
log.Error().Err(err).Msg("Config save failed!")
|
log.Error().Err(err).Msg("Config save failed!")
|
||||||
}
|
}
|
||||||
|
a.Config.Reset()
|
||||||
|
|
||||||
a.Flash().Infof("Switching context to %s", name)
|
a.Flash().Infof("Switching context to %s", name)
|
||||||
a.ReloadStyles(name)
|
a.ReloadStyles(name)
|
||||||
v := a.Config.ActiveView()
|
v := a.Config.ActiveView()
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,11 @@ func (b *Browser) SetInstance(path string) {
|
||||||
|
|
||||||
// Start initializes browser updates.
|
// Start initializes browser updates.
|
||||||
func (b *Browser) Start() {
|
func (b *Browser) Start() {
|
||||||
|
b.app.Config.ValidateFavorites()
|
||||||
|
if err := b.app.Config.Save(); err != nil {
|
||||||
|
log.Error().Err(err).Msgf("Config Save")
|
||||||
|
}
|
||||||
|
|
||||||
b.Stop()
|
b.Stop()
|
||||||
b.Table.Start()
|
b.Table.Start()
|
||||||
b.CmdBuff().AddListener(b)
|
b.CmdBuff().AddListener(b)
|
||||||
|
|
@ -359,7 +364,10 @@ func (b *Browser) switchNamespaceCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
b.app.switchNS(ns)
|
if err := b.app.switchNS(ns); err != nil {
|
||||||
|
b.App().Flash().Err(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
b.setNamespace(ns)
|
b.setNamespace(ns)
|
||||||
b.app.Flash().Infof("Viewing namespace `%s`...", ns)
|
b.app.Flash().Infof("Viewing namespace `%s`...", ns)
|
||||||
b.refresh()
|
b.refresh()
|
||||||
|
|
|
||||||
|
|
@ -147,8 +147,8 @@ func (c *Command) run(cmd, path string, clearStack bool) error {
|
||||||
if len(cmds) == 2 {
|
if len(cmds) == 2 {
|
||||||
ns = cmds[1]
|
ns = cmds[1]
|
||||||
}
|
}
|
||||||
if !c.app.switchNS(ns) {
|
if err := c.app.switchNS(ns); err != nil {
|
||||||
return fmt.Errorf("namespace switch failed for ns %q", ns)
|
return err
|
||||||
}
|
}
|
||||||
if !c.alias.Check(cmds[0]) {
|
if !c.alias.Check(cmds[0]) {
|
||||||
return fmt.Errorf("Huh? `%s` Command not found", cmd)
|
return fmt.Errorf("Huh? `%s` Command not found", cmd)
|
||||||
|
|
@ -159,7 +159,7 @@ func (c *Command) run(cmd, path string, clearStack bool) error {
|
||||||
|
|
||||||
func (c *Command) defaultCmd() error {
|
func (c *Command) defaultCmd() error {
|
||||||
if err := c.run(c.app.Config.ActiveView(), "", true); err != nil {
|
if err := c.run(c.app.Config.ActiveView(), "", true); err != nil {
|
||||||
log.Error().Err(err).Msgf("Saved command failed. Loading default view")
|
log.Error().Err(err).Msgf("Saved command load failed. Loading default view")
|
||||||
return c.run("pod", "", true)
|
return c.run("pod", "", true)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -228,7 +228,22 @@ func (c *Command) componentFor(gvr, path string, v *MetaViewer) ResourceViewer {
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Command) exec(cmd, gvr string, comp model.Component, clearStack bool) error {
|
func (c *Command) exec(cmd, gvr string, comp model.Component, clearStack bool) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
c.app.Content.Dump()
|
||||||
|
log.Debug().Msgf("History %v", c.app.cmdHistory.List())
|
||||||
|
|
||||||
|
hh := c.app.cmdHistory.List()
|
||||||
|
if len(hh) == 0 {
|
||||||
|
_ = c.run("pod", "", true)
|
||||||
|
} else {
|
||||||
|
_ = c.run(hh[0], "", true)
|
||||||
|
}
|
||||||
|
err = fmt.Errorf("Invalid command %q", cmd)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
if comp == nil {
|
if comp == nil {
|
||||||
return fmt.Errorf("No component found for %s", gvr)
|
return fmt.Errorf("No component found for %s", gvr)
|
||||||
}
|
}
|
||||||
|
|
@ -246,5 +261,5 @@ func (c *Command) exec(cmd, gvr string, comp model.Component, clearStack bool) e
|
||||||
}
|
}
|
||||||
c.app.cmdHistory.Push(cmd)
|
c.app.cmdHistory.Push(cmd)
|
||||||
|
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,10 @@ func showPodsWithLabels(app *App, path string, sel map[string]string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func showPods(app *App, path, labelSel, fieldSel string) {
|
func showPods(app *App, path, labelSel, fieldSel string) {
|
||||||
app.switchNS(client.AllNamespaces)
|
if err := app.switchNS(client.AllNamespaces); err != nil {
|
||||||
|
app.Flash().Err(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
v := NewPod(client.NewGVR("v1/pods"))
|
v := NewPod(client.NewGVR("v1/pods"))
|
||||||
v.SetContextFn(podCtx(app, path, labelSel, fieldSel))
|
v.SetContextFn(podCtx(app, path, labelSel, fieldSel))
|
||||||
|
|
|
||||||
|
|
@ -59,13 +59,16 @@ func (n *Namespace) useNsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
||||||
func (n *Namespace) useNamespace(fqn string) {
|
func (n *Namespace) useNamespace(fqn string) {
|
||||||
_, ns := client.Namespaced(fqn)
|
_, ns := client.Namespaced(fqn)
|
||||||
log.Debug().Msgf("SWITCHING NS %q", ns)
|
if err := n.App().switchNS(ns); err != nil {
|
||||||
n.App().switchNS(ns)
|
n.App().Flash().Err(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
if err := n.App().Config.SetActiveNamespace(ns); err != nil {
|
if err := n.App().Config.SetActiveNamespace(ns); err != nil {
|
||||||
n.App().Flash().Err(err)
|
n.App().Flash().Err(err)
|
||||||
} else {
|
return
|
||||||
n.App().Flash().Infof("Namespace %s is now active!", ns)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n.App().Flash().Infof("Namespace %s is now active!", ns)
|
||||||
if err := n.App().Config.Save(); err != nil {
|
if err := n.App().Config.Save(); err != nil {
|
||||||
log.Error().Err(err).Msg("Config file save failed!")
|
log.Error().Err(err).Msg("Config file save failed!")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package view
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
|
|
@ -72,15 +73,17 @@ func DismissPortForwards(v ResourceViewer, p *ui.Pages) {
|
||||||
// Helpers...
|
// Helpers...
|
||||||
|
|
||||||
func extractPort(p string) string {
|
func extractPort(p string) string {
|
||||||
tokens := strings.Split(p, ":")
|
rx := regexp.MustCompile(`\A(\w+)/?(\w+)?:?(\d+)?(╱UDP)?\z`)
|
||||||
switch {
|
mm := rx.FindStringSubmatch(p)
|
||||||
case len(tokens) < 2:
|
if len(mm) != 5 {
|
||||||
return tokens[0]
|
return p
|
||||||
case len(tokens) == 2:
|
|
||||||
return strings.Replace(tokens[1], "╱UDP", "", 1)
|
|
||||||
default:
|
|
||||||
return tokens[1]
|
|
||||||
}
|
}
|
||||||
|
for i := 3; i > 0; i-- {
|
||||||
|
if mm[i] != "" {
|
||||||
|
return mm[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractContainer(p string) string {
|
func extractContainer(p string) string {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@ func TestExtractPort(t *testing.T) {
|
||||||
uu := map[string]struct {
|
uu := map[string]struct {
|
||||||
port, e string
|
port, e string
|
||||||
}{
|
}{
|
||||||
|
"empty": {
|
||||||
|
"", "",
|
||||||
|
},
|
||||||
"full": {
|
"full": {
|
||||||
"co/fred:8000", "8000",
|
"co/fred:8000", "8000",
|
||||||
},
|
},
|
||||||
|
|
@ -22,6 +25,9 @@ func TestExtractPort(t *testing.T) {
|
||||||
"protocol": {
|
"protocol": {
|
||||||
"dns:53╱UDP", "53",
|
"dns:53╱UDP", "53",
|
||||||
},
|
},
|
||||||
|
"unamed": {
|
||||||
|
"dns/53", "53",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for k := range uu {
|
for k := range uu {
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,10 @@ type Pod struct {
|
||||||
|
|
||||||
// NewPod returns a new viewer.
|
// NewPod returns a new viewer.
|
||||||
func NewPod(gvr client.GVR) ResourceViewer {
|
func NewPod(gvr client.GVR) ResourceViewer {
|
||||||
p := Pod{
|
p := Pod{}
|
||||||
ResourceViewer: NewPortForwardExtender(
|
p.ResourceViewer = NewPortForwardExtender(
|
||||||
NewLogsExtender(NewBrowser(gvr), nil),
|
NewLogsExtender(NewBrowser(gvr), p.selectedContainer),
|
||||||
),
|
)
|
||||||
}
|
|
||||||
p.SetBindKeysFn(p.bindKeys)
|
p.SetBindKeysFn(p.bindKeys)
|
||||||
p.GetTable().SetEnterFn(p.showContainers)
|
p.GetTable().SetEnterFn(p.showContainers)
|
||||||
p.GetTable().SetColorerFn(render.Pod{}.ColorerFunc())
|
p.GetTable().SetColorerFn(render.Pod{}.ColorerFunc())
|
||||||
|
|
@ -67,6 +66,23 @@ func (p *Pod) bindKeys(aa ui.KeyActions) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Pod) selectedContainer() string {
|
||||||
|
path := p.GetTable().GetSelectedItem()
|
||||||
|
if path == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
cc, err := fetchContainers(p.App().factory, path, true)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf("Fetch containers")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if len(cc) == 1 {
|
||||||
|
return cc[0]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Pod) showContainers(app *App, model ui.Tabular, gvr, path string) {
|
func (p *Pod) showContainers(app *App, model ui.Tabular, gvr, path string) {
|
||||||
co := NewContainer(client.NewGVR("containers"))
|
co := NewContainer(client.NewGVR("containers"))
|
||||||
co.SetContextFn(p.coContext)
|
co.SetContextFn(p.coContext)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue