feat: allow for multiple plugin files in $XDG_DATA_DIRS/k9s/plugins (#2029)

mine
Chris Werner Rau 2023-11-12 19:35:46 +01:00 committed by GitHub
parent 32b9493a0d
commit 1bfd824ab4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 116 additions and 15 deletions

View File

@ -6,11 +6,14 @@ import (
"path/filepath"
"strings"
"github.com/adrg/xdg"
"github.com/rs/zerolog/log"
"gopkg.in/yaml.v2"
)
// K9sPlugins manages K9s plugins.
var K9sPlugins = filepath.Join(K9sHome(), "plugin.yml")
// K9sPluginsFilePath manages K9s plugins.
var K9sPluginsFilePath = filepath.Join(K9sHome(), "plugin.yml")
var K9sPluginDirectory = filepath.Join("k9s", "plugins")
// Plugins represents a collection of plugins.
type Plugins struct {
@ -42,23 +45,54 @@ func NewPlugins() Plugins {
// Load K9s plugins.
func (p Plugins) Load() error {
return p.LoadPlugins(K9sPlugins)
var pluginDirs []string
for _, dataDir := range xdg.DataDirs {
pluginDirs = append(pluginDirs, filepath.Join(dataDir, K9sPluginDirectory))
}
return p.LoadPlugins(K9sPluginsFilePath, pluginDirs)
}
// LoadPlugins loads plugins from a given file.
func (p Plugins) LoadPlugins(path string) error {
// LoadPlugins loads plugins from a given file and a set of plugin directories.
func (p Plugins) LoadPlugins(path string, pluginDirs []string) error {
f, err := os.ReadFile(path)
if err != nil {
return err
}
var pp Plugins
if err := yaml.Unmarshal(f, &pp); err != nil {
return err
}
for _, pluginDir := range pluginDirs {
pluginFiles, err := os.ReadDir(pluginDir)
if err != nil {
log.Warn().Msgf("Failed reading plugin path %s; %s", pluginDir, err)
continue
}
for _, file := range pluginFiles {
if file.IsDir() || !isYamlFile(file) {
continue
}
pluginFile, err := os.ReadFile(filepath.Join(pluginDir, file.Name()))
if err != nil {
return err
}
var plugin Plugin
if err = yaml.Unmarshal(pluginFile, &plugin); err != nil {
return err
}
p.Plugin[strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))] = plugin
}
}
for k, v := range pp.Plugin {
p.Plugin[k] = v
}
return nil
}
func isYamlFile(file os.DirEntry) bool {
ext := filepath.Ext(file.Name())
return ext == ".yml" || ext == ".yaml"
}

View File

@ -7,18 +7,61 @@ import (
"github.com/stretchr/testify/assert"
)
func TestPluginLoad(t *testing.T) {
var pluginYmlTestData = config.Plugin{
Scopes: []string{"po", "dp"},
Args: []string{"-n", "$NAMESPACE", "-boolean"},
ShortCut: "shift-s",
Description: "blee",
Command: "duh",
Confirm: true,
Background: false,
}
var test1YmlTestData = config.Plugin{
Scopes: []string{"po", "dp"},
Args: []string{"-n", "$NAMESPACE", "-boolean"},
ShortCut: "shift-s",
Description: "blee",
Command: "duh",
Confirm: true,
Background: false,
}
var test2YmlTestData = config.Plugin{
Scopes: []string{"svc", "ing"},
Args: []string{"-n", "$NAMESPACE", "-oyaml"},
ShortCut: "shift-r",
Description: "bla",
Command: "duha",
Confirm: false,
Background: true,
}
func TestSinglePluginFileLoad(t *testing.T) {
p := config.NewPlugins()
assert.Nil(t, p.LoadPlugins("testdata/plugin.yml"))
assert.Nil(t, p.LoadPlugins("testdata/plugin.yml", []string{"/random/dir/not/exist"}))
assert.Equal(t, 1, len(p.Plugin))
k, ok := p.Plugin["blah"]
assert.True(t, ok)
assert.Equal(t, "shift-s", k.ShortCut)
assert.True(t, k.Confirm)
assert.Equal(t, "blee", k.Description)
assert.Equal(t, []string{"po", "dp"}, k.Scopes)
assert.Equal(t, "duh", k.Command)
assert.False(t, k.Background)
assert.Equal(t, []string{"-n", "$NAMESPACE", "-boolean"}, k.Args)
assert.ObjectsAreEqual(pluginYmlTestData, k)
}
func TestMultiplePluginFilesLoad(t *testing.T) {
p := config.NewPlugins()
assert.Nil(t, p.LoadPlugins("testdata/plugin.yml", []string{"testdata/plugins"}))
testPlugins := map[string]config.Plugin{
"blah": pluginYmlTestData,
"test1": test1YmlTestData,
"test2": test2YmlTestData,
}
assert.Equal(t, len(testPlugins), len(p.Plugin))
for name, expectedPlugin := range testPlugins {
k, ok := p.Plugin[name]
assert.True(t, ok)
assert.ObjectsAreEqual(expectedPlugin, k)
}
}

View File

@ -0,0 +1,12 @@
shortCut: shift-s
confirm: true
description: blee
scopes:
- po
- dp
command: duh
background: false
args:
- -n
- $NAMESPACE
- -boolean

View File

@ -0,0 +1,12 @@
shortCut: shift-r
confirm: false
description: bla
scopes:
- svc
- ing
command: duha
background: true
args:
- -n
- $NAMESPACE
- -oyaml