235 lines
4.7 KiB
Go
235 lines
4.7 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"
|
|
maxFavorites = 10
|
|
)
|
|
|
|
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()))
|
|
)
|
|
|
|
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 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)
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Seed...
|
|
|
|
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",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|