mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-11-23 14:50:24 +00:00
205 lines
6.1 KiB
Go
205 lines
6.1 KiB
Go
|
/*
|
||
|
Copyright 2017 The Kubernetes Authors.
|
||
|
|
||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
you may not use this file except in compliance with the License.
|
||
|
You may obtain a copy of the License at
|
||
|
|
||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
||
|
Unless required by applicable law or agreed to in writing, software
|
||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
See the License for the specific language governing permissions and
|
||
|
limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package plugins
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
|
||
|
"github.com/ghodss/yaml"
|
||
|
"github.com/golang/glog"
|
||
|
|
||
|
"k8s.io/client-go/tools/clientcmd"
|
||
|
)
|
||
|
|
||
|
// PluginDescriptorFilename is the default file name for plugin descriptions.
|
||
|
const PluginDescriptorFilename = "plugin.yaml"
|
||
|
|
||
|
// PluginLoader is capable of loading a list of plugin descriptions.
|
||
|
type PluginLoader interface {
|
||
|
// Load loads the plugin descriptions.
|
||
|
Load() (Plugins, error)
|
||
|
}
|
||
|
|
||
|
// DirectoryPluginLoader is a PluginLoader that loads plugin descriptions
|
||
|
// from a given directory in the filesystem. Plugins are located in subdirs
|
||
|
// under the loader "root", where each subdir must contain, at least, a plugin
|
||
|
// descriptor file called "plugin.yaml" that translates into a PluginDescription.
|
||
|
type DirectoryPluginLoader struct {
|
||
|
Directory string
|
||
|
}
|
||
|
|
||
|
// Load reads the directory the loader holds and loads plugin descriptions.
|
||
|
func (l *DirectoryPluginLoader) Load() (Plugins, error) {
|
||
|
if len(l.Directory) == 0 {
|
||
|
return nil, fmt.Errorf("directory not specified")
|
||
|
}
|
||
|
|
||
|
list := Plugins{}
|
||
|
|
||
|
stat, err := os.Stat(l.Directory)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if !stat.IsDir() {
|
||
|
return nil, fmt.Errorf("not a directory: %s", l.Directory)
|
||
|
}
|
||
|
|
||
|
base, err := filepath.Abs(l.Directory)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
// read the base directory tree searching for plugin descriptors
|
||
|
// fails silently (descriptors unable to be read or unmarshalled are logged but skipped)
|
||
|
err = filepath.Walk(base, func(path string, fileInfo os.FileInfo, walkErr error) error {
|
||
|
if walkErr != nil || fileInfo.IsDir() || fileInfo.Name() != PluginDescriptorFilename {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
file, err := ioutil.ReadFile(path)
|
||
|
if err != nil {
|
||
|
glog.V(1).Infof("Unable to read plugin descriptor %s: %v", path, err)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
plugin := &Plugin{}
|
||
|
if err := yaml.Unmarshal(file, plugin); err != nil {
|
||
|
glog.V(1).Infof("Unable to unmarshal plugin descriptor %s: %v", path, err)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
if err := plugin.Validate(); err != nil {
|
||
|
glog.V(1).Infof("%v", err)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
var setSource func(path string, fileInfo os.FileInfo, p *Plugin)
|
||
|
setSource = func(path string, fileInfo os.FileInfo, p *Plugin) {
|
||
|
p.Dir = filepath.Dir(path)
|
||
|
p.DescriptorName = fileInfo.Name()
|
||
|
for _, child := range p.Tree {
|
||
|
setSource(path, fileInfo, child)
|
||
|
}
|
||
|
}
|
||
|
setSource(path, fileInfo, plugin)
|
||
|
|
||
|
glog.V(6).Infof("Plugin loaded: %s", plugin.Name)
|
||
|
list = append(list, plugin)
|
||
|
|
||
|
return nil
|
||
|
})
|
||
|
|
||
|
return list, err
|
||
|
}
|
||
|
|
||
|
// UserDirPluginLoader returns a PluginLoader that loads plugins from the
|
||
|
// "plugins" directory under the user's kubeconfig dir (usually "~/.kube/plugins/").
|
||
|
func UserDirPluginLoader() PluginLoader {
|
||
|
dir := filepath.Join(clientcmd.RecommendedConfigDir, "plugins")
|
||
|
return &DirectoryPluginLoader{
|
||
|
Directory: dir,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// PathFromEnvVarPluginLoader returns a PluginLoader that loads plugins from one or more
|
||
|
// directories specified by the provided env var name. In case the env var is not
|
||
|
// set, the PluginLoader just loads nothing. A list of subdirectories can be provided,
|
||
|
// which will be appended to each path specified by the env var.
|
||
|
func PathFromEnvVarPluginLoader(envVarName string, subdirs ...string) PluginLoader {
|
||
|
env := os.Getenv(envVarName)
|
||
|
if len(env) == 0 {
|
||
|
return &DummyPluginLoader{}
|
||
|
}
|
||
|
loader := MultiPluginLoader{}
|
||
|
for _, path := range filepath.SplitList(env) {
|
||
|
dir := append([]string{path}, subdirs...)
|
||
|
loader = append(loader, &DirectoryPluginLoader{
|
||
|
Directory: filepath.Join(dir...),
|
||
|
})
|
||
|
}
|
||
|
return loader
|
||
|
}
|
||
|
|
||
|
// KubectlPluginsPathPluginLoader returns a PluginLoader that loads plugins from one or more
|
||
|
// directories specified by the KUBECTL_PLUGINS_PATH env var.
|
||
|
func KubectlPluginsPathPluginLoader() PluginLoader {
|
||
|
return PathFromEnvVarPluginLoader("KUBECTL_PLUGINS_PATH")
|
||
|
}
|
||
|
|
||
|
// XDGDataDirsPluginLoader returns a PluginLoader that loads plugins from one or more
|
||
|
// directories specified by the XDG system directory structure spec in the
|
||
|
// XDG_DATA_DIRS env var, plus the "kubectl/plugins/" suffix. According to the
|
||
|
// spec, if XDG_DATA_DIRS is not set it defaults to "/usr/local/share:/usr/share".
|
||
|
func XDGDataDirsPluginLoader() PluginLoader {
|
||
|
envVarName := "XDG_DATA_DIRS"
|
||
|
if len(os.Getenv(envVarName)) > 0 {
|
||
|
return PathFromEnvVarPluginLoader(envVarName, "kubectl", "plugins")
|
||
|
}
|
||
|
return TolerantMultiPluginLoader{
|
||
|
&DirectoryPluginLoader{
|
||
|
Directory: "/usr/local/share/kubectl/plugins",
|
||
|
},
|
||
|
&DirectoryPluginLoader{
|
||
|
Directory: "/usr/share/kubectl/plugins",
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MultiPluginLoader is a PluginLoader that can encapsulate multiple plugin loaders,
|
||
|
// a successful loading means every encapsulated loader was able to load without errors.
|
||
|
type MultiPluginLoader []PluginLoader
|
||
|
|
||
|
// Load calls Load() for each of the encapsulated Loaders.
|
||
|
func (l MultiPluginLoader) Load() (Plugins, error) {
|
||
|
plugins := Plugins{}
|
||
|
for _, loader := range l {
|
||
|
loaded, err := loader.Load()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
plugins = append(plugins, loaded...)
|
||
|
}
|
||
|
return plugins, nil
|
||
|
}
|
||
|
|
||
|
// TolerantMultiPluginLoader is a PluginLoader than encapsulates multiple plugins loaders,
|
||
|
// but is tolerant to errors while loading from them.
|
||
|
type TolerantMultiPluginLoader []PluginLoader
|
||
|
|
||
|
// Load calls Load() for each of the encapsulated Loaders.
|
||
|
func (l TolerantMultiPluginLoader) Load() (Plugins, error) {
|
||
|
plugins := Plugins{}
|
||
|
for _, loader := range l {
|
||
|
loaded, _ := loader.Load()
|
||
|
if loaded != nil {
|
||
|
plugins = append(plugins, loaded...)
|
||
|
}
|
||
|
}
|
||
|
return plugins, nil
|
||
|
}
|
||
|
|
||
|
// DummyPluginLoader is a noop PluginLoader.
|
||
|
type DummyPluginLoader struct{}
|
||
|
|
||
|
// Load loads nothing.
|
||
|
func (l *DummyPluginLoader) Load() (Plugins, error) {
|
||
|
return Plugins{}, nil
|
||
|
}
|