Skip to content

Commit

Permalink
feat: make external plugin path configurable
Browse files Browse the repository at this point in the history
Co-authored-by: Bryce Palmer <everettraven@gmail.com>
  • Loading branch information
Eileen-Yu and everettraven committed Jun 6, 2023
1 parent 40ec611 commit b798681
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 27 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ require (
require (
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
Expand All @@ -26,6 +27,7 @@ require (
golang.org/x/mod v0.10.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.8.0 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
Expand All @@ -103,6 +105,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
Expand Down Expand Up @@ -471,7 +474,10 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
Expand Down
21 changes: 18 additions & 3 deletions pkg/cli/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,29 @@ func isHostSupported(host string) bool {
return false
}

// getPluginsRoot detects the host system and gets the plugins root based on the host.
// getPluginsRoot gets the plugin root path.
func getPluginsRoot(host string) (pluginsRoot string, err error) {
if !isHostSupported(host) {
// freebsd, openbsd, windows...
return "", fmt.Errorf("host not supported: %v", host)
}

// if user provides specific path, return
if pluginsPath := os.Getenv("KB_PLUGINS_PATH"); pluginsPath != "" {
// verify if the path actually exists
if _, err := os.Stat(pluginsPath); err != nil {
if os.IsNotExist(err) {
// the path does not exist
return "", fmt.Errorf("the specified path %s does not exist", pluginsPath)
}
// some other error
return "", fmt.Errorf("error checking the path: %v", err)
}
// the path exists
return pluginsPath, nil
}

// if no specific path, detects the host system and gets the plugins root based on the host.
pluginsRelativePath := filepath.Join("kubebuilder", "plugins")
if xdgHome := os.Getenv("XDG_CONFIG_HOME"); xdgHome != "" {
return filepath.Join(xdgHome, pluginsRelativePath), nil
Expand All @@ -206,9 +222,8 @@ func getPluginsRoot(host string) (pluginsRoot string, err error) {
if err != nil {
return "", fmt.Errorf("error retrieving home dir: %v", err)
}
pluginsRoot = filepath.Join(userHomeDir, pluginsRoot)

return
return filepath.Join(userHomeDir, pluginsRoot), nil
}

// DiscoverExternalPlugins discovers the external plugins in the plugins root directory
Expand Down
164 changes: 140 additions & 24 deletions pkg/cli/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package cli

import (
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
Expand All @@ -34,6 +35,145 @@ import (
)

var _ = Describe("Discover external plugins", func() {
Context("with valid plugins root path", func() {
var (
homePath string = os.Getenv("HOME")
customPath string = "/tmp/myplugins"
xdghome string = os.Getenv("XDG_CONFIG_HOME")
)

When("XDG_CONFIG_HOME is not set and using the $HOME environment variable", func() {
BeforeEach(func() {
err := os.Unsetenv("KB_PLUGINS_PATH")
Expect(err).To(BeNil())

err = os.Unsetenv("XDG_CONFIG_HOME")
Expect(err).To(BeNil())

xdghome = os.Getenv("XDG_CONFIG_HOME")
})

It("should return the correct path for the darwin OS", func() {
plgPath, err := getPluginsRoot("darwin")
Expect(err).To(BeNil())
Expect(plgPath).To(Equal(fmt.Sprintf("%s/Library/Application Support/kubebuilder/plugins", homePath)))
})

It("should return the correct path for the linux OS", func() {
plgPath, err := getPluginsRoot("linux")
Expect(err).To(BeNil())
Expect(plgPath).To(Equal(fmt.Sprintf("%s/.config/kubebuilder/plugins", homePath)))
})

It("should return error when the host is not darwin / linux", func() {
plgPath, err := getPluginsRoot("random")
Expect(plgPath).To(Equal(""))
Expect(err).ToNot(BeNil())
Expect(err.Error()).To(ContainSubstring("host not supported"))
})
})

When("XDG_CONFIG_HOME is set", func() {
BeforeEach(func() {
err := os.Unsetenv("KB_PLUGINS_PATH")
Expect(err).To(BeNil())

err = os.Setenv("XDG_CONFIG_HOME", fmt.Sprintf("%s/.config", homePath))
Expect(err).To(BeNil())

xdghome = os.Getenv("XDG_CONFIG_HOME")
})

AfterEach(func() {
err := os.Unsetenv("XDG_CONFIG_HOME")
Expect(err).To(BeNil())
})

It("should return the correct path for the darwin OS", func() {
plgPath, err := getPluginsRoot("darwin")
Expect(err).To(BeNil())
Expect(plgPath).To(Equal(fmt.Sprintf("%s/kubebuilder/plugins", xdghome)))
})

It("should return the correct path for the linux OS", func() {
plgPath, err := getPluginsRoot("linux")
Expect(err).To(BeNil())
Expect(plgPath).To(Equal(fmt.Sprintf("%s/kubebuilder/plugins", xdghome)))
})

It("should return error when the host is not darwin / linux", func() {
plgPath, err := getPluginsRoot("random")
Expect(plgPath).To(Equal(""))
Expect(err).ToNot(BeNil())
Expect(err.Error()).To(ContainSubstring("host not supported"))
})
})

When("using the custom path", func() {
BeforeEach(func() {
err := os.MkdirAll(customPath, 0750)
Expect(err).To(BeNil())

err = os.Setenv("KB_PLUGINS_PATH", customPath)
Expect(err).To(BeNil())
})

AfterEach(func() {
err := os.Unsetenv("KB_PLUGINS_PATH")
Expect(err).To(BeNil())
})

It("should return the user given path for darwin OS", func() {
plgPath, err := getPluginsRoot("darwin")
Expect(plgPath).To(Equal(customPath))
Expect(err).To(BeNil())
})

It("should return the user given path for linux OS", func() {
plgPath, err := getPluginsRoot("linux")
Expect(plgPath).To(Equal(customPath))
Expect(err).To(BeNil())
})

It("should report error when the host is not darwin / linux", func() {
plgPath, err := getPluginsRoot("random")
Expect(plgPath).To(Equal(""))
Expect(err).ToNot(BeNil())
Expect(err.Error()).To(ContainSubstring("host not supported"))
})
})
})

Context("with invalid plugins root path", func() {
BeforeEach(func() {
err := os.Setenv("KB_PLUGINS_PATH", "/non/existent/path")
Expect(err).To(BeNil())
})

AfterEach(func() {
err := os.Unsetenv("KB_PLUGINS_PATH")
Expect(err).To(BeNil())
})

It("should return an error for the darwin OS", func() {
plgPath, err := getPluginsRoot("darwin")
Expect(err).ToNot(BeNil())
Expect(plgPath).To(Equal(""))
})

It("should return an error for the linux OS", func() {
plgPath, err := getPluginsRoot("linux")
Expect(err).ToNot(BeNil())
Expect(plgPath).To(Equal(""))
})

It("should return an error when the host is not darwin / linux", func() {
plgPath, err := getPluginsRoot("random")
Expect(err).ToNot(BeNil())
Expect(plgPath).To(Equal(""))
})
})

Context("when plugin executables exist in the expected plugin directories", func() {
const (
filePermissions os.FileMode = 755
Expand Down Expand Up @@ -219,18 +359,6 @@ var _ = Describe("Discover external plugins", func() {
Expect(err).To(Equal(errPluginsRoot))
})

It("should fail for any other host that is not supported", func() {
_, err := getPluginsRoot("darwin")
Expect(err).To(BeNil())

_, err = getPluginsRoot("linux")
Expect(err).To(BeNil())

_, err = getPluginsRoot("random")
Expect(err).ToNot(BeNil())
Expect(err.Error()).To(ContainSubstring("host not supported"))
})

It("should skip parsing of directories if plugins root is not a directory", func() {
retrievePluginsRoot = func(host string) (string, error) {
return "externalplugin.sh", nil
Expand All @@ -240,18 +368,6 @@ var _ = Describe("Discover external plugins", func() {
Expect(err).To(BeNil())
})

It("should fail for any other host that is not supported", func() {
_, err := getPluginsRoot("darwin")
Expect(err).To(BeNil())

_, err = getPluginsRoot("linux")
Expect(err).To(BeNil())

_, err = getPluginsRoot("random")
Expect(err).ToNot(BeNil())
Expect(err.Error()).To(ContainSubstring("host not supported"))
})

It("should return full path to the external plugins without XDG_CONFIG_HOME", func() {
if _, ok := os.LookupEnv("XDG_CONFIG_HOME"); ok {
err = os.Setenv("XDG_CONFIG_HOME", "")
Expand Down

0 comments on commit b798681

Please sign in to comment.