k9s/internal/config/k9s.go

355 lines
8.4 KiB
Go

// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of K9s
package config
import (
"errors"
"path/filepath"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config/data"
)
// K9s tracks K9s configuration options.
type K9s struct {
LiveViewAutoRefresh bool `yaml:"liveViewAutoRefresh"`
ScreenDumpDir string `yaml:"screenDumpDir,omitempty"`
RefreshRate int `yaml:"refreshRate"`
MaxConnRetry int `yaml:"maxConnRetry"`
ReadOnly bool `yaml:"readOnly"`
NoExitOnCtrlC bool `yaml:"noExitOnCtrlC"`
UI UI `yaml:"ui"`
SkipLatestRevCheck bool `yaml:"skipLatestRevCheck"`
DisablePodCounting bool `yaml:"disablePodCounting"`
ShellPod *ShellPod `yaml:"shellPod"`
ImageScans *ImageScans `yaml:"imageScans"`
Logger *Logger `yaml:"logger"`
Thresholds Threshold `yaml:"thresholds"`
manualRefreshRate int
manualHeadless *bool
manualLogoless *bool
manualCrumbsless *bool
manualReadOnly *bool
manualCommand *string
manualScreenDumpDir *string
dir *data.Dir
activeContextName string
activeConfig *data.Config
conn client.Connection
ks data.KubeSettings
}
// NewK9s create a new K9s configuration.
func NewK9s(conn client.Connection, ks data.KubeSettings) *K9s {
return &K9s{
RefreshRate: defaultRefreshRate,
MaxConnRetry: defaultMaxConnRetry,
ScreenDumpDir: AppDumpsDir,
Logger: NewLogger(),
Thresholds: NewThreshold(),
ShellPod: NewShellPod(),
ImageScans: NewImageScans(),
dir: data.NewDir(AppContextsDir),
conn: conn,
ks: ks,
}
}
func (k *K9s) resetConnection(conn client.Connection) {
k.conn = conn
}
// Save saves the k9s config to dis.
func (k *K9s) Save() error {
if k.activeConfig != nil {
path := filepath.Join(
AppContextsDir,
data.SanitizeContextSubpath(k.activeConfig.Context.ClusterName, k.activeContextName),
data.MainConfigFile,
)
return k.activeConfig.Save(path)
}
return nil
}
// Refine merges k9s configs.
func (k *K9s) Refine(k1 *K9s) {
k.LiveViewAutoRefresh = k1.LiveViewAutoRefresh
k.ScreenDumpDir = k1.ScreenDumpDir
k.RefreshRate = k1.RefreshRate
k.MaxConnRetry = k1.MaxConnRetry
k.ReadOnly = k1.ReadOnly
k.NoExitOnCtrlC = k1.NoExitOnCtrlC
k.UI = k1.UI
k.SkipLatestRevCheck = k1.SkipLatestRevCheck
k.DisablePodCounting = k1.DisablePodCounting
k.ShellPod = k1.ShellPod
k.ImageScans = k1.ImageScans
k.Logger = k1.Logger
k.Thresholds = k1.Thresholds
}
// Override overrides k9s config from cli args.
func (k *K9s) Override(k9sFlags *Flags) {
if *k9sFlags.RefreshRate != DefaultRefreshRate {
k.OverrideRefreshRate(*k9sFlags.RefreshRate)
}
k.OverrideHeadless(*k9sFlags.Headless)
k.OverrideLogoless(*k9sFlags.Logoless)
k.OverrideCrumbsless(*k9sFlags.Crumbsless)
k.OverrideReadOnly(*k9sFlags.ReadOnly)
k.OverrideWrite(*k9sFlags.Write)
k.OverrideCommand(*k9sFlags.Command)
k.OverrideScreenDumpDir(*k9sFlags.ScreenDumpDir)
}
// OverrideScreenDumpDir set the screen dump dir manually.
func (k *K9s) OverrideScreenDumpDir(dir string) {
k.manualScreenDumpDir = &dir
}
// GetScreenDumpDir fetch screen dumps dir.
func (k *K9s) GetScreenDumpDir() string {
screenDumpDir := k.ScreenDumpDir
if k.manualScreenDumpDir != nil && *k.manualScreenDumpDir != "" {
screenDumpDir = *k.manualScreenDumpDir
}
if screenDumpDir == "" {
screenDumpDir = AppDumpsDir
}
return screenDumpDir
}
// Reset resets configuration and context.
func (k *K9s) Reset() {
k.activeConfig, k.activeContextName = nil, ""
}
// ActiveScreenDumpsDir fetch context specific screen dumps dir.
func (k *K9s) ActiveScreenDumpsDir() string {
return filepath.Join(k.GetScreenDumpDir(), k.ActiveContextDir())
}
// ActiveContextDir fetch current cluster/context path.
func (k *K9s) ActiveContextDir() string {
if k.activeConfig == nil {
return "na"
}
return data.SanitizeContextSubpath(
k.activeConfig.Context.ClusterName,
k.ActiveContextName(),
)
}
// ActiveContextNamespace fetch the context active ns.
func (k *K9s) ActiveContextNamespace() (string, error) {
if k.activeConfig != nil {
return k.activeConfig.Context.Namespace.Active, nil
}
return "", errors.New("context config is not set")
}
// ActiveContextName returns the active context name.
func (k *K9s) ActiveContextName() string {
return k.activeContextName
}
// ActiveContext returns the currently active context.
func (k *K9s) ActiveContext() (*data.Context, error) {
if k.activeConfig != nil {
if k.activeConfig.Context == nil {
ct, err := k.ks.CurrentContext()
if err != nil {
return nil, err
}
k.activeConfig.Context = data.NewContextFromConfig(ct)
}
return k.activeConfig.Context, nil
}
ct, err := k.ActivateContext(k.activeContextName)
if err != nil {
return nil, err
}
return ct, nil
}
// ActivateContext initializes the active context is not present.
func (k *K9s) ActivateContext(n string) (*data.Context, error) {
k.activeContextName = n
ct, err := k.ks.GetContext(n)
if err != nil {
return nil, err
}
k.activeConfig, err = k.dir.Load(n, ct)
if err != nil {
return nil, err
}
// If the context specifies a default namespace, use it!
if k.conn != nil {
k.Validate(k.conn, k.ks)
if ns := k.conn.ActiveNamespace(); ns != client.BlankNamespace {
k.activeConfig.Context.Namespace.Active = ns
} else {
k.activeConfig.Context.Namespace.Active = client.DefaultNamespace
}
}
return k.activeConfig.Context, nil
}
// Reload reloads the active config from disk.
func (k *K9s) Reload() error {
ct, err := k.ks.GetContext(k.activeContextName)
if err != nil {
return err
}
k.activeConfig, err = k.dir.Load(k.activeContextName, ct)
if err != nil {
return err
}
return nil
}
// OverrideRefreshRate set the refresh rate manually.
func (k *K9s) OverrideRefreshRate(r int) {
k.manualRefreshRate = r
}
// OverrideHeadless toggle the header manually.
func (k *K9s) OverrideHeadless(b bool) {
k.manualHeadless = &b
}
// OverrideLogoless toggle the k9s logo manually.
func (k *K9s) OverrideLogoless(b bool) {
k.manualLogoless = &b
}
// OverrideCrumbsless tooh the crumbslessness manually.
func (k *K9s) OverrideCrumbsless(b bool) {
k.manualCrumbsless = &b
}
// OverrideReadOnly set the readonly mode manually.
func (k *K9s) OverrideReadOnly(b bool) {
if b {
k.manualReadOnly = &b
}
}
// OverrideWrite set the write mode manually.
func (k *K9s) OverrideWrite(b bool) {
if b {
var flag bool
k.manualReadOnly = &flag
}
}
// OverrideCommand set the command manually.
func (k *K9s) OverrideCommand(cmd string) {
k.manualCommand = &cmd
}
// IsHeadless returns headless setting.
func (k *K9s) IsHeadless() bool {
h := k.UI.Headless
if k.manualHeadless != nil && *k.manualHeadless {
h = *k.manualHeadless
}
return h
}
// IsLogoless returns logoless setting.
func (k *K9s) IsLogoless() bool {
h := k.UI.Logoless
if k.manualLogoless != nil && *k.manualLogoless {
h = *k.manualLogoless
}
return h
}
// IsCrumbsless returns crumbsless setting.
func (k *K9s) IsCrumbsless() bool {
h := k.UI.Crumbsless
if k.manualCrumbsless != nil && *k.manualCrumbsless {
h = *k.manualCrumbsless
}
return h
}
// GetRefreshRate returns the current refresh rate.
func (k *K9s) GetRefreshRate() int {
rate := k.RefreshRate
if k.manualRefreshRate != 0 {
rate = k.manualRefreshRate
}
return rate
}
// IsReadOnly returns the readonly setting.
func (k *K9s) IsReadOnly() bool {
readOnly := k.ReadOnly
if k.manualReadOnly != nil {
readOnly = *k.manualReadOnly
}
if k.activeConfig != nil && k.activeConfig.Context.ReadOnly {
readOnly = true
}
return readOnly
}
func (k *K9s) validateDefaults() {
if k.RefreshRate <= 0 {
k.RefreshRate = defaultRefreshRate
}
if k.MaxConnRetry <= 0 {
k.MaxConnRetry = defaultMaxConnRetry
}
}
// Validate the current configuration.
func (k *K9s) Validate(c client.Connection, ks data.KubeSettings) {
k.validateDefaults()
if k.activeConfig == nil {
if n, err := ks.CurrentContextName(); err == nil {
_, _ = k.ActivateContext(n)
}
}
if k.ImageScans == nil {
k.ImageScans = NewImageScans()
}
if k.ShellPod == nil {
k.ShellPod = NewShellPod()
}
k.ShellPod.Validate()
if k.Logger == nil {
k.Logger = NewLogger()
} else {
k.Logger.Validate()
}
if k.Thresholds == nil {
k.Thresholds = NewThreshold()
}
k.Thresholds.Validate()
if k.activeConfig != nil {
k.activeConfig.Validate(c, ks)
}
}