Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
2b178ad6
Commit
2b178ad6
authored
Nov 25, 2016
by
Fabiano Franz
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Basic support for kubectl plugins
parent
acca01bc
Hide whitespace changes
Inline
Side-by-side
Showing
29 changed files
with
1103 additions
and
2 deletions
+1103
-2
test-cmd-util.sh
hack/make-rules/test-cmd-util.sh
+44
-0
BUILD
pkg/kubectl/BUILD
+1
-0
BUILD
pkg/kubectl/cmd/BUILD
+4
-0
cmd.go
pkg/kubectl/cmd/cmd.go
+18
-0
plugin.go
pkg/kubectl/cmd/plugin.go
+54
-0
plugin_test.go
pkg/kubectl/cmd/plugin_test.go
+111
-0
normalizers.go
pkg/kubectl/cmd/templates/normalizers.go
+6
-0
BUILD
pkg/kubectl/cmd/testing/BUILD
+1
-0
fake.go
pkg/kubectl/cmd/testing/fake.go
+9
-0
BUILD
pkg/kubectl/cmd/util/BUILD
+1
-0
factory.go
pkg/kubectl/cmd/util/factory.go
+5
-0
factory_builder.go
pkg/kubectl/cmd/util/factory_builder.go
+21
-0
BUILD
pkg/kubectl/plugins/BUILD
+48
-0
aging.rb
pkg/kubectl/plugins/examples/aging/aging.rb
+69
-0
plugin.yaml
pkg/kubectl/plugins/examples/aging/plugin.yaml
+8
-0
plugin.yaml
pkg/kubectl/plugins/examples/hello/plugin.yaml
+3
-0
loader.go
pkg/kubectl/plugins/loader.go
+192
-0
loader_test.go
pkg/kubectl/plugins/loader_test.go
+197
-0
plugins.go
pkg/kubectl/plugins/plugins.go
+58
-0
plugins_test.go
pkg/kubectl/plugins/plugins_test.go
+70
-0
runner.go
pkg/kubectl/plugins/runner.go
+69
-0
runner_test.go
pkg/kubectl/plugins/runner_test.go
+71
-0
loader.go
staging/src/k8s.io/client-go/tools/clientcmd/loader.go
+5
-2
plugin.yaml
test/fixtures/pkg/kubectl/plugins/echo/plugin.yaml
+4
-0
plugin.yaml
test/fixtures/pkg/kubectl/plugins/error/plugin.yaml
+3
-0
plugin.yaml
test/fixtures/pkg/kubectl/plugins/get/plugin.yaml
+3
-0
plugin.yaml
test/fixtures/pkg/kubectl/plugins/incomplete/plugin.yaml
+2
-0
hello.sh
test/fixtures/pkg/kubectl/plugins2/hello/hello.sh
+19
-0
plugin.yaml
test/fixtures/pkg/kubectl/plugins2/hello/plugin.yaml
+7
-0
No files found.
hack/make-rules/test-cmd-util.sh
View file @
2b178ad6
...
...
@@ -3679,5 +3679,49 @@ __EOF__
kube::test::get_object_assert csr
"{{range.items}}{{
$id_field
}}{{end}}"
''
fi
###########
# Plugins #
###########
kube::log::status
"Testing kubectl plugins"
# single plugins path
output_message
=
$(
KUBECTL_PLUGINS_PATH
=
test
/fixtures/pkg/kubectl/plugins kubectl
-h
2>&1
)
kube::test::if_has_string
"
${
output_message
}
"
'echo\s\+Echoes for test-cmd'
kube::test::if_has_string
"
${
output_message
}
"
'get\s\+The wonderful new plugin-based get!'
kube::test::if_has_string
"
${
output_message
}
"
'error\s\+The tremendous plugin that always fails!'
kube::test::if_has_not_string
"
${
output_message
}
"
'The hello plugin'
kube::test::if_has_not_string
"
${
output_message
}
"
'Incomplete plugin'
# when overriding existing command, both appear in help. TODO handle this to not register plugins that override existing cmd.
kube::test::if_has_string
"
${
output_message
}
"
'get\s\+Display one or many resources'
# multiple plugins path
output_message
=
$(
KUBECTL_PLUGINS_PATH
=
test
/fixtures/pkg/kubectl/plugins/:test/fixtures/pkg/kubectl/plugins2/ kubectl
-h
2>&1
)
kube::test::if_has_string
"
${
output_message
}
"
'echo\s\+Echoes for test-cmd'
kube::test::if_has_string
"
${
output_message
}
"
'get\s\+The wonderful new plugin-based get!'
kube::test::if_has_string
"
${
output_message
}
"
'error\s\+The tremendous plugin that always fails!'
kube::test::if_has_string
"
${
output_message
}
"
'hello\s\+The hello plugin'
kube::test::if_has_not_string
"
${
output_message
}
"
'Incomplete plugin'
# don't override existing commands
output_message
=
$(
KUBECTL_PLUGINS_PATH
=
test
/fixtures/pkg/kubectl/plugins/:test/fixtures/pkg/kubectl/plugins2/ kubectl get
-h
2>&1
)
kube::test::if_has_string
"
${
output_message
}
"
'Display one or many resources'
kube::test::if_has_not_string
"
$output_message
{output_message}"
'The wonderful new plugin-based get'
# plugin help
output_message
=
$(
KUBECTL_PLUGINS_PATH
=
test
/fixtures/pkg/kubectl/plugins/:test/fixtures/pkg/kubectl/plugins2/ kubectl hello
-h
2>&1
)
kube::test::if_has_string
"
${
output_message
}
"
'The hello plugin is a new plugin used by test-cmd to test multiple plugin locations.'
kube::test::if_has_string
"
${
output_message
}
"
'Usage:'
# run plugin
output_message
=
$(
KUBECTL_PLUGINS_PATH
=
test
/fixtures/pkg/kubectl/plugins/:test/fixtures/pkg/kubectl/plugins2/ kubectl hello 2>&1
)
kube::test::if_has_string
"
${
output_message
}
"
'#hello#'
output_message
=
$(
KUBECTL_PLUGINS_PATH
=
test
/fixtures/pkg/kubectl/plugins/:test/fixtures/pkg/kubectl/plugins2/ kubectl
echo
2>&1
)
kube::test::if_has_string
"
${
output_message
}
"
'This plugin works!'
output_message
=
$(
!
KUBECTL_PLUGINS_PATH
=
test
/fixtures/pkg/kubectl/plugins/ kubectl hello 2>&1
)
kube::test::if_has_string
"
${
output_message
}
"
'unknown command'
output_message
=
$(
!
KUBECTL_PLUGINS_PATH
=
test
/fixtures/pkg/kubectl/plugins/ kubectl error 2>&1
)
kube::test::if_has_string
"
${
output_message
}
"
'error: exit status 1'
kube::test::clear_all
}
pkg/kubectl/BUILD
View file @
2b178ad6
...
...
@@ -190,6 +190,7 @@ filegroup(
":package-srcs",
"//pkg/kubectl/cmd:all-srcs",
"//pkg/kubectl/metricsutil:all-srcs",
"//pkg/kubectl/plugins:all-srcs",
"//pkg/kubectl/resource:all-srcs",
"//pkg/kubectl/testing:all-srcs",
],
...
...
pkg/kubectl/cmd/BUILD
View file @
2b178ad6
...
...
@@ -51,6 +51,7 @@ go_library(
"logs.go",
"options.go",
"patch.go",
"plugin.go",
"portforward.go",
"proxy.go",
"replace.go",
...
...
@@ -93,6 +94,7 @@ go_library(
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/cmd/util/editor:go_default_library",
"//pkg/kubectl/metricsutil:go_default_library",
"//pkg/kubectl/plugins:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/kubelet/types:go_default_library",
"//pkg/printers:go_default_library",
...
...
@@ -173,6 +175,7 @@ go_test(
"label_test.go",
"logs_test.go",
"patch_test.go",
"plugin_test.go",
"portforward_test.go",
"replace_test.go",
"rollingupdate_test.go",
...
...
@@ -207,6 +210,7 @@ go_test(
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/testing:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/plugins:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/printers/internalversion:go_default_library",
...
...
pkg/kubectl/cmd/cmd.go
View file @
2b178ad6
...
...
@@ -347,6 +347,24 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob
},
},
}
// Loads plugins and create commands for each plugin identified
loadedPlugins
,
loadErr
:=
f
.
PluginLoader
()
.
Load
()
if
loadErr
!=
nil
{
glog
.
V
(
1
)
.
Infof
(
"Unable to load plugins: %v"
,
loadErr
)
}
pluginRunner
:=
f
.
PluginRunner
()
if
len
(
loadedPlugins
)
>
0
{
pluginCmds
:=
[]
*
cobra
.
Command
{}
for
_
,
p
:=
range
loadedPlugins
{
pluginCmds
=
append
(
pluginCmds
,
NewCmdForPlugin
(
p
,
pluginRunner
,
in
,
out
,
err
))
}
groups
=
append
(
groups
,
templates
.
CommandGroup
{
Message
:
"Plugins:"
,
Commands
:
pluginCmds
,
})
}
groups
.
Add
(
cmds
)
filters
:=
[]
string
{
...
...
pkg/kubectl/cmd/plugin.go
0 → 100644
View file @
2b178ad6
/*
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
cmd
import
(
"io"
"os"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil
"k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/plugins"
)
// NewCmdForPlugin creates a command capable of running the provided plugin.
func
NewCmdForPlugin
(
plugin
*
plugins
.
Plugin
,
runner
plugins
.
PluginRunner
,
in
io
.
Reader
,
out
,
errout
io
.
Writer
)
*
cobra
.
Command
{
if
!
plugin
.
IsValid
()
{
return
nil
}
return
&
cobra
.
Command
{
Use
:
plugin
.
Name
,
Short
:
plugin
.
ShortDesc
,
Long
:
templates
.
LongDesc
(
plugin
.
LongDesc
),
Example
:
templates
.
Examples
(
plugin
.
Example
),
Run
:
func
(
cmd
*
cobra
.
Command
,
args
[]
string
)
{
ctx
:=
plugins
.
RunningContext
{
In
:
in
,
Out
:
out
,
ErrOut
:
errout
,
Args
:
args
,
Env
:
os
.
Environ
(),
WorkingDir
:
plugin
.
Dir
,
}
if
err
:=
runner
.
Run
(
plugin
,
ctx
);
err
!=
nil
{
cmdutil
.
CheckErr
(
err
)
}
},
}
}
pkg/kubectl/cmd/plugin_test.go
0 → 100644
View file @
2b178ad6
/*
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
cmd
import
(
"bytes"
"fmt"
"testing"
cmdutil
"k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/plugins"
)
type
mockPluginRunner
struct
{
success
bool
}
func
(
r
*
mockPluginRunner
)
Run
(
p
*
plugins
.
Plugin
,
ctx
plugins
.
RunningContext
)
error
{
if
!
r
.
success
{
return
fmt
.
Errorf
(
"oops %s"
,
p
.
Name
)
}
ctx
.
Out
.
Write
([]
byte
(
fmt
.
Sprintf
(
"ok: %s"
,
p
.
Name
)))
return
nil
}
func
TestPluginCmd
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
plugin
*
plugins
.
Plugin
expectedSuccess
bool
expectedNilCmd
bool
}{
{
name
:
"success"
,
plugin
:
&
plugins
.
Plugin
{
Description
:
plugins
.
Description
{
Name
:
"success"
,
ShortDesc
:
"The Test Plugin"
,
Command
:
"echo ok"
,
},
},
expectedSuccess
:
true
,
},
{
name
:
"incomplete"
,
plugin
:
&
plugins
.
Plugin
{
Description
:
plugins
.
Description
{
Name
:
"incomplete"
,
ShortDesc
:
"The Incomplete Plugin"
,
},
},
expectedNilCmd
:
true
,
},
{
name
:
"failure"
,
plugin
:
&
plugins
.
Plugin
{
Description
:
plugins
.
Description
{
Name
:
"failure"
,
ShortDesc
:
"The Failing Plugin"
,
Command
:
"false"
,
},
},
expectedSuccess
:
false
,
},
}
for
_
,
test
:=
range
tests
{
inBuf
:=
bytes
.
NewBuffer
([]
byte
{})
outBuf
:=
bytes
.
NewBuffer
([]
byte
{})
errBuf
:=
bytes
.
NewBuffer
([]
byte
{})
cmdutil
.
BehaviorOnFatal
(
func
(
str
string
,
code
int
)
{
errBuf
.
Write
([]
byte
(
str
))
})
runner
:=
&
mockPluginRunner
{
success
:
test
.
expectedSuccess
,
}
cmd
:=
NewCmdForPlugin
(
test
.
plugin
,
runner
,
inBuf
,
outBuf
,
errBuf
)
if
cmd
==
nil
{
if
!
test
.
expectedNilCmd
{
t
.
Fatalf
(
"%s: command was unexpectedly not registered"
,
test
.
name
)
}
continue
}
cmd
.
Run
(
cmd
,
[]
string
{})
if
test
.
expectedSuccess
&&
outBuf
.
String
()
!=
fmt
.
Sprintf
(
"ok: %s"
,
test
.
plugin
.
Name
)
{
t
.
Errorf
(
"%s: unexpected output: %q"
,
test
.
name
,
outBuf
.
String
())
}
if
!
test
.
expectedSuccess
&&
errBuf
.
String
()
!=
fmt
.
Sprintf
(
"error: oops %s"
,
test
.
plugin
.
Name
)
{
t
.
Errorf
(
"%s: unexpected err output: %q"
,
test
.
name
,
errBuf
.
String
())
}
}
}
pkg/kubectl/cmd/templates/normalizers.go
View file @
2b178ad6
...
...
@@ -28,11 +28,17 @@ const Indentation = ` `
// LongDesc normalizes a command's long description to follow the conventions.
func
LongDesc
(
s
string
)
string
{
if
len
(
s
)
==
0
{
return
s
}
return
normalizer
{
s
}
.
heredoc
()
.
markdown
()
.
trim
()
.
string
}
// Examples normalizes a command's examples to follow the conventions.
func
Examples
(
s
string
)
string
{
if
len
(
s
)
==
0
{
return
s
}
return
normalizer
{
s
}
.
trim
()
.
indent
()
.
string
}
...
...
pkg/kubectl/cmd/testing/BUILD
View file @
2b178ad6
...
...
@@ -20,6 +20,7 @@ go_library(
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/cmd/util/openapi:go_default_library",
"//pkg/kubectl/plugins:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/printers:go_default_library",
"//vendor/github.com/emicklei/go-restful/swagger:go_default_library",
...
...
pkg/kubectl/cmd/testing/fake.go
View file @
2b178ad6
...
...
@@ -43,6 +43,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl"
cmdutil
"k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
"k8s.io/kubernetes/pkg/kubectl/plugins"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
)
...
...
@@ -481,6 +482,14 @@ func (f *FakeFactory) SuggestedPodTemplateResources() []schema.GroupResource {
return
[]
schema
.
GroupResource
{}
}
func
(
f
*
FakeFactory
)
PluginLoader
()
plugins
.
PluginLoader
{
return
&
plugins
.
DummyPluginLoader
{}
}
func
(
f
*
FakeFactory
)
PluginRunner
()
plugins
.
PluginRunner
{
return
&
plugins
.
ExecPluginRunner
{}
}
type
fakeMixedFactory
struct
{
cmdutil
.
Factory
tf
*
TestFactory
...
...
pkg/kubectl/cmd/util/BUILD
View file @
2b178ad6
...
...
@@ -38,6 +38,7 @@ go_library(
"//pkg/controller:go_default_library",
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/util/openapi:go_default_library",
"//pkg/kubectl/plugins:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/printers/internalversion:go_default_library",
...
...
pkg/kubectl/cmd/util/factory.go
View file @
2b178ad6
...
...
@@ -52,6 +52,7 @@ import (
coreclient
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
"k8s.io/kubernetes/pkg/kubectl/plugins"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
)
...
...
@@ -240,6 +241,10 @@ type BuilderFactory interface {
PrintObject
(
cmd
*
cobra
.
Command
,
mapper
meta
.
RESTMapper
,
obj
runtime
.
Object
,
out
io
.
Writer
)
error
// One stop shopping for a Builder
NewBuilder
()
*
resource
.
Builder
// PluginLoader provides the implementation to be used to load cli plugins.
PluginLoader
()
plugins
.
PluginLoader
// PluginRunner provides the implementation to be used to run cli plugins.
PluginRunner
()
plugins
.
PluginRunner
}
func
getGroupVersionKinds
(
gvks
[]
schema
.
GroupVersionKind
,
group
string
)
[]
schema
.
GroupVersionKind
{
...
...
pkg/kubectl/cmd/util/factory_builder.go
View file @
2b178ad6
...
...
@@ -21,12 +21,14 @@ package util
import
(
"fmt"
"io"
"os"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/kubectl/plugins"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
)
...
...
@@ -130,3 +132,22 @@ func (f *ring2Factory) NewBuilder() *resource.Builder {
return
resource
.
NewBuilder
(
mapper
,
categoryExpander
,
typer
,
resource
.
ClientMapperFunc
(
f
.
objectMappingFactory
.
ClientForMapping
),
f
.
clientAccessFactory
.
Decoder
(
true
))
}
// PluginLoader loads plugins from a path set by the KUBECTL_PLUGINS_PATH env var.
// If this env var is not set, it defaults to
// "~/.kube/plugins", plus
// "./kubectl/plugins" directory under the "data dir" directory specified by the XDG
// system directory structure spec for the given platform.
func
(
f
*
ring2Factory
)
PluginLoader
()
plugins
.
PluginLoader
{
if
len
(
os
.
Getenv
(
"KUBECTL_PLUGINS_PATH"
))
>
0
{
return
plugins
.
PluginsEnvVarPluginLoader
()
}
return
plugins
.
TolerantMultiPluginLoader
{
plugins
.
XDGDataPluginLoader
(),
plugins
.
UserDirPluginLoader
(),
}
}
func
(
f
*
ring2Factory
)
PluginRunner
()
plugins
.
PluginRunner
{
return
&
plugins
.
ExecPluginRunner
{}
}
pkg/kubectl/plugins/BUILD
0 → 100644
View file @
2b178ad6
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"loader.go",
"plugins.go",
"runner.go",
],
tags = ["automanaged"],
deps = [
"//vendor/github.com/ghodss/yaml:go_default_library",
"//vendor/github.com/golang/glog: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 = [
"loader_test.go",
"plugins_test.go",
"runner_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
)
pkg/kubectl/plugins/examples/aging/aging.rb
0 → 100755
View file @
2b178ad6
#!/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
pods_json
=
`kubectl 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
pkg/kubectl/plugins/examples/aging/plugin.yaml
0 → 100644
View file @
2b178ad6
name
:
"
aging"
shortDesc
:
"
Aging
shows
pods
by
age"
longDesc
:
>
Aging shows pods from the current namespace by age.
Once we have plugin support for global flags through
env vars (planned for V1) we'll be able to switch
between namespaces using the --namespace flag.
command
:
./aging.rb
pkg/kubectl/plugins/examples/hello/plugin.yaml
0 → 100644
View file @
2b178ad6
name
:
"
hello"
shortDesc
:
"
I
say
hello!"
command
:
"
echo
Hello
plugins!"
pkg/kubectl/plugins/loader.go
0 → 100644
View file @
2b178ad6
/*
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"
)
const
PluginDescriptorFilename
=
"plugin.yaml"
// PluginLoader is capable of loading a list of plugin descriptions.
type
PluginLoader
interface
{
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
fileInfo
.
IsDir
()
||
fileInfo
.
Name
()
!=
PluginDescriptorFilename
||
walkErr
!=
nil
{
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
}
plugin
.
Dir
=
filepath
.
Dir
(
path
)
plugin
.
DescriptorName
=
fileInfo
.
Name
()
glog
.
V
(
6
)
.
Infof
(
"Plugin loaded: %s"
,
plugin
.
Name
)
list
=
append
(
list
,
plugin
)
return
nil
})
return
list
,
err
}
// UserDirPluginLoader is 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 is 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
}
// PluginsEnvVarPluginLoader is a PluginLoader that loads plugins from one or more
// directories specified by the KUBECTL_PLUGINS_PATH env var.
func
PluginsEnvVarPluginLoader
()
PluginLoader
{
return
PathFromEnvVarPluginLoader
(
"KUBECTL_PLUGINS_PATH"
)
}
// XDGDataPluginLoader is 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
XDGDataPluginLoader
()
PluginLoader
{
envVarName
:=
"XDG_DATA_DIRS"
if
len
(
os
.
Getenv
(
envVarName
))
>
0
{
return
PathFromEnvVarPluginLoader
(
envVarName
,
"kubectl"
,
"plugins"
)
}
return
TolerantMultiPluginLoader
{
&
DirectoryPluginLoader
{
Directory
:
"/usr/local/share"
,
},
&
DirectoryPluginLoader
{
Directory
:
"/usr/share"
,
},
}
}
// 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
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
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 loads nothing.
type
DummyPluginLoader
struct
{}
func
(
l
*
DummyPluginLoader
)
Load
()
(
Plugins
,
error
)
{
return
Plugins
{},
nil
}
pkg/kubectl/plugins/loader_test.go
0 → 100644
View file @
2b178ad6
/*
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
)
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
)
}
}
}
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
TestPluginsEnvVarPluginLoader
(
t
*
testing
.
T
)
{
tmp
,
err
:=
setupValidPlugins
(
1
)
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
:=
PluginsEnvVarPluginLoader
()
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
setupValidPlugins
(
count
int
)
(
string
,
error
)
{
tmp
,
err
:=
ioutil
.
TempDir
(
""
,
""
)
if
err
!=
nil
{
return
""
,
fmt
.
Errorf
(
"unexpected ioutil.TempDir error: %v"
,
err
)
}
for
i
:=
1
;
i
<=
count
;
i
++
{
name
:=
fmt
.
Sprintf
(
"plugin%d"
,
i
)
descriptor
:=
fmt
.
Sprintf
(
`
name: %[1]s
shortDesc: The %[1]s test plugin
command: echo %[1]s`
,
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
}
pkg/kubectl/plugins/plugins.go
0 → 100644
View file @
2b178ad6
/*
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"
// Plugin is the representation of a CLI extension (plugin).
type
Plugin
struct
{
Description
Source
Context
RunningContext
`json:"-"`
}
// PluginDescription 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"`
}
// PluginSource holds the location of a given plugin in the filesystem.
type
Source
struct
{
Dir
string
`json:"-"`
DescriptorName
string
`json:"-"`
}
var
IncompleteError
=
fmt
.
Errorf
(
"incomplete plugin descriptor: name, shortDesc and command fields are required"
)
func
(
p
Plugin
)
Validate
()
error
{
if
len
(
p
.
Name
)
==
0
||
len
(
p
.
ShortDesc
)
==
0
||
len
(
p
.
Command
)
==
0
{
return
IncompleteError
}
return
nil
}
func
(
p
Plugin
)
IsValid
()
bool
{
return
p
.
Validate
()
==
nil
}
// Plugins is a list of plugins.
type
Plugins
[]
*
Plugin
pkg/kubectl/plugins/plugins_test.go
0 → 100644
View file @
2b178ad6
/*
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
(
"strings"
"testing"
)
func
TestPlugin
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
plugin
Plugin
expectedErr
string
expectedValid
bool
}{
{
plugin
:
Plugin
{
Description
:
Description
{
Name
:
"test"
,
ShortDesc
:
"The test"
,
Command
:
"echo 1"
,
},
},
expectedValid
:
true
,
},
{
plugin
:
Plugin
{
Description
:
Description
{
Name
:
"test"
,
ShortDesc
:
"The test"
,
},
},
expectedErr
:
"incomplete"
,
},
{
plugin
:
Plugin
{},
expectedErr
:
"incomplete"
,
},
}
for
_
,
test
:=
range
tests
{
if
is
:=
test
.
plugin
.
IsValid
();
test
.
expectedValid
!=
is
{
t
.
Errorf
(
"%s: expected valid=%v, got %v"
,
test
.
plugin
.
Name
,
test
.
expectedValid
,
is
)
}
err
:=
test
.
plugin
.
Validate
()
if
len
(
test
.
expectedErr
)
>
0
{
if
err
==
nil
{
t
.
Errorf
(
"%s: expected error, got none"
,
test
.
plugin
.
Name
)
}
else
if
!
strings
.
Contains
(
err
.
Error
(),
test
.
expectedErr
)
{
t
.
Errorf
(
"%s: expected error containing %q, got %v"
,
test
.
plugin
.
Name
,
test
.
expectedErr
,
err
)
}
}
else
if
err
!=
nil
{
t
.
Errorf
(
"%s: expected no error, got %v"
,
test
.
plugin
.
Name
,
err
)
}
}
}
pkg/kubectl/plugins/runner.go
0 → 100644
View file @
2b178ad6
/*
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/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
Env
[]
string
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
(
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
cmd
.
Env
=
ctx
.
Env
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
()
}
pkg/kubectl/plugins/runner_test.go
0 → 100644
View file @
2b178ad6
/*
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"
"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"
,
},
}
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
:
"."
,
}
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
)
}
}
}
staging/src/k8s.io/client-go/tools/clientcmd/loader.go
View file @
2b178ad6
...
...
@@ -47,8 +47,11 @@ const (
RecommendedSchemaName
=
"schema"
)
var
RecommendedHomeFile
=
path
.
Join
(
homedir
.
HomeDir
(),
RecommendedHomeDir
,
RecommendedFileName
)
var
RecommendedSchemaFile
=
path
.
Join
(
homedir
.
HomeDir
(),
RecommendedHomeDir
,
RecommendedSchemaName
)
var
(
RecommendedConfigDir
=
path
.
Join
(
homedir
.
HomeDir
(),
RecommendedHomeDir
)
RecommendedHomeFile
=
path
.
Join
(
RecommendedConfigDir
,
RecommendedFileName
)
RecommendedSchemaFile
=
path
.
Join
(
RecommendedConfigDir
,
RecommendedSchemaName
)
)
// currentMigrationRules returns a map that holds the history of recommended home directories used in previous versions.
// Any future changes to RecommendedHomeFile and related are expected to add a migration rule here, in order to make
...
...
test/fixtures/pkg/kubectl/plugins/echo/plugin.yaml
0 → 100644
View file @
2b178ad6
name
:
"
echo"
shortDesc
:
"
Echoes
for
test-cmd"
longDesc
:
"
Long
description
for
the
test-cmd
echo
plugin"
command
:
"
echo
This
plugin
works!"
test/fixtures/pkg/kubectl/plugins/error/plugin.yaml
0 → 100644
View file @
2b178ad6
name
:
"
error"
shortDesc
:
"
The
tremendous
plugin
that
always
fails!"
command
:
"
false"
test/fixtures/pkg/kubectl/plugins/get/plugin.yaml
0 → 100644
View file @
2b178ad6
name
:
"
get"
shortDesc
:
"
The
wonderful
new
plugin-based
get!"
command
:
"
echo
new-get"
test/fixtures/pkg/kubectl/plugins/incomplete/plugin.yaml
0 → 100644
View file @
2b178ad6
name
:
"
incomplete"
shortDesc
:
"
Incomplete
plugin"
test/fixtures/pkg/kubectl/plugins2/hello/hello.sh
0 → 100755
View file @
2b178ad6
#!/bin/bash
# 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.
echo
"#######"
echo
"#hello#"
echo
"#######"
test/fixtures/pkg/kubectl/plugins2/hello/plugin.yaml
0 → 100644
View file @
2b178ad6
name
:
hello
shortDesc
:
"
The
hello
plugin"
longDesc
:
>
The hello plugin is a new
plugin used by test-cmd
to test multiple plugin locations.
command
:
./hello.sh
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment