Skip to content

Commit

Permalink
feat: allow for multiple plugin files in $XDG_DATA_DIRS/k9s/plugins (d…
Browse files Browse the repository at this point in the history
  • Loading branch information
cwrau authored and thejoeejoee committed Jan 31, 2024
1 parent 685d792 commit 7daa8cd
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 15 deletions.
46 changes: 40 additions & 6 deletions internal/config/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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"
}
61 changes: 52 additions & 9 deletions internal/config/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
12 changes: 12 additions & 0 deletions internal/config/testdata/plugins/test1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
shortCut: shift-s
confirm: true
description: blee
scopes:
- po
- dp
command: duh
background: false
args:
- -n
- $NAMESPACE
- -boolean
12 changes: 12 additions & 0 deletions internal/config/testdata/plugins/test2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
shortCut: shift-r
confirm: false
description: bla
scopes:
- svc
- ing
command: duha
background: true
args:
- -n
- $NAMESPACE
- -oyaml

0 comments on commit 7daa8cd

Please sign in to comment.