Commit d2c2f7e3 authored by Satnam Singh's avatar Satnam Singh

Merge pull request #9993 from mikedanese/re-patch

reenable patch serverside using strategic-merge-patch
parents 3c622108 1a7d811b
...@@ -337,10 +337,12 @@ _kubectl_update() ...@@ -337,10 +337,12 @@ _kubectl_update()
flags_completion+=("__handle_filename_extension_flag json|yaml|yml") flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
flags+=("--help") flags+=("--help")
flags+=("-h") flags+=("-h")
flags+=("--patch=")
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=()
} }
......
...@@ -21,6 +21,9 @@ $ kubectl update -f pod.json ...@@ -21,6 +21,9 @@ $ kubectl update -f pod.json
// Update a pod based on the JSON passed into stdin. // Update a pod based on the JSON passed into stdin.
$ cat pod.json | kubectl update -f - $ cat pod.json | kubectl update -f -
// Partially update a node using strategic merge patch
kubectl --api-version=v1 update node k8s-node-1 --patch='{"spec":{"unschedulable":true}}'
``` ```
### Options ### Options
...@@ -28,6 +31,7 @@ $ cat pod.json | kubectl update -f - ...@@ -28,6 +31,7 @@ $ cat pod.json | kubectl update -f -
``` ```
-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 update the resource.
-h, --help=false: help for update -h, --help=false: help for update
--patch="": A JSON document to override the existing resource. The resource is downloaded, patched with the JSON, then updated.
``` ```
### Options inherited from parent commands ### Options inherited from parent commands
...@@ -62,6 +66,6 @@ $ cat pod.json | kubectl update -f - ...@@ -62,6 +66,6 @@ $ cat pod.json | kubectl update -f -
### 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-05-29 01:11:24.431126385 +0000 UTC ###### Auto generated by spf13/cobra at 2015-06-18 19:03:00.935576604 +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_update.md?pixel)]()
...@@ -28,6 +28,10 @@ JSON and YAML formats are accepted. ...@@ -28,6 +28,10 @@ JSON and YAML formats are accepted.
\fB\-h\fP, \fB\-\-help\fP=false \fB\-h\fP, \fB\-\-help\fP=false
help for update help for update
.PP
\fB\-\-patch\fP=""
A JSON document to override the existing resource. The resource is downloaded, patched with the JSON, then updated.
.SH OPTIONS INHERITED FROM PARENT COMMANDS .SH OPTIONS INHERITED FROM PARENT COMMANDS
.PP .PP
...@@ -138,6 +142,9 @@ $ kubectl update \-f pod.json ...@@ -138,6 +142,9 @@ $ kubectl update \-f pod.json
// Update a pod based on the JSON passed into stdin. // Update a pod based on the JSON passed into stdin.
$ cat pod.json | kubectl update \-f \- $ cat pod.json | kubectl update \-f \-
// Partially update a node using strategic merge patch
kubectl \-\-api\-version=v1 update node k8s\-node\-1 \-\-patch='\{"spec":\{"unschedulable":true\}\}'
.fi .fi
.RE .RE
......
...@@ -141,6 +141,7 @@ for version in "${kube_api_versions[@]}"; do ...@@ -141,6 +141,7 @@ for version in "${kube_api_versions[@]}"; do
rc_status_replicas_field=".status.replicas" rc_status_replicas_field=".status.replicas"
rc_container_image_field=".spec.template.spec.containers" rc_container_image_field=".spec.template.spec.containers"
port_field="(index .spec.ports 0).port" port_field="(index .spec.ports 0).port"
image_field="(index .spec.containers 0).image"
# Passing no arguments to create is an error # Passing no arguments to create is an error
! kubectl create ! kubectl create
...@@ -306,6 +307,14 @@ for version in "${kube_api_versions[@]}"; do ...@@ -306,6 +307,14 @@ for version in "${kube_api_versions[@]}"; do
# 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
# Pre-condition: valid-pod POD is running
kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" 'valid-pod:'
# Command
kubectl update "${kube_flags[@]}" pod valid-pod --patch='{"spec":{"containers":[{"name": "kubernetes-serve-hostname", "image": "nginx"}]}}'
# Post-condition: valid-pod POD has image nginx
kube::test::get_object_assert pods "{{range.items}}{{$image_field}}:{{end}}" 'nginx:'
### 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
kube::test::get_object_assert 'pod valid-pod' "{{${labels_field}.name}}" 'valid-pod' kube::test::get_object_assert 'pod valid-pod' "{{${labels_field}.name}}" 'valid-pod'
...@@ -615,6 +624,15 @@ __EOF__ ...@@ -615,6 +624,15 @@ __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 #
......
...@@ -20,6 +20,7 @@ import ( ...@@ -20,6 +20,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
) )
...@@ -47,6 +48,10 @@ func (c *FakeRESTClient) Put() *Request { ...@@ -47,6 +48,10 @@ func (c *FakeRESTClient) Put() *Request {
return NewRequest(c, "PUT", &url.URL{Host: "localhost"}, testapi.Version(), c.Codec) return NewRequest(c, "PUT", &url.URL{Host: "localhost"}, testapi.Version(), c.Codec)
} }
func (c *FakeRESTClient) Patch(_ api.PatchType) *Request {
return NewRequest(c, "PATCH", &url.URL{Host: "localhost"}, testapi.Version(), c.Codec)
}
func (c *FakeRESTClient) Post() *Request { func (c *FakeRESTClient) Post() *Request {
return NewRequest(c, "POST", &url.URL{Host: "localhost"}, testapi.Version(), c.Codec) return NewRequest(c, "POST", &url.URL{Host: "localhost"}, testapi.Version(), c.Codec)
} }
......
...@@ -22,6 +22,7 @@ import ( ...@@ -22,6 +22,7 @@ import (
"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"
...@@ -36,7 +37,10 @@ JSON and YAML formats are accepted.` ...@@ -36,7 +37,10 @@ JSON and YAML formats are accepted.`
$ kubectl update -f pod.json $ kubectl update -f pod.json
// Update a pod based on the JSON passed into stdin. // Update a pod based on the JSON passed into stdin.
$ cat pod.json | kubectl update -f -` $ cat pod.json | kubectl update -f -
// Partially update a node using strategic merge patch
kubectl --api-version=v1 update node k8s-node-1 --patch='{"spec":{"unschedulable":true}}'`
) )
func NewCmdUpdate(f *cmdutil.Factory, out io.Writer) *cobra.Command { func NewCmdUpdate(f *cmdutil.Factory, out io.Writer) *cobra.Command {
...@@ -54,9 +58,8 @@ func NewCmdUpdate(f *cmdutil.Factory, out io.Writer) *cobra.Command { ...@@ -54,9 +58,8 @@ func NewCmdUpdate(f *cmdutil.Factory, out io.Writer) *cobra.Command {
usage := "Filename, directory, or URL to file to use to update the resource." usage := "Filename, directory, or URL to file to use to update the resource."
kubectl.AddJsonFilenameFlag(cmd, &filenames, usage) kubectl.AddJsonFilenameFlag(cmd, &filenames, usage)
cmd.MarkFlagRequired("filename") cmd.MarkFlagRequired("filename")
// TODO: re-enable --patch and make it use strategic-merge-patch cmd.Flags().String("patch", "", "A JSON document to override the existing resource. The resource is downloaded, patched with the JSON, then updated.")
// 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.MarkFlagRequired("patch")
return cmd return cmd
} }
...@@ -71,7 +74,7 @@ func RunUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str ...@@ -71,7 +74,7 @@ func RunUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
return err return err
} }
/* patch := cmdutil.GetFlagString(cmd, "patch") patch := cmdutil.GetFlagString(cmd, "patch")
if len(filenames) == 0 && len(patch) == 0 { if len(filenames) == 0 && len(patch) == 0 {
return cmdutil.UsageError(cmd, "Must specify --filename or --patch to update") return cmdutil.UsageError(cmd, "Must specify --filename or --patch to update")
} }
...@@ -87,7 +90,7 @@ func RunUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str ...@@ -87,7 +90,7 @@ func RunUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
} }
fmt.Fprintf(out, "%s\n", name) fmt.Fprintf(out, "%s\n", name)
return nil return nil
} */ }
if len(filenames) == 0 { if len(filenames) == 0 {
return cmdutil.UsageError(cmd, "Must specify --filename to update") return cmdutil.UsageError(cmd, "Must specify --filename to update")
} }
...@@ -154,21 +157,6 @@ func updateWithPatch(cmd *cobra.Command, args []string, f *cmdutil.Factory, patc ...@@ -154,21 +157,6 @@ func updateWithPatch(cmd *cobra.Command, args []string, f *cmdutil.Factory, patc
name, namespace := infos[0].Name, infos[0].Namespace name, namespace := infos[0].Name, infos[0].Namespace
helper := resource.NewHelper(client, mapping) helper := resource.NewHelper(client, mapping)
obj, err := helper.Get(namespace, name) _, err = helper.Patch(namespace, name, api.StrategicMergePatchType, []byte(patch))
if err != nil {
return "", err
}
patchedObj, err := cmdutil.Merge(obj, patch, mapping.Kind)
if err != nil {
return "", err
}
data, err := helper.Codec.Encode(patchedObj)
if err != nil {
return "", err
}
_, err = helper.Update(namespace, name, true, data)
return name, err return name, err
} }
...@@ -17,6 +17,7 @@ limitations under the License. ...@@ -17,6 +17,7 @@ limitations under the License.
package kubectl package kubectl
import ( import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
) )
...@@ -25,6 +26,7 @@ import ( ...@@ -25,6 +26,7 @@ import (
type RESTClient interface { type RESTClient interface {
Get() *client.Request Get() *client.Request
Post() *client.Request Post() *client.Request
Patch(api.PatchType) *client.Request
Delete() *client.Request Delete() *client.Request
Put() *client.Request Put() *client.Request
} }
...@@ -17,6 +17,7 @@ limitations under the License. ...@@ -17,6 +17,7 @@ limitations under the License.
package resource package resource
import ( import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields" "github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
...@@ -43,11 +44,10 @@ type Helper struct { ...@@ -43,11 +44,10 @@ type Helper struct {
// NewHelper creates a Helper from a ResourceMapping // NewHelper creates a Helper from a ResourceMapping
func NewHelper(client RESTClient, mapping *meta.RESTMapping) *Helper { func NewHelper(client RESTClient, mapping *meta.RESTMapping) *Helper {
return &Helper{ return &Helper{
RESTClient: client, RESTClient: client,
Resource: mapping.Resource, Resource: mapping.Resource,
Codec: mapping.Codec, Codec: mapping.Codec,
Versioner: mapping.MetadataAccessor, Versioner: mapping.MetadataAccessor,
NamespaceScoped: mapping.Scope.Name() == meta.RESTScopeNameNamespace, NamespaceScoped: mapping.Scope.Name() == meta.RESTScopeNameNamespace,
} }
} }
...@@ -133,6 +133,15 @@ func (m *Helper) Create(namespace string, modify bool, data []byte) (runtime.Obj ...@@ -133,6 +133,15 @@ func (m *Helper) Create(namespace string, modify bool, data []byte) (runtime.Obj
func (m *Helper) createResource(c RESTClient, resource, namespace string, data []byte) (runtime.Object, error) { func (m *Helper) createResource(c RESTClient, resource, namespace string, data []byte) (runtime.Object, error) {
return c.Post().NamespaceIfScoped(namespace, m.NamespaceScoped).Resource(resource).Body(data).Do().Get() return c.Post().NamespaceIfScoped(namespace, m.NamespaceScoped).Resource(resource).Body(data).Do().Get()
} }
func (m *Helper) Patch(namespace, name string, pt api.PatchType, data []byte) (runtime.Object, error) {
return m.RESTClient.Patch(pt).
NamespaceIfScoped(namespace, m.NamespaceScoped).
Resource(m.Resource).
Name(name).
Body(data).
Do().
Get()
}
func (m *Helper) Update(namespace, name string, overwrite bool, data []byte) (runtime.Object, error) { func (m *Helper) Update(namespace, name string, overwrite bool, data []byte) (runtime.Object, error) {
c := m.RESTClient c := m.RESTClient
......
...@@ -17,6 +17,7 @@ limitations under the License. ...@@ -17,6 +17,7 @@ limitations under the License.
package resource package resource
import ( import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
) )
...@@ -26,6 +27,7 @@ import ( ...@@ -26,6 +27,7 @@ import (
type RESTClient interface { type RESTClient interface {
Get() *client.Request Get() *client.Request
Post() *client.Request Post() *client.Request
Patch(api.PatchType) *client.Request
Delete() *client.Request Delete() *client.Request
Put() *client.Request Put() *client.Request
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment