Add command "kubectl replace". "kubectl update" is still supported as an alias.

"kubectl replace --patch" is NOT supported. It's moved to "kubectl patch" as a separate command in another commit.
This commit is contained in:
Chao Xu 2015-06-26 21:25:08 -07:00
parent ad12c98e6d
commit 9b3d42c090
16 changed files with 101 additions and 189 deletions

View File

@ -321,9 +321,9 @@ _kubectl_create()
must_have_one_noun=() must_have_one_noun=()
} }
_kubectl_update() _kubectl_replace()
{ {
last_command="kubectl_update" last_command="kubectl_replace"
commands=() commands=()
flags=() flags=()
@ -342,13 +342,11 @@ _kubectl_update()
flags+=("--grace-period=") flags+=("--grace-period=")
flags+=("--help") flags+=("--help")
flags+=("-h") flags+=("-h")
flags+=("--patch=")
flags+=("--timeout=") flags+=("--timeout=")
must_have_one_flag=() must_have_one_flag=()
must_have_one_flag+=("--filename=") must_have_one_flag+=("--filename=")
must_have_one_flag+=("-f") must_have_one_flag+=("-f")
must_have_one_flag+=("--patch=")
must_have_one_noun=() must_have_one_noun=()
} }
@ -906,7 +904,7 @@ _kubectl()
commands+=("get") commands+=("get")
commands+=("describe") commands+=("describe")
commands+=("create") commands+=("create")
commands+=("update") commands+=("replace")
commands+=("delete") commands+=("delete")
commands+=("namespace") commands+=("namespace")
commands+=("logs") commands+=("logs")

View File

@ -20,9 +20,9 @@ kubectl_logs.md
kubectl_namespace.md kubectl_namespace.md
kubectl_port-forward.md kubectl_port-forward.md
kubectl_proxy.md kubectl_proxy.md
kubectl_replace.md
kubectl_rolling-update.md kubectl_rolling-update.md
kubectl_run.md kubectl_run.md
kubectl_scale.md kubectl_scale.md
kubectl_stop.md kubectl_stop.md
kubectl_update.md
kubectl_version.md kubectl_version.md

View File

@ -44,7 +44,7 @@ pods are replicated, upgrades can be done without special coordination.
If you want more control over the upgrading process, you may use the following workflow: If you want more control over the upgrading process, you may use the following workflow:
1. Mark the node to be rebooted as unschedulable: 1. Mark the node to be rebooted as unschedulable:
`kubectl update nodes $NODENAME --patch='{"apiVersion": "v1", "spec": {"unschedulable": true}}'`. `kubectl replace nodes $NODENAME --patch='{"apiVersion": "v1", "spec": {"unschedulable": true}}'`.
This keeps new pods from landing on the node while you are trying to get them off. This keeps new pods from landing on the node while you are trying to get them off.
1. Get the pods off the machine, via any of the following strategies: 1. Get the pods off the machine, via any of the following strategies:
1. wait for finite-duration pods to complete 1. wait for finite-duration pods to complete
@ -53,7 +53,7 @@ If you want more control over the upgrading process, you may use the following w
1. for pods with no replication controller, you need to bring up a new copy of the pod, and assuming it is not part of a service, redirect clients to it. 1. for pods with no replication controller, you need to bring up a new copy of the pod, and assuming it is not part of a service, redirect clients to it.
1. Work on the node 1. Work on the node
1. Make the node schedulable again: 1. Make the node schedulable again:
`kubectl update nodes $NODENAME --patch='{"apiVersion": "v1", "spec": {"unschedulable": false}}'`. `kubectl replace nodes $NODENAME --patch='{"apiVersion": "v1", "spec": {"unschedulable": false}}'`.
If you deleted the node's VM instance and created a new one, then a new schedulable node resource will If you deleted the node's VM instance and created a new one, then a new schedulable node resource will
be created automatically when you create a new VM instance (if you're using a cloud provider that supports be created automatically when you create a new VM instance (if you're using a cloud provider that supports
node discovery; currently this is only Google Compute Engine, not including CoreOS on Google Compute Engine using kube-register). See [Node](node.md). node discovery; currently this is only Google Compute Engine, not including CoreOS on Google Compute Engine using kube-register). See [Node](node.md).

View File

