mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
Add the client side bits of kubectl export
This commit is contained in:
parent
4ca66d2aef
commit
4123a61df7
@ -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")
|
||||
|
@ -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.
|
||||
|
@ -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 -->
|
||||
[]()
|
||||
|
@ -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}
|
||||
|
@ -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[@]}")
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -68,6 +68,8 @@ type Builder struct {
|
||||
singleResourceType bool
|
||||
continueOnError bool
|
||||
|
||||
export bool
|
||||
|
||||
schema validation.Schema
|
||||
}
|
||||
|
||||
@ -234,6 +236,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 {
|
||||
@ -512,7 +520,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}
|
||||
@ -570,7 +578,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)
|
||||
}
|
||||
|
||||
@ -619,7 +627,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}
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user