mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
vendor files
This commit is contained in:
50
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/BUILD
generated
vendored
Normal file
50
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/BUILD
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"env.go",
|
||||
"loader.go",
|
||||
"plugins.go",
|
||||
"runner.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubectl/plugins",
|
||||
deps = [
|
||||
"//vendor/github.com/ghodss/yaml:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"env_test.go",
|
||||
"loader_test.go",
|
||||
"plugins_test.go",
|
||||
"runner_test.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubectl/plugins",
|
||||
library = ":go_default_library",
|
||||
deps = ["//vendor/github.com/spf13/pflag:go_default_library"],
|
||||
)
|
161
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/env.go
generated
vendored
Normal file
161
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/env.go
generated
vendored
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
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"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// Env represents an environment variable with its name and value.
|
||||
type Env struct {
|
||||
N string
|
||||
V string
|
||||
}
|
||||
|
||||
// String returns "name=value" string.
|
||||
func (e Env) String() string {
|
||||
return fmt.Sprintf("%s=%s", e.N, e.V)
|
||||
}
|
||||
|
||||
// EnvList is a list of Env.
|
||||
type EnvList []Env
|
||||
|
||||
// Slice returns a slice of "name=value" strings.
|
||||
func (e EnvList) Slice() []string {
|
||||
envs := []string{}
|
||||
for _, env := range e {
|
||||
envs = append(envs, env.String())
|
||||
}
|
||||
return envs
|
||||
}
|
||||
|
||||
// Merge converts "name=value" strings into Env values and merges them into e.
|
||||
func (e EnvList) Merge(s ...string) EnvList {
|
||||
newList := e
|
||||
newList = append(newList, fromSlice(s)...)
|
||||
return newList
|
||||
}
|
||||
|
||||
// EnvProvider provides the environment in which the plugin will run.
|
||||
type EnvProvider interface {
|
||||
// Env returns the env list.
|
||||
Env() (EnvList, error)
|
||||
}
|
||||
|
||||
// MultiEnvProvider satisfies the EnvProvider interface for multiple env providers.
|
||||
type MultiEnvProvider []EnvProvider
|
||||
|
||||
// Env returns the combined env list of multiple env providers, returns on first error.
|
||||
func (p MultiEnvProvider) Env() (EnvList, error) {
|
||||
env := EnvList{}
|
||||
for _, provider := range p {
|
||||
pEnv, err := provider.Env()
|
||||
if err != nil {
|
||||
return EnvList{}, err
|
||||
}
|
||||
env = append(env, pEnv...)
|
||||
}
|
||||
return env, nil
|
||||
}
|
||||
|
||||
// PluginCallerEnvProvider satisfies the EnvProvider interface.
|
||||
type PluginCallerEnvProvider struct{}
|
||||
|
||||
// Env returns env with the path to the caller binary (usually full path to 'kubectl').
|
||||
func (p *PluginCallerEnvProvider) Env() (EnvList, error) {
|
||||
caller, err := os.Executable()
|
||||
if err != nil {
|
||||
return EnvList{}, err
|
||||
}
|
||||
return EnvList{
|
||||
{"KUBECTL_PLUGINS_CALLER", caller},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// PluginDescriptorEnvProvider satisfies the EnvProvider interface.
|
||||
type PluginDescriptorEnvProvider struct {
|
||||
Plugin *Plugin
|
||||
}
|
||||
|
||||
// Env returns env with information about the running plugin.
|
||||
func (p *PluginDescriptorEnvProvider) Env() (EnvList, error) {
|
||||
if p.Plugin == nil {
|
||||
return []Env{}, fmt.Errorf("plugin not present to extract env")
|
||||
}
|
||||
prefix := "KUBECTL_PLUGINS_DESCRIPTOR_"
|
||||
env := EnvList{
|
||||
{prefix + "NAME", p.Plugin.Name},
|
||||
{prefix + "SHORT_DESC", p.Plugin.ShortDesc},
|
||||
{prefix + "LONG_DESC", p.Plugin.LongDesc},
|
||||
{prefix + "EXAMPLE", p.Plugin.Example},
|
||||
{prefix + "COMMAND", p.Plugin.Command},
|
||||
}
|
||||
return env, nil
|
||||
}
|
||||
|
||||
// OSEnvProvider satisfies the EnvProvider interface.
|
||||
type OSEnvProvider struct{}
|
||||
|
||||
// Env returns the current environment from the operating system.
|
||||
func (p *OSEnvProvider) Env() (EnvList, error) {
|
||||
return fromSlice(os.Environ()), nil
|
||||
}
|
||||
|
||||
// EmptyEnvProvider satisfies the EnvProvider interface.
|
||||
type EmptyEnvProvider struct{}
|
||||
|
||||
// Env returns an empty environment.
|
||||
func (p *EmptyEnvProvider) Env() (EnvList, error) {
|
||||
return EnvList{}, nil
|
||||
}
|
||||
|
||||
// FlagToEnvName converts a flag string into a UNIX like environment variable name.
|
||||
// e.g --some-flag => "PREFIX_SOME_FLAG"
|
||||
func FlagToEnvName(flagName, prefix string) string {
|
||||
envName := strings.TrimPrefix(flagName, "--")
|
||||
envName = strings.ToUpper(envName)
|
||||
envName = strings.Replace(envName, "-", "_", -1)
|
||||
envName = prefix + envName
|
||||
return envName
|
||||
}
|
||||
|
||||
// FlagToEnv converts a flag and its value into an Env.
|
||||
// e.g --some-flag some-value => Env{N: "PREFIX_SOME_FLAG", V="SOME_VALUE"}
|
||||
func FlagToEnv(flag *pflag.Flag, prefix string) Env {
|
||||
envName := FlagToEnvName(flag.Name, prefix)
|
||||
return Env{envName, flag.Value.String()}
|
||||
}
|
||||
|
||||
func fromSlice(envs []string) EnvList {
|
||||
list := EnvList{}
|
||||
for _, env := range envs {
|
||||
list = append(list, parseEnv(env))
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func parseEnv(env string) Env {
|
||||
if !strings.Contains(env, "=") {
|
||||
env = env + "="
|
||||
}
|
||||
parsed := strings.SplitN(env, "=", 2)
|
||||
return Env{parsed[0], parsed[1]}
|
||||
}
|
162
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/env_test.go
generated
vendored
Normal file
162
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/env_test.go
generated
vendored
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
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 (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func TestEnv(t *testing.T) {
|
||||
tests := []struct {
|
||||
env Env
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
env: Env{"FOO", "BAR"},
|
||||
expected: "FOO=BAR",
|
||||
},
|
||||
{
|
||||
env: Env{"FOO", "BAR="},
|
||||
expected: "FOO=BAR=",
|
||||
},
|
||||
{
|
||||
env: Env{"FOO", ""},
|
||||
expected: "FOO=",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if s := test.env.String(); s != test.expected {
|
||||
t.Errorf("%v: expected string %q, got %q", test.env, test.expected, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvListToSlice(t *testing.T) {
|
||||
tests := []struct {
|
||||
env EnvList
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
env: EnvList{
|
||||
{"FOO", "BAR"},
|
||||
{"ZEE", "YO"},
|
||||
{"ONE", "1"},
|
||||
{"EQUALS", "=="},
|
||||
{"EMPTY", ""},
|
||||
},
|
||||
expected: []string{"FOO=BAR", "ZEE=YO", "ONE=1", "EQUALS===", "EMPTY="},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if s := test.env.Slice(); !reflect.DeepEqual(test.expected, s) {
|
||||
t.Errorf("%v: expected %v, got %v", test.env, test.expected, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddToEnvList(t *testing.T) {
|
||||
tests := []struct {
|
||||
add []string
|
||||
expected EnvList
|
||||
}{
|
||||
{
|
||||
add: []string{"FOO=BAR", "EMPTY=", "EQUALS===", "JUSTNAME"},
|
||||
expected: EnvList{
|
||||
{"FOO", "BAR"},
|
||||
{"EMPTY", ""},
|
||||
{"EQUALS", "=="},
|
||||
{"JUSTNAME", ""},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
env := EnvList{}.Merge(test.add...)
|
||||
if !reflect.DeepEqual(test.expected, env) {
|
||||
t.Errorf("%v: expected %v, got %v", test.add, test.expected, env)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlagToEnv(t *testing.T) {
|
||||
flags := pflag.NewFlagSet("", pflag.ContinueOnError)
|
||||
flags.String("test", "ok", "")
|
||||
flags.String("kube-master", "http://something", "")
|
||||
flags.String("from-file", "default", "")
|
||||
flags.Parse([]string{"--from-file=nondefault"})
|
||||
|
||||
tests := []struct {
|
||||
flag *pflag.Flag
|
||||
prefix string
|
||||
expected Env
|
||||
}{
|
||||
{
|
||||
flag: flags.Lookup("test"),
|
||||
expected: Env{"TEST", "ok"},
|
||||
},
|
||||
{
|
||||
flag: flags.Lookup("kube-master"),
|
||||
expected: Env{"KUBE_MASTER", "http://something"},
|
||||
},
|
||||
{
|
||||
prefix: "KUBECTL_",
|
||||
flag: flags.Lookup("from-file"),
|
||||
expected: Env{"KUBECTL_FROM_FILE", "nondefault"},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if env := FlagToEnv(test.flag, test.prefix); !reflect.DeepEqual(test.expected, env) {
|
||||
t.Errorf("%v: expected %v, got %v", test.flag.Name, test.expected, env)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPluginDescriptorEnvProvider(t *testing.T) {
|
||||
tests := []struct {
|
||||
plugin *Plugin
|
||||
expected EnvList
|
||||
}{
|
||||
{
|
||||
plugin: &Plugin{
|
||||
Description: Description{
|
||||
Name: "test",
|
||||
ShortDesc: "Short Description",
|
||||
Command: "foo --bar",
|
||||
},
|
||||
},
|
||||
expected: EnvList{
|
||||
{"KUBECTL_PLUGINS_DESCRIPTOR_NAME", "test"},
|
||||
{"KUBECTL_PLUGINS_DESCRIPTOR_SHORT_DESC", "Short Description"},
|
||||
{"KUBECTL_PLUGINS_DESCRIPTOR_LONG_DESC", ""},
|
||||
{"KUBECTL_PLUGINS_DESCRIPTOR_EXAMPLE", ""},
|
||||
{"KUBECTL_PLUGINS_DESCRIPTOR_COMMAND", "foo --bar"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
provider := &PluginDescriptorEnvProvider{
|
||||
Plugin: test.plugin,
|
||||
}
|
||||
env, _ := provider.Env()
|
||||
if !reflect.DeepEqual(test.expected, env) {
|
||||
t.Errorf("%v: expected %v, got %v", test.plugin.Name, test.expected, env)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
70
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/examples/aging/aging.rb
generated
vendored
Executable file
70
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/examples/aging/aging.rb
generated
vendored
Executable file
@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require 'json'
|
||||
require 'date'
|
||||
|
||||
class Numeric
|
||||
def duration
|
||||
secs = self.to_int
|
||||
mins = secs / 60
|
||||
hours = mins / 60
|
||||
days = hours / 24
|
||||
|
||||
if days > 0
|
||||
"#{days} days and #{hours % 24} hours"
|
||||
elsif hours > 0
|
||||
"#{hours} hours and #{mins % 60} minutes"
|
||||
elsif mins > 0
|
||||
"#{mins} minutes and #{secs % 60} seconds"
|
||||
elsif secs >= 0
|
||||
"#{secs} seconds"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
namespace = ENV['KUBECTL_PLUGINS_CURRENT_NAMESPACE'] || 'default'
|
||||
pods_json = `kubectl --namespace #{namespace} get pods -o json`
|
||||
pods_parsed = JSON.parse(pods_json)
|
||||
|
||||
puts "The Magnificent Aging Plugin."
|
||||
|
||||
data = Hash.new
|
||||
max_name_length = 0
|
||||
max_age = 0
|
||||
min_age = 0
|
||||
|
||||
pods_parsed['items'].each { |pod|
|
||||
name = pod['metadata']['name']
|
||||
creation = pod['metadata']['creationTimestamp']
|
||||
|
||||
age = Time.now - DateTime.parse(creation).to_time
|
||||
data[name] = age
|
||||
|
||||
if name.length > max_name_length
|
||||
max_name_length = name.length
|
||||
end
|
||||
if age > max_age
|
||||
max_age = age
|
||||
end
|
||||
if age < min_age
|
||||
min_age = age
|
||||
end
|
||||
}
|
||||
|
||||
data = data.sort_by{ |name, age| age }
|
||||
|
||||
if data.length > 0
|
||||
puts ""
|
||||
data.each { |name, age|
|
||||
output = ""
|
||||
output += name.rjust(max_name_length, ' ') + ": "
|
||||
bar_size = (age*80/max_age).ceil
|
||||
bar_size.times{ output += "▒" }
|
||||
output += " " + age.duration
|
||||
puts output
|
||||
puts ""
|
||||
}
|
||||
else
|
||||
puts "No pods"
|
||||
end
|
||||
|
5
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/examples/aging/plugin.yaml
generated
vendored
Normal file
5
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/examples/aging/plugin.yaml
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
name: "aging"
|
||||
shortDesc: "Aging shows pods by age"
|
||||
longDesc: >
|
||||
Aging shows pods from the current namespace by age.
|
||||
command: ./aging.rb
|
3
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/examples/hello/plugin.yaml
generated
vendored
Normal file
3
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/examples/hello/plugin.yaml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
name: "hello"
|
||||
shortDesc: "I say hello!"
|
||||
command: "echo Hello plugins!"
|
204
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/loader.go
generated
vendored
Normal file
204
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/loader.go
generated
vendored
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
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
|
||||
}
|
262
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/loader_test.go
generated
vendored
Normal file
262
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/loader_test.go
generated
vendored
Normal file
@ -0,0 +1,262 @@
|
||||
/*
|
||||
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"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSuccessfulDirectoryPluginLoader(t *testing.T) {
|
||||
tmp, err := setupValidPlugins(3, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
loader := &DirectoryPluginLoader{
|
||||
Directory: tmp,
|
||||
}
|
||||
plugins, err := loader.Load()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error loading plugins: %v", err)
|
||||
}
|
||||
|
||||
if count := len(plugins); count != 3 {
|
||||
t.Errorf("Unexpected number of loaded plugins, wanted 3, got %d", count)
|
||||
}
|
||||
|
||||
for _, plugin := range plugins {
|
||||
if m, _ := regexp.MatchString("^plugin[123]$", plugin.Name); !m {
|
||||
t.Errorf("Unexpected plugin name %s", plugin.Name)
|
||||
}
|
||||
if m, _ := regexp.MatchString("^The plugin[123] test plugin$", plugin.ShortDesc); !m {
|
||||
t.Errorf("Unexpected plugin short desc %s", plugin.ShortDesc)
|
||||
}
|
||||
if m, _ := regexp.MatchString("^echo plugin[123]$", plugin.Command); !m {
|
||||
t.Errorf("Unexpected plugin command %s", plugin.Command)
|
||||
}
|
||||
if count := len(plugin.Tree); count != 0 {
|
||||
t.Errorf("Unexpected number of loaded child plugins, wanted 0, got %d", count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyDirectoryPluginLoader(t *testing.T) {
|
||||
loader := &DirectoryPluginLoader{}
|
||||
_, err := loader.Load()
|
||||
if err == nil {
|
||||
t.Errorf("Expected error, got none")
|
||||
}
|
||||
if m, _ := regexp.MatchString("^directory not specified$", err.Error()); !m {
|
||||
t.Errorf("Unexpected error %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotDirectoryPluginLoader(t *testing.T) {
|
||||
tmp, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected ioutil.TempDir error: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
file := filepath.Join(tmp, "test.tmp")
|
||||
if err := ioutil.WriteFile(file, []byte("test"), 644); err != nil {
|
||||
t.Fatalf("unexpected ioutil.WriteFile error: %v", err)
|
||||
}
|
||||
|
||||
loader := &DirectoryPluginLoader{
|
||||
Directory: file,
|
||||
}
|
||||
_, err = loader.Load()
|
||||
if err == nil {
|
||||
t.Errorf("Expected error, got none")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "not a directory") {
|
||||
t.Errorf("Unexpected error %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnexistentDirectoryPluginLoader(t *testing.T) {
|
||||
loader := &DirectoryPluginLoader{
|
||||
Directory: "/hopefully-does-not-exist",
|
||||
}
|
||||
_, err := loader.Load()
|
||||
if err == nil {
|
||||
t.Errorf("Expected error, got none")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "no such file or directory") {
|
||||
t.Errorf("Unexpected error %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKubectlPluginsPathPluginLoader(t *testing.T) {
|
||||
tmp, err := setupValidPlugins(1, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
env := "KUBECTL_PLUGINS_PATH"
|
||||
os.Setenv(env, tmp)
|
||||
defer os.Unsetenv(env)
|
||||
|
||||
loader := KubectlPluginsPathPluginLoader()
|
||||
|
||||
plugins, err := loader.Load()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error loading plugins: %v", err)
|
||||
}
|
||||
|
||||
if count := len(plugins); count != 1 {
|
||||
t.Errorf("Unexpected number of loaded plugins, wanted 1, got %d", count)
|
||||
}
|
||||
|
||||
plugin := plugins[0]
|
||||
if "plugin1" != plugin.Name {
|
||||
t.Errorf("Unexpected plugin name %s", plugin.Name)
|
||||
}
|
||||
if "The plugin1 test plugin" != plugin.ShortDesc {
|
||||
t.Errorf("Unexpected plugin short desc %s", plugin.ShortDesc)
|
||||
}
|
||||
if "echo plugin1" != plugin.Command {
|
||||
t.Errorf("Unexpected plugin command %s", plugin.Command)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIncompletePluginDescriptor(t *testing.T) {
|
||||
tmp, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected ioutil.TempDir error: %v", err)
|
||||
}
|
||||
|
||||
descriptor := `
|
||||
name: incomplete
|
||||
shortDesc: The incomplete test plugin`
|
||||
|
||||
if err := os.Mkdir(filepath.Join(tmp, "incomplete"), 0755); err != nil {
|
||||
t.Fatalf("unexpected os.Mkdir error: %v", err)
|
||||
}
|
||||
if err := ioutil.WriteFile(filepath.Join(tmp, "incomplete", "plugin.yaml"), []byte(descriptor), 0644); err != nil {
|
||||
t.Fatalf("unexpected ioutil.WriteFile error: %v", err)
|
||||
}
|
||||
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
loader := &DirectoryPluginLoader{
|
||||
Directory: tmp,
|
||||
}
|
||||
plugins, err := loader.Load()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if count := len(plugins); count != 0 {
|
||||
t.Errorf("Unexpected number of loaded plugins, wanted 0, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDirectoryTreePluginLoader(t *testing.T) {
|
||||
tmp, err := setupValidPlugins(1, 2)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
loader := &DirectoryPluginLoader{
|
||||
Directory: tmp,
|
||||
}
|
||||
plugins, err := loader.Load()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error loading plugins: %v", err)
|
||||
}
|
||||
|
||||
if count := len(plugins); count != 1 {
|
||||
t.Errorf("Unexpected number of loaded plugins, wanted 1, got %d", count)
|
||||
}
|
||||
|
||||
for _, plugin := range plugins {
|
||||
if m, _ := regexp.MatchString("^plugin1$", plugin.Name); !m {
|
||||
t.Errorf("Unexpected plugin name %s", plugin.Name)
|
||||
}
|
||||
if m, _ := regexp.MatchString("^The plugin1 test plugin$", plugin.ShortDesc); !m {
|
||||
t.Errorf("Unexpected plugin short desc %s", plugin.ShortDesc)
|
||||
}
|
||||
if m, _ := regexp.MatchString("^echo plugin1$", plugin.Command); !m {
|
||||
t.Errorf("Unexpected plugin command %s", plugin.Command)
|
||||
}
|
||||
if count := len(plugin.Tree); count != 2 {
|
||||
t.Errorf("Unexpected number of loaded child plugins, wanted 2, got %d", count)
|
||||
}
|
||||
for _, child := range plugin.Tree {
|
||||
if m, _ := regexp.MatchString("^child[12]$", child.Name); !m {
|
||||
t.Errorf("Unexpected plugin child name %s", child.Name)
|
||||
}
|
||||
if m, _ := regexp.MatchString("^The child[12] test plugin child of plugin1 of House Targaryen$", child.ShortDesc); !m {
|
||||
t.Errorf("Unexpected plugin child short desc %s", child.ShortDesc)
|
||||
}
|
||||
if m, _ := regexp.MatchString("^echo child[12]$", child.Command); !m {
|
||||
t.Errorf("Unexpected plugin child command %s", child.Command)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setupValidPlugins(nPlugins, nChildren int) (string, error) {
|
||||
tmp, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unexpected ioutil.TempDir error: %v", err)
|
||||
}
|
||||
|
||||
for i := 1; i <= nPlugins; i++ {
|
||||
name := fmt.Sprintf("plugin%d", i)
|
||||
descriptor := fmt.Sprintf(`
|
||||
name: %[1]s
|
||||
shortDesc: The %[1]s test plugin
|
||||
command: echo %[1]s
|
||||
flags:
|
||||
- name: %[1]s-flag
|
||||
desc: A flag for %[1]s`, name)
|
||||
|
||||
if nChildren > 0 {
|
||||
descriptor += `
|
||||
tree:`
|
||||
}
|
||||
|
||||
for j := 1; j <= nChildren; j++ {
|
||||
child := fmt.Sprintf("child%d", i)
|
||||
descriptor += fmt.Sprintf(`
|
||||
- name: %[1]s
|
||||
shortDesc: The %[1]s test plugin child of %[2]s of House Targaryen
|
||||
command: echo %[1]s`, child, name)
|
||||
}
|
||||
|
||||
if err := os.Mkdir(filepath.Join(tmp, name), 0755); err != nil {
|
||||
return "", fmt.Errorf("unexpected os.Mkdir error: %v", err)
|
||||
}
|
||||
if err := ioutil.WriteFile(filepath.Join(tmp, name, "plugin.yaml"), []byte(descriptor), 0644); err != nil {
|
||||
return "", fmt.Errorf("unexpected ioutil.WriteFile error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return tmp, nil
|
||||
}
|
123
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/plugins.go
generated
vendored
Normal file
123
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/plugins.go
generated
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
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"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrIncompletePlugin indicates plugin is incomplete.
|
||||
ErrIncompletePlugin = fmt.Errorf("incomplete plugin descriptor: name, shortDesc and command fields are required")
|
||||
// ErrInvalidPluginName indicates plugin name is invalid.
|
||||
ErrInvalidPluginName = fmt.Errorf("plugin name can't contain spaces")
|
||||
// ErrIncompleteFlag indicates flag is incomplete.
|
||||
ErrIncompleteFlag = fmt.Errorf("incomplete flag descriptor: name and desc fields are required")
|
||||
// ErrInvalidFlagName indicates flag name is invalid.
|
||||
ErrInvalidFlagName = fmt.Errorf("flag name can't contain spaces")
|
||||
// ErrInvalidFlagShorthand indicates flag shorthand is invalid.
|
||||
ErrInvalidFlagShorthand = fmt.Errorf("flag shorthand must be only one letter")
|
||||
)
|
||||
|
||||
// Plugin is the representation of a CLI extension (plugin).
|
||||
type Plugin struct {
|
||||
Description
|
||||
Source
|
||||
Context RunningContext `json:"-"`
|
||||
}
|
||||
|
||||
// Description holds everything needed to register a
|
||||
// plugin as a command. Usually comes from a descriptor file.
|
||||
type Description struct {
|
||||
Name string `json:"name"`
|
||||
ShortDesc string `json:"shortDesc"`
|
||||
LongDesc string `json:"longDesc,omitempty"`
|
||||
Example string `json:"example,omitempty"`
|
||||
Command string `json:"command"`
|
||||
Flags []Flag `json:"flags,omitempty"`
|
||||
Tree Plugins `json:"tree,omitempty"`
|
||||
}
|
||||
|
||||
// Source holds the location of a given plugin in the filesystem.
|
||||
type Source struct {
|
||||
Dir string `json:"-"`
|
||||
DescriptorName string `json:"-"`
|
||||
}
|
||||
|
||||
// Validate validates plugin data.
|
||||
func (p Plugin) Validate() error {
|
||||
if len(p.Name) == 0 || len(p.ShortDesc) == 0 || (len(p.Command) == 0 && len(p.Tree) == 0) {
|
||||
return ErrIncompletePlugin
|
||||
}
|
||||
if strings.Index(p.Name, " ") > -1 {
|
||||
return ErrInvalidPluginName
|
||||
}
|
||||
for _, flag := range p.Flags {
|
||||
if err := flag.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, child := range p.Tree {
|
||||
if err := child.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsValid returns true if plugin data is valid.
|
||||
func (p Plugin) IsValid() bool {
|
||||
return p.Validate() == nil
|
||||
}
|
||||
|
||||
// Plugins is a list of plugins.
|
||||
type Plugins []*Plugin
|
||||
|
||||
// Flag describes a single flag supported by a given plugin.
|
||||
type Flag struct {
|
||||
Name string `json:"name"`
|
||||
Shorthand string `json:"shorthand,omitempty"`
|
||||
Desc string `json:"desc"`
|
||||
DefValue string `json:"defValue,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates flag data.
|
||||
func (f Flag) Validate() error {
|
||||
if len(f.Name) == 0 || len(f.Desc) == 0 {
|
||||
return ErrIncompleteFlag
|
||||
}
|
||||
if strings.Index(f.Name, " ") > -1 {
|
||||
return ErrInvalidFlagName
|
||||
}
|
||||
return f.ValidateShorthand()
|
||||
}
|
||||
|
||||
// ValidateShorthand validates flag shorthand data.
|
||||
func (f Flag) ValidateShorthand() error {
|
||||
length := len(f.Shorthand)
|
||||
if length == 0 || (length == 1 && unicode.IsLetter(rune(f.Shorthand[0]))) {
|
||||
return nil
|
||||
}
|
||||
return ErrInvalidFlagShorthand
|
||||
}
|
||||
|
||||
// Shorthanded returns true if flag shorthand data is valid.
|
||||
func (f Flag) Shorthanded() bool {
|
||||
return f.ValidateShorthand() == nil
|
||||
}
|
169
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/plugins_test.go
generated
vendored
Normal file
169
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/plugins_test.go
generated
vendored
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
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 "testing"
|
||||
|
||||
func TestPlugin(t *testing.T) {
|
||||
tests := []struct {
|
||||
plugin *Plugin
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
plugin: &Plugin{
|
||||
Description: Description{
|
||||
Name: "test",
|
||||
ShortDesc: "The test",
|
||||
Command: "echo 1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
plugin: &Plugin{
|
||||
Description: Description{
|
||||
Name: "test",
|
||||
ShortDesc: "The test",
|
||||
},
|
||||
},
|
||||
expectedErr: ErrIncompletePlugin,
|
||||
},
|
||||
{
|
||||
plugin: &Plugin{},
|
||||
expectedErr: ErrIncompletePlugin,
|
||||
},
|
||||
{
|
||||
plugin: &Plugin{
|
||||
Description: Description{
|
||||
Name: "test spaces",
|
||||
ShortDesc: "The test",
|
||||
Command: "echo 1",
|
||||
},
|
||||
},
|
||||
expectedErr: ErrInvalidPluginName,
|
||||
},
|
||||
{
|
||||
plugin: &Plugin{
|
||||
Description: Description{
|
||||
Name: "test",
|
||||
ShortDesc: "The test",
|
||||
Command: "echo 1",
|
||||
Flags: []Flag{
|
||||
{
|
||||
Name: "aflag",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErr: ErrIncompleteFlag,
|
||||
},
|
||||
{
|
||||
plugin: &Plugin{
|
||||
Description: Description{
|
||||
Name: "test",
|
||||
ShortDesc: "The test",
|
||||
Command: "echo 1",
|
||||
Flags: []Flag{
|
||||
{
|
||||
Name: "a flag",
|
||||
Desc: "Invalid flag",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErr: ErrInvalidFlagName,
|
||||
},
|
||||
{
|
||||
plugin: &Plugin{
|
||||
Description: Description{
|
||||
Name: "test",
|
||||
ShortDesc: "The test",
|
||||
Command: "echo 1",
|
||||
Flags: []Flag{
|
||||
{
|
||||
Name: "aflag",
|
||||
Desc: "Invalid shorthand",
|
||||
Shorthand: "aa",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErr: ErrInvalidFlagShorthand,
|
||||
},
|
||||
{
|
||||
plugin: &Plugin{
|
||||
Description: Description{
|
||||
Name: "test",
|
||||
ShortDesc: "The test",
|
||||
Command: "echo 1",
|
||||
Flags: []Flag{
|
||||
{
|
||||
Name: "aflag",
|
||||
Desc: "Invalid shorthand",
|
||||
Shorthand: "2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErr: ErrInvalidFlagShorthand,
|
||||
},
|
||||
{
|
||||
plugin: &Plugin{
|
||||
Description: Description{
|
||||
Name: "test",
|
||||
ShortDesc: "The test",
|
||||
Command: "echo 1",
|
||||
Flags: []Flag{
|
||||
{
|
||||
Name: "aflag",
|
||||
Desc: "A flag",
|
||||
Shorthand: "a",
|
||||
},
|
||||
},
|
||||
Tree: Plugins{
|
||||
&Plugin{
|
||||
Description: Description{
|
||||
Name: "child",
|
||||
ShortDesc: "The child",
|
||||
LongDesc: "The child long desc",
|
||||
Example: "You can use it like this but you're not supposed to",
|
||||
Command: "echo 1",
|
||||
Flags: []Flag{
|
||||
{
|
||||
Name: "childflag",
|
||||
Desc: "A child flag",
|
||||
},
|
||||
{
|
||||
Name: "childshorthand",
|
||||
Desc: "A child shorthand flag",
|
||||
Shorthand: "s",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := test.plugin.Validate()
|
||||
if err != test.expectedErr {
|
||||
t.Errorf("%s: expected error %v, got %v", test.plugin.Name, test.expectedErr, err)
|
||||
}
|
||||
}
|
||||
}
|
74
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/runner.go
generated
vendored
Normal file
74
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/runner.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
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 (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// PluginRunner is capable of running a plugin in a given running context.
|
||||
type PluginRunner interface {
|
||||
Run(plugin *Plugin, ctx RunningContext) error
|
||||
}
|
||||
|
||||
// RunningContext holds the context in which a given plugin is running - the
|
||||
// in, out, and err streams, arguments and environment passed to it, and the
|
||||
// working directory.
|
||||
type RunningContext struct {
|
||||
In io.Reader
|
||||
Out io.Writer
|
||||
ErrOut io.Writer
|
||||
Args []string
|
||||
EnvProvider EnvProvider
|
||||
WorkingDir string
|
||||
}
|
||||
|
||||
// ExecPluginRunner is a PluginRunner that uses Go's os/exec to run plugins.
|
||||
type ExecPluginRunner struct{}
|
||||
|
||||
// Run takes a given plugin and runs it in a given context using os/exec, returning
|
||||
// any error found while running.
|
||||
func (r *ExecPluginRunner) Run(plugin *Plugin, ctx RunningContext) error {
|
||||
command := strings.Split(os.ExpandEnv(plugin.Command), " ")
|
||||
base := command[0]
|
||||
args := []string{}
|
||||
if len(command) > 1 {
|
||||
args = command[1:]
|
||||
}
|
||||
args = append(args, ctx.Args...)
|
||||
|
||||
cmd := exec.Command(base, args...)
|
||||
|
||||
cmd.Stdin = ctx.In
|
||||
cmd.Stdout = ctx.Out
|
||||
cmd.Stderr = ctx.ErrOut
|
||||
|
||||
env, err := ctx.EnvProvider.Env()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.Env = env.Slice()
|
||||
cmd.Dir = ctx.WorkingDir
|
||||
|
||||
glog.V(9).Infof("Running plugin %q as base command %q with args %v", plugin.Name, base, args)
|
||||
return cmd.Run()
|
||||
}
|
81
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/runner_test.go
generated
vendored
Normal file
81
vendor/k8s.io/kubernetes/pkg/kubectl/plugins/runner_test.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
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 (
|
||||
"bytes"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExecRunner(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
command string
|
||||
expectedMsg string
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
command: "echo test ok",
|
||||
expectedMsg: "test ok\n",
|
||||
},
|
||||
{
|
||||
name: "invalid",
|
||||
command: "false",
|
||||
expectedErr: "exit status 1",
|
||||
},
|
||||
{
|
||||
name: "env",
|
||||
command: "echo $KUBECTL_PLUGINS_TEST",
|
||||
expectedMsg: "ok\n",
|
||||
},
|
||||
}
|
||||
|
||||
os.Setenv("KUBECTL_PLUGINS_TEST", "ok")
|
||||
defer os.Unsetenv("KUBECTL_PLUGINS_TEST")
|
||||
|
||||
for _, test := range tests {
|
||||
outBuf := bytes.NewBuffer([]byte{})
|
||||
|
||||
plugin := &Plugin{
|
||||
Description: Description{
|
||||
Name: test.name,
|
||||
ShortDesc: "Test Runner Plugin",
|
||||
Command: test.command,
|
||||
},
|
||||
}
|
||||
|
||||
ctx := RunningContext{
|
||||
Out: outBuf,
|
||||
WorkingDir: ".",
|
||||
EnvProvider: &EmptyEnvProvider{},
|
||||
}
|
||||
|
||||
runner := &ExecPluginRunner{}
|
||||
err := runner.Run(plugin, ctx)
|
||||
|
||||
if outBuf.String() != test.expectedMsg {
|
||||
t.Errorf("%s: unexpected output: %q", test.name, outBuf.String())
|
||||
}
|
||||
|
||||
if err != nil && err.Error() != test.expectedErr {
|
||||
t.Errorf("%s: unexpected err output: %v", test.name, err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user