@ -58,13 +58,13 @@ kubectl
* [kubectl namespace](kubectl_namespace.md) - SUPERCEDED: Set and view the current Kubernetes namespace * [kubectl namespace](kubectl_namespace.md) - SUPERCEDED: Set and view the current Kubernetes namespace
* [kubectl port-forward](kubectl_port-forward.md) - Forward one or more local ports to a pod. * [kubectl port-forward](kubectl_port-forward.md) - Forward one or more local ports to a pod.
* [kubectl proxy](kubectl_proxy.md) - Run a proxy to the Kubernetes API server * [kubectl proxy](kubectl_proxy.md) - Run a proxy to the Kubernetes API server
* [kubectl replace](kubectl_replace.md) - Replace a resource by filename or stdin.
* [kubectl rolling-update](kubectl_rolling-update.md) - Perform a rolling update of the given ReplicationController. * [kubectl rolling-update](kubectl_rolling-update.md) - Perform a rolling update of the given ReplicationController.
* [kubectl run](kubectl_run.md) - Run a particular image on the cluster. * [kubectl run](kubectl_run.md) - Run a particular image on the cluster.
* [kubectl scale](kubectl_scale.md) - Set a new size for a Replication Controller. * [kubectl scale](kubectl_scale.md) - Set a new size for a Replication Controller.
* [kubectl stop](kubectl_stop.md) - Gracefully shut down a resource by id or filename. * [kubectl stop](kubectl_stop.md) - Gracefully shut down a resource by id or filename.
* [kubectl update](kubectl_update.md) - Update a resource by filename or stdin.
* [kubectl version](kubectl_version.md) - Print the client and server version information. * [kubectl version](kubectl_version.md) - Print the client and server version information.
###### Auto generated by spf13/cobra at 2015-05-22 14:24:30.1784975 +0000 UTC ###### Auto generated by spf13/cobra at 2015-06-29 00:10:06.115525904 +0000 UTC
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/kubectl.md?pixel)]() [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/kubectl.md?pixel)]()

View File

@ -1,44 +1,40 @@
## kubectl update ## kubectl replace
Update a resource by filename or stdin. Replace a resource by filename or stdin.
### Synopsis ### Synopsis
Update a resource by filename or stdin. Replace a resource by filename or stdin.
JSON and YAML formats are accepted. JSON and YAML formats are accepted.
``` ```
kubectl update -f FILENAME kubectl replace -f FILENAME
``` ```
### Examples ### Examples
``` ```
// Update a pod using the data in pod.json. // Replace a pod using the data in pod.json.
$ kubectl update -f pod.json $ kubectl replace -f pod.json
// Update a pod based on the JSON passed into stdin. // Replace a pod based on the JSON passed into stdin.
$ cat pod.json | kubectl update -f - $ cat pod.json | kubectl replace -f -
// Partially update a node using strategic merge patch // Force replace, delete and then re-create the resource
kubectl --api-version=v1 update node k8s-node-1 --patch='{"spec":{"unschedulable":true}}' kubectl replace --force -f pod.json
// Force update, delete and then re-create the resource
kubectl update --force -f pod.json
``` ```
### Options ### Options
``` ```
--cascade=false: Only relevant during a force update. If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController). Default true. --cascade=false: Only relevant during a force replace. If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController). Default true.
-f, --filename=[]: Filename, directory, or URL to file to use to update the resource. -f, --filename=[]: Filename, directory, or URL to file to use to replace the resource.
--force=false: Delete and re-create the specified resource --force=false: Delete and re-create the specified resource
--grace-period=-1: Only relevant during a force update. Period of time in seconds given to the old resource to terminate gracefully. Ignored if negative. --grace-period=-1: Only relevant during a force replace. Period of time in seconds given to the old resource to terminate gracefully. Ignored if negative.
-h, --help=false: help for update -h, --help=false: help for replace
--patch="": A JSON document to override the existing resource. The resource is downloaded, patched with the JSON, then updated. --timeout=0: Only relevant during a force replace. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object
--timeout=0: Only relevant during a force update. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object
``` ```
### Options inherited from parent commands ### Options inherited from parent commands
@ -73,6 +69,6 @@ kubectl update --force -f pod.json
### SEE ALSO ### SEE ALSO
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
###### Auto generated by spf13/cobra at 2015-06-26 00:15:55.835055081 +0000 UTC ###### Auto generated by spf13/cobra at 2015-06-29 00:11:27.040756424 +0000 UTC
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/kubectl_update.md?pixel)]() [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/kubectl_replace.md?pixel)]()

View File

@ -19,10 +19,10 @@ kubectl-logs.1
kubectl-namespace.1 kubectl-namespace.1
kubectl-port-forward.1 kubectl-port-forward.1
kubectl-proxy.1 kubectl-proxy.1
kubectl-replace.1
kubectl-rolling-update.1 kubectl-rolling-update.1
kubectl-run.1 kubectl-run.1
kubectl-scale.1 kubectl-scale.1
kubectl-stop.1 kubectl-stop.1
kubectl-update.1
kubectl-version.1 kubectl-version.1
kubectl.1 kubectl.1

View File

@ -3,17 +3,17 @@
.SH NAME .SH NAME
.PP .PP
kubectl update \- Update a resource by filename or stdin. kubectl replace \- Replace a resource by filename or stdin.
.SH SYNOPSIS .SH SYNOPSIS
.PP .PP
\fBkubectl update\fP [OPTIONS] \fBkubectl replace\fP [OPTIONS]
.SH DESCRIPTION .SH DESCRIPTION
.PP .PP
Update a resource by filename or stdin. Replace a resource by filename or stdin.
.PP .PP
JSON and YAML formats are accepted. JSON and YAML formats are accepted.
@ -22,11 +22,11 @@ JSON and YAML formats are accepted.
.SH OPTIONS .SH OPTIONS
.PP .PP
\fB\-\-cascade\fP=false \fB\-\-cascade\fP=false
Only relevant during a force update. If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController). Default true. Only relevant during a force replace. If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController). Default true.
.PP .PP
\fB\-f\fP, \fB\-\-filename\fP=[] \fB\-f\fP, \fB\-\-filename\fP=[]
Filename, directory, or URL to file to use to update the resource. Filename, directory, or URL to file to use to replace the resource.
.PP .PP
\fB\-\-force\fP=false \fB\-\-force\fP=false
@ -34,19 +34,15 @@ JSON and YAML formats are accepted.
.PP .PP
\fB\-\-grace\-period\fP=\-1 \fB\-\-grace\-period\fP=\-1
Only relevant during a force update. Period of time in seconds given to the old resource to terminate gracefully. Ignored if negative. Only relevant during a force replace. Period of time in seconds given to the old resource to terminate gracefully. Ignored if negative.
.PP .PP
\fB\-h\fP, \fB\-\-help\fP=false \fB\-h\fP, \fB\-\-help\fP=false
help for update help for replace
.PP
\fB\-\-patch\fP=""
A JSON document to override the existing resource. The resource is downloaded, patched with the JSON, then updated.
.PP .PP
\fB\-\-timeout\fP=0 \fB\-\-timeout\fP=0
Only relevant during a force update. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object Only relevant during a force replace. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object
.SH OPTIONS INHERITED FROM PARENT COMMANDS .SH OPTIONS INHERITED FROM PARENT COMMANDS
@ -152,17 +148,14 @@ JSON and YAML formats are accepted.
.RS .RS
.nf .nf
// Update a pod using the data in pod.json. // Replace a pod using the data in pod.json.
$ kubectl update \-f pod.json $ kubectl replace \-f pod.json
// Update a pod based on the JSON passed into stdin. // Replace a pod based on the JSON passed into stdin.
$ cat pod.json | kubectl update \-f \- $ cat pod.json | kubectl replace \-f \-
// Partially update a node using strategic merge patch // Force replace, delete and then re\-create the resource
kubectl \-\-api\-version=v1 update node k8s\-node\-1 \-\-patch='\{"spec":\{"unschedulable":true\}\}' kubectl replace \-\-force \-f pod.json
// Force update, delete and then re\-create the resource
kubectl update \-\-force \-f pod.json
.fi .fi
.RE .RE

View File

