k9s/views/config.go

233 lines
4.6 KiB
Go

package views
import (
"fmt"
"io/ioutil"
"os"
"os/user"
"path/filepath"
"github.com/derailed/k9s/resource"
"github.com/derailed/k9s/resource/k8s"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
v1 "k8s.io/api/core/v1"
)
const (
defaultRefreshRate = 2
defaultLogBufferSize = 200
defaultView = "po"
)
var (
k9sCfg = defaultK9sConfig()
// K9sHome represent K9s home directory.
K9sHome = filepath.Join(mustK9sHome(), ".k9s")
// K9sConfig represents K9s config file location.
K9sConfig = filepath.Join(K9sHome, "config.yml")
// K9sLogs represents K9s log.
K9sLogs = filepath.Join(os.TempDir(), fmt.Sprintf("k9s-%s.log", mustK9sUser()))
)
const maxFavorites = 5
type (
namespace struct {
Active string `yaml:"active"`
Favorites []string `yaml:"favorites"`
}
view struct {
Active string `yaml:"active"`
}
k9s struct {
RefreshRate int `yaml:"refreshRate"`
LogBufferSize int `yaml:"logBufferSize"`
Namespace namespace `yaml:"namespace"`
View view `yaml:"view"`
}
config struct {
K9s k9s `yaml:"k9s"`
}
)
func (c *config) load(path string) {
f, err := ioutil.ReadFile(path)
if err != nil {
log.Errorf("[Config] Unable to locate K9s configuration in %s. Using defaults", path)
}
if err = yaml.Unmarshal(f, k9sCfg); err != nil {
log.Errorf("[Config] hydrating K9s YAML: %v", err)
}
log.Debugf("[Config] Loaded K9s configuration `%s", c.K9s.Namespace.Active)
}
func (c *config) save(path string) error {
log.Debugf("[Config] Saving configuration `%s", c.K9s.Namespace.Active)
ensurePath(path, 0755)
cfg, err := yaml.Marshal(c)
if err != nil {
log.Errorf("[Config] Unable to save K9s config file: %v", err)
return err
}
return ioutil.WriteFile(path, cfg, 0644)
}
func (c *config) validateAndSave() error {
c.validate()
return c.save(K9sConfig)
}
func (c *config) validate() {
if c.K9s.RefreshRate <= 0 {
c.K9s.RefreshRate = defaultRefreshRate
}
if c.K9s.LogBufferSize <= 0 {
c.K9s.LogBufferSize = defaultLogBufferSize
}
nn, err := activeNamespaces()
if err != nil {
return
}
if !c.isAllNamespace() && !inList(nn, c.K9s.Namespace.Active) {
log.Debugf("[Config] Validation error active namepace reset to `default")
c.K9s.Namespace.Active = resource.DefaultNamespace
}
for _, f := range c.K9s.Namespace.Favorites {
if f != resource.AllNamespace && !inList(nn, f) {
log.Debugf("[Config] Invalid favorite found '%s' - %t", f, c.isAllNamespace())
c.rmFavNS(f)
}
}
}
func (c *config) isAllNamespace() bool {
return c.K9s.Namespace.Active == resource.AllNamespace
}
func (*config) reset() {
k9sCfg = defaultK9sConfig()
}
func (c *config) addActive(ns string) {
c.K9s.Namespace.Active = ns
c.addFavNS(ns)
}
func (c *config) addFavNS(ns string) {
fv := c.K9s.Namespace.Favorites
if inList(fv, ns) {
return
}
nfv := make([]string, 0, maxFavorites)
nfv = append(nfv, ns)
for i := 0; i < len(fv); i++ {
if i+1 < maxFavorites {
nfv = append(nfv, fv[i])
}
}
c.K9s.Namespace.Favorites = nfv
}
func (c *config) rmFavNS(ns string) {
fv, victim := c.K9s.Namespace.Favorites, -1
for i, f := range fv {
if f == ns {
victim = i
break
}
}
if victim < 0 {
return
}
fv = append(fv[:victim], fv[victim+1:]...)
c.K9s.Namespace.Favorites = fv
}
func defaultK9sConfig() *config {
return &config{
K9s: k9s{
RefreshRate: 5,
LogBufferSize: 200,
View: view{
Active: "po",
},
Namespace: namespace{
Active: resource.DefaultNamespace,
Favorites: []string{
resource.AllNamespace,
resource.DefaultNamespace,
"kube-system",
},
},
},
}
}
func inList(ll []string, n string) bool {
for _, l := range ll {
if l == n {
return true
}
}
return false
}
func inNSList(nn []interface{}, ns string) bool {
for _, n := range nn {
nsp := n.(v1.Namespace)
if nsp.Name == ns {
return true
}
}
return false
}
func activeNamespaces() ([]string, error) {
nn, err := k8s.NewNamespace().List(defaultNS)
if err != nil {
log.Errorf("Unable to retrieve active namespaces: %#v", err.Error())
return []string{}, err
}
ss := make([]string, len(nn))
for i, n := range nn {
ss[i] = n.(v1.Namespace).Name
}
return ss, nil
}
func mustK9sHome() string {
usr, err := user.Current()
if err != nil {
panic(err)
}
return usr.HomeDir
}
func mustK9sUser() string {
usr, err := user.Current()
if err != nil {
panic(err)
}
return usr.Username
}
func ensurePath(path string, mod os.FileMode) {
dir := filepath.Dir(path)
if _, err := os.Stat(dir); os.IsNotExist(err) {
if err = os.Mkdir(dir, mod); err != nil {
log.Errorf("Unable to create K9s home config dir: %v", err)
panic(err)
}
}
}