Merge pull request #18101 from brendandburns/kubectl6

Auto commit by PR queue bot
This commit is contained in:
k8s-merge-robot 2015-12-29 12:43:42 -08:00
commit 356487c951
14 changed files with 69 additions and 23 deletions

View File

@ -265,6 +265,7 @@ _kubectl_get()
flags_completion=()
flags+=("--all-namespaces")
flags+=("--export")
flags+=("--filename=")
flags_with_completion+=("--filename")
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")

View File

@ -32,6 +32,10 @@ of the \-\-template flag, you can filter the attributes of the fetched resource(
\fB\-\-all\-namespaces\fP=false
If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with \-\-namespace.
.PP
\fB\-\-export\fP=false
If true, use 'export' for the resources. Exported resources are stripped of cluster\-specific information.
.PP
\fB\-f\fP, \fB\-\-filename\fP=[]
Filename, directory, or URL to a file identifying the resource to get from a server.

View File

@ -86,6 +86,7 @@ $ kubectl get rc/web service/frontend pods/web-pod-13je7
```
--all-namespaces[=false]: If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.
--export[=false]: If true, use 'export' for the resources. Exported resources are stripped of cluster-specific information.
-f, --filename=[]: Filename, directory, or URL to a file identifying the resource to get from a server.
-L, --label-columns=[]: Accepts a comma separated list of labels that are going to be presented as columns. Names are case-sensitive. You can also use multiple flag statements like -L label1 -L label2...
--no-headers[=false]: When using the default output, don't print headers.
@ -131,7 +132,7 @@ $ kubectl get rc/web service/frontend pods/web-pod-13je7
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
###### Auto generated by spf13/cobra on 8-Dec-2015
###### Auto generated by spf13/cobra on 22-Dec-2015
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_get.md?pixel)]()

View File

@ -30,8 +30,9 @@ kube::test::get_object_assert() {
local object=$1
local request=$2
local expected=$3
local args=${4:-}
res=$(eval kubectl get "${kube_flags[@]}" $object -o go-template=\"$request\")
res=$(eval kubectl ${args} get "${kube_flags[@]}" $object -o go-template=\"$request\")
if [[ "$res" =~ ^$expected$ ]]; then
echo -n ${green}

View File

@ -287,6 +287,9 @@ runTests() {
# Describe command (resource only) should print detailed information
kube::test::describe_resource_assert pods "Name:" "Image(s):" "Node:" "Labels:" "Status:" "Controllers"
### Validate Export ###
kube::test::get_object_assert 'pods/valid-pod' "{{.metadata.namespace}} {{.metadata.name}}" '<no value> valid-pod' "--export=true"
### Dump current valid-pod POD
output_pod=$(kubectl get pod valid-pod -o yaml --output-version=v1 "${kube_flags[@]}")

View File

@ -170,6 +170,7 @@ func walkMapPath(t *testing.T, start map[string]interface{}, path []string) map[
}
func TestApplyObject(t *testing.T) {
initTestErrorHandler(t)
nameRC, currentRC := readAndAnnotateReplicationController(t, filenameRC)
pathRC := "/namespaces/test/replicationcontrollers/" + nameRC

View File

@ -41,6 +41,12 @@ import (
"k8s.io/kubernetes/pkg/util"
)
func initTestErrorHandler(t *testing.T) {
cmdutil.BehaviorOnFatal(func(str string) {
t.Errorf("Error running command: %s", str)
})
}
type internalType struct {
Kind string
APIVersion string

View File

@ -27,6 +27,7 @@ import (
)
func TestExtraArgsFail(t *testing.T) {
initTestErrorHandler(t)
buf := bytes.NewBuffer([]byte{})
f, _, _ := NewAPIFactory()
@ -37,6 +38,7 @@ func TestExtraArgsFail(t *testing.T) {
}
func TestCreateObject(t *testing.T) {
initTestErrorHandler(t)
_, _, rc := testData()
rc.Items[0].Name = "redis-master-controller"
@ -69,6 +71,7 @@ func TestCreateObject(t *testing.T) {
}
func TestCreateMultipleObject(t *testing.T) {
initTestErrorHandler(t)
_, svc, rc := testData()
f, tf, codec := NewAPIFactory()
@ -103,6 +106,7 @@ func TestCreateMultipleObject(t *testing.T) {
}
func TestCreateDirectory(t *testing.T) {
initTestErrorHandler(t)
_, svc, rc := testData()
rc.Items[0].Name = "name"
@ -136,6 +140,7 @@ func TestCreateDirectory(t *testing.T) {
}
func TestPrintObjectSpecificMessage(t *testing.T) {
initTestErrorHandler(t)
tests := []struct {
obj runtime.Object
expectOutput bool
@ -170,6 +175,7 @@ func TestPrintObjectSpecificMessage(t *testing.T) {
}
func TestMakePortsString(t *testing.T) {
initTestErrorHandler(t)
tests := []struct {
ports []api.ServicePort
useNodePort bool

View File

@ -101,6 +101,7 @@ func NewCmdGet(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd.Flags().Bool("watch-only", false, "Watch for changes to the requested object(s), without listing/getting first.")
cmd.Flags().Bool("all-namespaces", false, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
cmd.Flags().StringSliceP("label-columns", "L", []string{}, "Accepts a comma separated list of labels that are going to be presented as columns. Names are case-sensitive. You can also use multiple flag statements like -L label1 -L label2...")
cmd.Flags().Bool("export", false, "If true, use 'export' for the resources. Exported resources are stripped of cluster-specific information.")
usage := "Filename, directory, or URL to a file identifying the resource to get from a server."
kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage)
return cmd
@ -131,6 +132,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string
if len(options.Filenames) > 0 || argsHasNames {
cmd.Flag("show-all").Value.Set("true")
}
export := cmdutil.GetFlagBool(cmd, "export")
// handle watch separately since we cannot watch multiple resource types
isWatch, isWatchOnly := cmdutil.GetFlagBool(cmd, "watch"), cmdutil.GetFlagBool(cmd, "watch-only")
@ -139,6 +141,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string
NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces).
FilenameParam(enforceNamespace, options.Filenames...).
SelectorParam(selector).
ExportParam(export).
ResourceTypeOrNameArgs(true, args...).
SingleResourceType().
Latest().
@ -193,6 +196,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string
NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces).
FilenameParam(enforceNamespace, options.Filenames...).
SelectorParam(selector).
ExportParam(export).
ResourceTypeOrNameArgs(true, args...).
ContinueOnError().
Latest()

View File

@ -69,6 +69,8 @@ type Builder struct {
singleResourceType bool
continueOnError bool
export bool
schema validation.Schema
}
@ -235,6 +237,12 @@ func (b *Builder) Selector(selector labels.Selector) *Builder {
return b
}
// ExportParam accepts the export boolean for these resources
func (b *Builder) ExportParam(export bool) *Builder {
b.export = export
return b
}
// NamespaceParam accepts the namespace that these resources should be
// considered under from - used by DefaultNamespace() and RequireNamespace()
func (b *Builder) NamespaceParam(namespace string) *Builder {
@ -513,7 +521,7 @@ func (b *Builder) visitorResult() *Result {
if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
selectorNamespace = ""
}
visitors = append(visitors, NewSelector(client, mapping, selectorNamespace, b.selector))
visitors = append(visitors, NewSelector(client, mapping, selectorNamespace, b.selector, b.export))
}
if b.continueOnError {
return &Result{visitor: EagerVisitorList(visitors), sources: visitors}
@ -571,7 +579,7 @@ func (b *Builder) visitorResult() *Result {
}
}
info := NewInfo(client, mapping, selectorNamespace, tuple.Name)
info := NewInfo(client, mapping, selectorNamespace, tuple.Name, b.export)
items = append(items, info)
}
@ -620,7 +628,7 @@ func (b *Builder) visitorResult() *Result {
visitors := []Visitor{}
for _, name := range b.names {
info := NewInfo(client, mapping, selectorNamespace, name)
info := NewInfo(client, mapping, selectorNamespace, name, b.export)
visitors = append(visitors, info)
}
return &Result{singular: isSingular, visitor: VisitorList(visitors), sources: visitors}

View File

@ -17,6 +17,8 @@ limitations under the License.
package resource
import (
"strconv"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/labels"
@ -51,23 +53,27 @@ func NewHelper(client RESTClient, mapping *meta.RESTMapping) *Helper {
}
}
func (m *Helper) Get(namespace, name string) (runtime.Object, error) {
return m.RESTClient.Get().
func (m *Helper) Get(namespace, name string, export bool) (runtime.Object, error) {
req := m.RESTClient.Get().
NamespaceIfScoped(namespace, m.NamespaceScoped).
Resource(m.Resource).
Name(name).
Do().
Get()
Name(name)
if export {
req.Param("export", strconv.FormatBool(export))
}
return req.Do().Get()
}
// TODO: add field selector
func (m *Helper) List(namespace, apiVersion string, selector labels.Selector) (runtime.Object, error) {
return m.RESTClient.Get().
func (m *Helper) List(namespace, apiVersion string, selector labels.Selector, export bool) (runtime.Object, error) {
req := m.RESTClient.Get().
NamespaceIfScoped(namespace, m.NamespaceScoped).
Resource(m.Resource).
LabelsSelectorParam(selector).
Do().
Get()
LabelsSelectorParam(selector)
if export {
req.Param("export", strconv.FormatBool(export))
}
return req.Do().Get()
}
func (m *Helper) Watch(namespace, resourceVersion, apiVersion string, labelSelector labels.Selector) (watch.Interface, error) {

View File

@ -270,7 +270,7 @@ func TestHelperGet(t *testing.T) {
RESTClient: client,
NamespaceScoped: true,
}
obj, err := modifier.Get("bar", "foo")
obj, err := modifier.Get("bar", "foo", false)
if (err != nil) != test.Err {
t.Errorf("unexpected error: %t %v", test.Err, err)
}
@ -341,7 +341,7 @@ func TestHelperList(t *testing.T) {
RESTClient: client,
NamespaceScoped: true,
}
obj, err := modifier.List("bar", testapi.Default.GroupVersion().String(), labels.SelectorFromSet(labels.Set{"foo": "baz"}))
obj, err := modifier.List("bar", testapi.Default.GroupVersion().String(), labels.SelectorFromSet(labels.Set{"foo": "baz"}), false)
if (err != nil) != test.Err {
t.Errorf("unexpected error: %t %v", test.Err, err)
}

View File

@ -31,21 +31,23 @@ type Selector struct {
Mapping *meta.RESTMapping
Namespace string
Selector labels.Selector
Export bool
}
// NewSelector creates a resource selector which hides details of getting items by their label selector.
func NewSelector(client RESTClient, mapping *meta.RESTMapping, namespace string, selector labels.Selector) *Selector {
func NewSelector(client RESTClient, mapping *meta.RESTMapping, namespace string, selector labels.Selector, export bool) *Selector {
return &Selector{
Client: client,
Mapping: mapping,
Namespace: namespace,
Selector: selector,
Export: export,
}
}
// Visit implements Visitor
func (r *Selector) Visit(fn VisitorFunc) error {
list, err := NewHelper(r.Client, r.Mapping).List(r.Namespace, r.ResourceMapping().GroupVersionKind.GroupVersion().String(), r.Selector)
list, err := NewHelper(r.Client, r.Mapping).List(r.Namespace, r.ResourceMapping().GroupVersionKind.GroupVersion().String(), r.Selector, r.Export)
if err != nil {
if errors.IsBadRequest(err) || errors.IsNotFound(err) {
if r.Selector.Empty() {

View File

@ -85,15 +85,18 @@ type Info struct {
// but if set it should be equal to or newer than the resource version of the
// object (however the server defines resource version).
ResourceVersion string
// Optional, should this resource be exported, stripped of cluster-specific and instance specific fields
Export bool
}
// NewInfo returns a new info object
func NewInfo(client RESTClient, mapping *meta.RESTMapping, namespace, name string) *Info {
func NewInfo(client RESTClient, mapping *meta.RESTMapping, namespace, name string, export bool) *Info {
return &Info{
Client: client,
Mapping: mapping,
Namespace: namespace,
Name: name,
Export: export,
}
}
@ -103,8 +106,8 @@ func (i *Info) Visit(fn VisitorFunc) error {
}
// Get retrieves the object from the Namespace and Name fields
func (i *Info) Get() error {
obj, err := NewHelper(i.Client, i.Mapping).Get(i.Namespace, i.Name)
func (i *Info) Get() (err error) {
obj, err := NewHelper(i.Client, i.Mapping).Get(i.Namespace, i.Name, i.Export)
if err != nil {
return err
}
@ -573,7 +576,7 @@ func RetrieveLatest(info *Info, err error) error {
if info.Namespaced() && len(info.Namespace) == 0 {
return fmt.Errorf("no namespace set on resource %s %q", info.Mapping.Resource, info.Name)
}
obj, err := NewHelper(info.Client, info.Mapping).Get(info.Namespace, info.Name)
obj, err := NewHelper(info.Client, info.Mapping).Get(info.Namespace, info.Name, info.Export)
if err != nil {
return err
}