@ -124,7 +124,7 @@ Find more information at
.SH SEE ALSO .SH SEE ALSO
.PP .PP
\fBkubectl\-get(1)\fP, \fBkubectl\-describe(1)\fP, \fBkubectl\-create(1)\fP, \fBkubectl\-update(1)\fP, \fBkubectl\-delete(1)\fP, \fBkubectl\-namespace(1)\fP, \fBkubectl\-logs(1)\fP, \fBkubectl\-rolling\-update(1)\fP, \fBkubectl\-scale(1)\fP, \fBkubectl\-exec(1)\fP, \fBkubectl\-port\-forward(1)\fP, \fBkubectl\-proxy(1)\fP, \fBkubectl\-run(1)\fP, \fBkubectl\-stop(1)\fP, \fBkubectl\-expose(1)\fP, \fBkubectl\-label(1)\fP, \fBkubectl\-config(1)\fP, \fBkubectl\-cluster\-info(1)\fP, \fBkubectl\-api\-versions(1)\fP, \fBkubectl\-version(1)\fP, \fBkubectl\-get(1)\fP, \fBkubectl\-describe(1)\fP, \fBkubectl\-create(1)\fP, \fBkubectl\-replace(1)\fP, \fBkubectl\-delete(1)\fP, \fBkubectl\-namespace(1)\fP, \fBkubectl\-logs(1)\fP, \fBkubectl\-rolling\-update(1)\fP, \fBkubectl\-scale(1)\fP, \fBkubectl\-exec(1)\fP, \fBkubectl\-port\-forward(1)\fP, \fBkubectl\-proxy(1)\fP, \fBkubectl\-run(1)\fP, \fBkubectl\-stop(1)\fP, \fBkubectl\-expose(1)\fP, \fBkubectl\-label(1)\fP, \fBkubectl\-config(1)\fP, \fBkubectl\-cluster\-info(1)\fP, \fBkubectl\-api\-versions(1)\fP, \fBkubectl\-version(1)\fP,
.SH HISTORY .SH HISTORY

View File

@ -146,7 +146,7 @@ node, but will not affect any existing pods on the node. This is useful as a
preparatory step before a node reboot, etc. For example, to mark a node preparatory step before a node reboot, etc. For example, to mark a node
unschedulable, run this command: unschedulable, run this command:
``` ```
kubectl update nodes 10.1.2.3 --patch='{"apiVersion": "v1", "unschedulable": true}' kubectl replace nodes 10.1.2.3 --patch='{"apiVersion": "v1", "unschedulable": true}'
``` ```
### Node capacity ### Node capacity

View File

@ -377,21 +377,13 @@ for version in "${kube_api_versions[@]}"; do
kubectl create -f examples/limitrange/valid-pod.json "${kube_flags[@]}" kubectl create -f examples/limitrange/valid-pod.json "${kube_flags[@]}"
# Post-condition: valid-pod POD is running # Post-condition: valid-pod POD is running
kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" 'valid-pod:' kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" 'valid-pod:'
## --patch update pod can change image ## --force replace pod can change other field, e.g., spec.container.name
# Pre-condition: valid-pod POD is running
kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" 'valid-pod:'
# Command # Command
kubectl update "${kube_flags[@]}" pod valid-pod --patch='{"spec":{"containers":[{"name": "kubernetes-serve-hostname", "image": "nginx"}]}}' kubectl get "${kube_flags[@]}" pod valid-pod -o json | sed 's/"kubernetes-serve-hostname"/"replaced-k8s-serve-hostname"/g' > tmp-valid-pod.json
# Post-condition: valid-pod POD has image nginx kubectl replace "${kube_flags[@]}" --force -f tmp-valid-pod.json
kube::test::get_object_assert pods "{{range.items}}{{$image_field}}:{{end}}" 'nginx:' # Post-condition: spec.container.name = "replaced-k8s-serve-hostname"
kube::test::get_object_assert 'pod valid-pod' "{{(index .spec.containers 0).name}}" 'replaced-k8s-serve-hostname'
## --force update pod can change other field, e.g., spec.container.name
# Command
kubectl get "${kube_flags[@]}" pod valid-pod -o json | sed 's/"kubernetes-serve-hostname"/"update-k8s-serve-hostname"/g' > tmp-valid-pod.json
kubectl update "${kube_flags[@]}" --force -f tmp-valid-pod.json
# Post-condition: spec.container.name = "update-k8s-serve-hostname"
kube::test::get_object_assert 'pod valid-pod' "{{(index .spec.containers 0).name}}" 'update-k8s-serve-hostname'
### Overwriting an existing label is not permitted ### Overwriting an existing label is not permitted
# Pre-condition: name is valid-pod # Pre-condition: name is valid-pod
@ -528,7 +520,7 @@ __EOF__
kube::test::get_object_assert services "{{range.items}}{{$id_field}}:{{end}}" 'kubernetes:redis-master:service-.*-test:' kube::test::get_object_assert services "{{range.items}}{{$id_field}}:{{end}}" 'kubernetes:redis-master:service-.*-test:'
### Identity ### Identity
kubectl get service "${kube_flags[@]}" service-${version}-test -o json | kubectl update "${kube_flags[@]}" -f - kubectl get service "${kube_flags[@]}" service-${version}-test -o json | kubectl replace "${kube_flags[@]}" -f -
### Delete services by id ### Delete services by id
# Pre-condition: redis-master-service service is running # Pre-condition: redis-master-service service is running
@ -711,16 +703,6 @@ __EOF__
kube::test::describe_object_assert nodes "127.0.0.1" "Name:" "Labels:" "CreationTimestamp:" "Conditions:" "Addresses:" "Capacity:" "Pods:" kube::test::describe_object_assert nodes "127.0.0.1" "Name:" "Labels:" "CreationTimestamp:" "Conditions:" "Addresses:" "Capacity:" "Pods:"
### --patch update can mark node unschedulable
# Pre-condition: node is schedulable
kube::test::get_object_assert "nodes 127.0.0.1" "{{.spec.unschedulable}}" '<no value>'
kubectl update "${kube_flags[@]}" nodes "127.0.0.1" --patch='{"spec":{"unschedulable":true}}'
# Post-condition: node is unschedulable
kube::test::get_object_assert "nodes 127.0.0.1" "{{.spec.unschedulable}}" 'true'
kubectl update "${kube_flags[@]}" nodes "127.0.0.1" --patch='{"spec":{"unschedulable":null}}'
# Post-condition: node is schedulable
kube::test::get_object_assert "nodes 127.0.0.1" "{{.spec.unschedulable}}" '<no value>'
########### ###########
# Nodes # # Nodes #
########### ###########

View File

@ -114,7 +114,7 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
cmds.AddCommand(NewCmdGet(f, out)) cmds.AddCommand(NewCmdGet(f, out))
cmds.AddCommand(NewCmdDescribe(f, out)) cmds.AddCommand(NewCmdDescribe(f, out))
cmds.AddCommand(NewCmdCreate(f, out)) cmds.AddCommand(NewCmdCreate(f, out))
cmds.AddCommand(NewCmdUpdate(f, out)) cmds.AddCommand(NewCmdReplace(f, out))
cmds.AddCommand(NewCmdDelete(f, out)) cmds.AddCommand(NewCmdDelete(f, out))
cmds.AddCommand(NewCmdNamespace(out)) cmds.AddCommand(NewCmdNamespace(out))

View File

@ -83,7 +83,7 @@ func updateObject(info *resource.Info, updateFn func(runtime.Object) (runtime.Ob
return nil, err return nil, err
} }
_, err = helper.Update(info.Namespace, info.Name, true, data) _, err = helper.Replace(info.Namespace, info.Name, true, data)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -19,10 +19,10 @@ package cmd
import ( import (
"fmt" "fmt"
"io" "io"
"os"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
@ -31,47 +31,47 @@ import (
) )
const ( const (
update_long = `Update a resource by filename or stdin. replace_long = `Replace a resource by filename or stdin.
JSON and YAML formats are accepted.` JSON and YAML formats are accepted.`
update_example = `// Update a pod using the data in pod.json. replace_example = `// Replace a pod using the data in pod.json.
$ kubectl update -f pod.json $ kubectl replace -f pod.json
// Update a pod based on the JSON passed into stdin. // Replace a pod based on the JSON passed into stdin.
$ cat pod.json | kubectl update -f - $ cat pod.json | kubectl replace -f -
// Partially update a node using strategic merge patch // Force replace, delete and then re-create the resource
kubectl --api-version=v1 update node k8s-node-1 --patch='{"spec":{"unschedulable":true}}' kubectl replace --force -f pod.json`
// Force update, delete and then re-create the resource
kubectl update --force -f pod.json`
) )
func NewCmdUpdate(f *cmdutil.Factory, out io.Writer) *cobra.Command { func NewCmdReplace(f *cmdutil.Factory, out io.Writer) *cobra.Command {
var filenames util.StringList var filenames util.StringList
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "update -f FILENAME", Use: "replace -f FILENAME",
Short: "Update a resource by filename or stdin.", // update is deprecated.
Long: update_long, Aliases: []string{"update"},
Example: update_example, Short: "Replace a resource by filename or stdin.",
Long: replace_long,
Example: replace_example,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := RunUpdate(f, out, cmd, args, filenames) err := RunReplace(f, out, cmd, args, filenames)
cmdutil.CheckCustomErr("Update failed", err) cmdutil.CheckCustomErr("Replace failed", err)
}, },
} }
usage := "Filename, directory, or URL to file to use to update the resource." usage := "Filename, directory, or URL to file to use to replace the resource."
kubectl.AddJsonFilenameFlag(cmd, &filenames, usage) kubectl.AddJsonFilenameFlag(cmd, &filenames, usage)
cmd.MarkFlagRequired("filename") cmd.MarkFlagRequired("filename")
cmd.Flags().String("patch", "", "A JSON document to override the existing resource. The resource is downloaded, patched with the JSON, then updated.")
cmd.MarkFlagRequired("patch")
cmd.Flags().Bool("force", false, "Delete and re-create the specified resource") cmd.Flags().Bool("force", false, "Delete and re-create the specified resource")
cmd.Flags().Bool("cascade", false, "Only relevant during a force update. If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController). Default true.") cmd.Flags().Bool("cascade", false, "Only relevant during a force replace. If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController). Default true.")
cmd.Flags().Int("grace-period", -1, "Only relevant during a force update. Period of time in seconds given to the old resource to terminate gracefully. Ignored if negative.") cmd.Flags().Int("grace-period", -1, "Only relevant during a force replace. Period of time in seconds given to the old resource to terminate gracefully. Ignored if negative.")
cmd.Flags().Duration("timeout", 0, "Only relevant during a force update. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object") cmd.Flags().Duration("timeout", 0, "Only relevant during a force replace. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object")
return cmd return cmd
} }
func RunUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, filenames util.StringList) error { func RunReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, filenames util.StringList) error {
if os.Args[1] == "update" {
printDeprecationWarning("replace", "update")
}
schema, err := f.Validator() schema, err := f.Validator()
if err != nil { if err != nil {
return err return err
@ -83,32 +83,12 @@ func RunUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
} }
force := cmdutil.GetFlagBool(cmd, "force") force := cmdutil.GetFlagBool(cmd, "force")
patch := cmdutil.GetFlagString(cmd, "patch")
if len(filenames) == 0 && len(patch) == 0 {
return cmdutil.UsageError(cmd, "Must specify --filename or --patch to update")
}
if len(filenames) != 0 && len(patch) != 0 {
return cmdutil.UsageError(cmd, "Can not specify both --filename and --patch")
}
if len(filenames) == 0 && force {
return cmdutil.UsageError(cmd, "--force can only be used with --filename")
}
// TODO: Make patching work with -f, updating with patched JSON input files
if len(filenames) == 0 { if len(filenames) == 0 {
name, err := updateWithPatch(cmd, args, f, patch) return cmdutil.UsageError(cmd, "Must specify --filename to replace")
if err != nil {
return err
}
fmt.Fprintf(out, "%s\n", name)
return nil
}
if len(filenames) == 0 {
return cmdutil.UsageError(cmd, "Must specify --filename to update")
} }
if force { if force {
return forceUpdate(f, out, cmd, args, filenames) return forceReplace(f, out, cmd, args, filenames)
} }
mapper, typer := f.Object() mapper, typer := f.Object()
@ -127,11 +107,11 @@ func RunUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
return r.Visit(func(info *resource.Info) error { return r.Visit(func(info *resource.Info) error {
data, err := info.Mapping.Codec.Encode(info.Object) data, err := info.Mapping.Codec.Encode(info.Object)
if err != nil { if err != nil {
return cmdutil.AddSourceToErr("updating", info.Source, err) return cmdutil.AddSourceToErr("replacing", info.Source, err)
} }
obj, err := resource.NewHelper(info.Client, info.Mapping).Update(info.Namespace, info.Name, true, data) obj, err := resource.NewHelper(info.Client, info.Mapping).Replace(info.Namespace, info.Name, true, data)
if err != nil { if err != nil {
return cmdutil.AddSourceToErr("updating", info.Source, err) return cmdutil.AddSourceToErr("replacing", info.Source, err)
} }
info.Refresh(obj, true) info.Refresh(obj, true)
printObjectSpecificMessage(obj, out) printObjectSpecificMessage(obj, out)
@ -140,44 +120,7 @@ func RunUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
}) })
} }
func updateWithPatch(cmd *cobra.Command, args []string, f *cmdutil.Factory, patch string) (string, error) { func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, filenames util.StringList) error {
cmdNamespace, err := f.DefaultNamespace()
if err != nil {
return "", err
}
mapper, typer := f.Object()
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
ResourceTypeOrNameArgs(false, args...).
Flatten().
Do()
err = r.Err()
if err != nil {
return "", err
}
mapping, err := r.ResourceMapping()
if err != nil {
return "", err
}
client, err := f.RESTClient(mapping)
if err != nil {
return "", err
}
infos, err := r.Infos()
if err != nil {
return "", err
}
name, namespace := infos[0].Name, infos[0].Namespace
helper := resource.NewHelper(client, mapping)
_, err = helper.Patch(namespace, name, api.StrategicMergePatchType, []byte(patch))
return name, err
}
func forceUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, filenames util.StringList) error {
schema, err := f.Validator() schema, err := f.Validator()
if err != nil { if err != nil {
return err return err
@ -200,7 +143,7 @@ func forceUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
if err != nil { if err != nil {
return err return err
} }
//Update will create a resource if it doesn't exist already, so ignore not found error //Replace will create a resource if it doesn't exist already, so ignore not found error
ignoreNotFound := true ignoreNotFound := true
// By default use a reaper to delete all related resources. // By default use a reaper to delete all related resources.
if cmdutil.GetFlagBool(cmd, "cascade") { if cmdutil.GetFlagBool(cmd, "cascade") {
@ -245,7 +188,7 @@ func forceUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
return err return err
} }
if count == 0 { if count == 0 {
return fmt.Errorf("no objects passed to update") return fmt.Errorf("no objects passed to replace")
} }
return nil return nil
} }

View File

@ -25,7 +25,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
) )
func TestUpdateObject(t *testing.T) { func TestReplaceObject(t *testing.T) {
_, _, rc := testData() _, _, rc := testData()
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
@ -47,7 +47,7 @@ func TestUpdateObject(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := NewCmdUpdate(f, buf) cmd := NewCmdReplace(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
@ -66,7 +66,7 @@ func TestUpdateObject(t *testing.T) {
} }
} }
func TestUpdateMultipleObject(t *testing.T) { func TestReplaceMultipleObject(t *testing.T) {
_, svc, rc := testData() _, svc, rc := testData()
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
@ -92,7 +92,7 @@ func TestUpdateMultipleObject(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := NewCmdUpdate(f, buf) cmd := NewCmdReplace(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml")
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.yaml") cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.yaml")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
@ -111,7 +111,7 @@ func TestUpdateMultipleObject(t *testing.T) {
} }
} }
func TestUpdateDirectory(t *testing.T) { func TestReplaceDirectory(t *testing.T) {
_, svc, rc := testData() _, svc, rc := testData()
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
@ -137,7 +137,7 @@ func TestUpdateDirectory(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := NewCmdUpdate(f, buf) cmd := NewCmdReplace(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook") cmd.Flags().Set("filename", "../../../examples/guestbook")
cmd.Flags().Set("namespace", "test") cmd.Flags().Set("namespace", "test")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
@ -157,7 +157,7 @@ func TestUpdateDirectory(t *testing.T) {
} }
} }
func TestForceUpdateObjectNotFound(t *testing.T) { func TestForceReplaceObjectNotFound(t *testing.T) {
_, _, rc := testData() _, _, rc := testData()
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
@ -179,7 +179,7 @@ func TestForceUpdateObjectNotFound(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := NewCmdUpdate(f, buf) cmd := NewCmdReplace(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml")
cmd.Flags().Set("force", "true") cmd.Flags().Set("force", "true")
cmd.Flags().Set("cascade", "false") cmd.Flags().Set("cascade", "false")

View File

@ -143,27 +143,27 @@ func (m *Helper) Patch(namespace, name string, pt api.PatchType, data []byte) (r
Get() Get()
} }
func (m *Helper) Update(namespace, name string, overwrite bool, data []byte) (runtime.Object, error) { func (m *Helper) Replace(namespace, name string, overwrite bool, data []byte) (runtime.Object, error) {
c := m.RESTClient c := m.RESTClient
obj, err := m.Codec.Decode(data) obj, err := m.Codec.Decode(data)
if err != nil { if err != nil {
// We don't know how to handle this object, but update it anyway // We don't know how to handle this object, but replace it anyway
return m.updateResource(c, m.Resource, namespace, name, data) return m.replaceResource(c, m.Resource, namespace, name, data)
} }
// Attempt to version the object based on client logic. // Attempt to version the object based on client logic.
version, err := m.Versioner.ResourceVersion(obj) version, err := m.Versioner.ResourceVersion(obj)
if err != nil { if err != nil {
// We don't know how to version this object, so send it to the server as is // We don't know how to version this object, so send it to the server as is
return m.updateResource(c, m.Resource, namespace, name, data) return m.replaceResource(c, m.Resource, namespace, name, data)
} }
if version == "" && overwrite { if version == "" && overwrite {
// Retrieve the current version of the object to overwrite the server object // Retrieve the current version of the object to overwrite the server object
serverObj, err := c.Get().Namespace(namespace).Resource(m.Resource).Name(name).Do().Get() serverObj, err := c.Get().Namespace(namespace).Resource(m.Resource).Name(name).Do().Get()
if err != nil { if err != nil {
// The object does not exist, but we want it to be created // The object does not exist, but we want it to be created
return m.updateResource(c, m.Resource, namespace, name, data) return m.replaceResource(c, m.Resource, namespace, name, data)
} }
serverVersion, err := m.Versioner.ResourceVersion(serverObj) serverVersion, err := m.Versioner.ResourceVersion(serverObj)
if err != nil { if err != nil {
@ -179,9 +179,9 @@ func (m *Helper) Update(namespace, name string, overwrite bool, data []byte) (ru
data = newData data = newData
} }
return m.updateResource(c, m.Resource, namespace, name, data) return m.replaceResource(c, m.Resource, namespace, name, data)
} }
func (m *Helper) updateResource(c RESTClient, resource, namespace, name string, data []byte) (runtime.Object, error) { func (m *Helper) replaceResource(c RESTClient, resource, namespace, name string, data []byte) (runtime.Object, error) {
return c.Put().NamespaceIfScoped(namespace, m.NamespaceScoped).Resource(resource).Name(name).Body(data).Do().Get() return c.Put().NamespaceIfScoped(namespace, m.NamespaceScoped).Resource(resource).Name(name).Body(data).Do().Get()
} }

View File

@ -363,7 +363,7 @@ func TestHelperList(t *testing.T) {
} }
} }
func TestHelperUpdate(t *testing.T) { func TestHelperReplace(t *testing.T) {
expectPut := func(req *http.Request) bool { expectPut := func(req *http.Request) bool {
if req.Method != "PUT" { if req.Method != "PUT" {
t.Errorf("unexpected method: %#v", req) t.Errorf("unexpected method: %#v", req)
@ -457,7 +457,7 @@ func TestHelperUpdate(t *testing.T) {
if test.Object != nil { if test.Object != nil {
data = []byte(runtime.EncodeOrDie(testapi.Codec(), test.Object)) data = []byte(runtime.EncodeOrDie(testapi.Codec(), test.Object))
} }
_, err := modifier.Update("bar", "foo", test.Overwrite, data) _, err := modifier.Replace("bar", "foo", test.Overwrite, data)
if (err != nil) != test.Err { if (err != nil) != test.Err {
t.Errorf("%d: unexpected error: %t %v", i, test.Err, err) t.Errorf("%d: unexpected error: %t %v", i, test.Err, err)
} }