mirror of https://github.com/k3s-io/k3s
Merge pull request #46091 from xilabao/new-output-in-edit
Automatic merge from submit-queue (batch tested with PRs 46091, 48280) allow output patch string in edit command **What this PR does / why we need it**: allow user to get the patch from edit command if user is not familiar with the patch format. ``` # ./cluster/kubectl.sh create role a --verb=get,list --resource=no role "a" created # ./cluster/kubectl.sh edit role a --output-patch=true Patch: {"rules":[{"apiGroups":[""],"resources":["nodes"],"verbs":["get","list","delete"]}]} role "a" edited # ./cluster/kubectl.sh create role b --verb=get,list --resource=no role "b" created # ./cluster/kubectl.sh patch role b -p '{"rules":[{"apiGroups":[""],"resources":["nodes"],"verbs":["get","list","delete"]}]}' role "b" patched ``` **Which issue this PR fixes**: fixes #47173 **Special notes for your reviewer**: **Release note**: ```release-note Could get the patch from kubectl edit command ```pull/6/head
commit
8ce6378512
|
@ -797,7 +797,7 @@ __EOF__
|
|||
chmod +x /tmp/tmp-editor.sh
|
||||
# Pre-condition: valid-pod POD has image nginx
|
||||
kube::test::get_object_assert pods "{{range.items}}{{$image_field}}:{{end}}" 'nginx:'
|
||||
EDITOR=/tmp/tmp-editor.sh kubectl edit "${kube_flags[@]}" pods/valid-pod
|
||||
[[ "$(EDITOR=/tmp/tmp-editor.sh kubectl edit "${kube_flags[@]}" pods/valid-pod --output-patch=true | grep Patch:)" ]]
|
||||
# Post-condition: valid-pod POD has image gcr.io/google_containers/serve_hostname
|
||||
kube::test::get_object_assert pods "{{range.items}}{{$image_field}}:{{end}}" 'gcr.io/google_containers/serve_hostname:'
|
||||
# cleaning
|
||||
|
|
|
@ -526,6 +526,7 @@ output-base
|
|||
output-directory
|
||||
output-file-base
|
||||
output-package
|
||||
output-patch
|
||||
output-print-type
|
||||
output-version
|
||||
out-version
|
||||
|
|
|
@ -107,6 +107,7 @@ func NewCmdEdit(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
|
|||
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
|
||||
cmdutil.AddValidateOptionFlags(cmd, &options.ValidateOptions)
|
||||
cmd.Flags().StringVarP(&options.Output, "output", "o", "yaml", "Output format. One of: yaml|json.")
|
||||
cmd.Flags().BoolVarP(&options.OutputPatch, "output-patch", "", false, "Output the patch if the resource is edited.")
|
||||
|
||||
cmd.Flags().BoolVar(&options.WindowsLineEndings, "windows-line-endings", runtime.GOOS == "windows",
|
||||
"Defaults to the line ending native to your platform.")
|
||||
|
|
|
@ -49,6 +49,7 @@ type EditTestCase struct {
|
|||
Args []string `yaml:"args"`
|
||||
Filename string `yaml:"filename"`
|
||||
Output string `yaml:"outputFormat"`
|
||||
OutputPatch string `yaml:"outputPatch"`
|
||||
SaveConfig string `yaml:"saveConfig"`
|
||||
Namespace string `yaml:"namespace"`
|
||||
ExpectedStdout []string `yaml:"expectedStdout"`
|
||||
|
@ -250,6 +251,9 @@ func TestEdit(t *testing.T) {
|
|||
if len(testcase.Output) > 0 {
|
||||
cmd.Flags().Set("output", testcase.Output)
|
||||
}
|
||||
if len(testcase.OutputPatch) > 0 {
|
||||
cmd.Flags().Set("output-patch", testcase.OutputPatch)
|
||||
}
|
||||
if len(testcase.SaveConfig) > 0 {
|
||||
cmd.Flags().Set("save-config", testcase.SaveConfig)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Service",
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "{\"kind\":\"Service\",\"apiVersion\":\"v1\",\"metadata\":{\"name\":\"svc1\",\"creationTimestamp\":null,\"labels\":{\"app\":\"svc1\"}},\"spec\":{\"ports\":[{\"name\":\"80\",\"protocol\":\"TCP\",\"port\":80,\"targetPort\":80}],\"selector\":{\"app\":\"svc1\"},\"type\":\"ClusterIP\"},\"status\":{\"loadBalancer\":{}}}\n"
|
||||
},
|
||||
"creationTimestamp": "2017-02-27T19:40:53Z",
|
||||
"labels": {
|
||||
"app": "svc1"
|
||||
},
|
||||
"name": "svc1",
|
||||
"namespace": "edit-test",
|
||||
"resourceVersion": "670",
|
||||
"selfLink": "/api/v1/namespaces/edit-test/services/svc1",
|
||||
"uid": "a6c11186-fd24-11e6-b53c-480fcf4a5275"
|
||||
},
|
||||
"spec": {
|
||||
"clusterIP": "10.0.0.204",
|
||||
"ports": [
|
||||
{
|
||||
"name": "80",
|
||||
"port": 80,
|
||||
"protocol": "TCP",
|
||||
"targetPort": 80
|
||||
}
|
||||
],
|
||||
"selector": {
|
||||
"app": "svc1"
|
||||
},
|
||||
"sessionAffinity": "None",
|
||||
"type": "ClusterIP"
|
||||
},
|
||||
"status": {
|
||||
"loadBalancer": {}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# Please edit the object below. Lines beginning with a '#' will be ignored,
|
||||
# and an empty file will abort the edit. If an error occurs while saving this file will be
|
||||
# reopened with the relevant failures.
|
||||
#
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
annotations:
|
||||
kubectl.kubernetes.io/last-applied-configuration: |
|
||||
{"kind":"Service","apiVersion":"v1","metadata":{"name":"svc1","creationTimestamp":null,"labels":{"app":"svc1"}},"spec":{"ports":[{"name":"80","protocol":"TCP","port":80,"targetPort":80}],"selector":{"app":"svc1"},"type":"ClusterIP"},"status":{"loadBalancer":{}}}
|
||||
creationTimestamp: 2017-02-27T19:40:53Z
|
||||
labels:
|
||||
app: svc1
|
||||
new-label: new-value
|
||||
name: svc1
|
||||
namespace: edit-test
|
||||
resourceVersion: "670"
|
||||
selfLink: /api/v1/namespaces/edit-test/services/svc1
|
||||
uid: a6c11186-fd24-11e6-b53c-480fcf4a5275
|
||||
spec:
|
||||
clusterIP: 10.0.0.204
|
||||
ports:
|
||||
- name: "80"
|
||||
port: 80
|
||||
protocol: TCP
|
||||
targetPort: 80
|
||||
selector:
|
||||
app: svc1
|
||||
sessionAffinity: None
|
||||
type: ClusterIP
|
||||
status:
|
||||
loadBalancer: {}
|
|
@ -0,0 +1,31 @@
|
|||
# Please edit the object below. Lines beginning with a '#' will be ignored,
|
||||
# and an empty file will abort the edit. If an error occurs while saving this file will be
|
||||
# reopened with the relevant failures.
|
||||
#
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
annotations:
|
||||
kubectl.kubernetes.io/last-applied-configuration: |
|
||||
{"kind":"Service","apiVersion":"v1","metadata":{"name":"svc1","creationTimestamp":null,"labels":{"app":"svc1"}},"spec":{"ports":[{"name":"80","protocol":"TCP","port":80,"targetPort":80}],"selector":{"app":"svc1"},"type":"ClusterIP"},"status":{"loadBalancer":{}}}
|
||||
creationTimestamp: 2017-02-27T19:40:53Z
|
||||
labels:
|
||||
app: svc1
|
||||
name: svc1
|
||||
namespace: edit-test
|
||||
resourceVersion: "670"
|
||||
selfLink: /api/v1/namespaces/edit-test/services/svc1
|
||||
uid: a6c11186-fd24-11e6-b53c-480fcf4a5275
|
||||
spec:
|
||||
clusterIP: 10.0.0.204
|
||||
ports:
|
||||
- name: "80"
|
||||
port: 80
|
||||
protocol: TCP
|
||||
targetPort: 80
|
||||
selector:
|
||||
app: svc1
|
||||
sessionAffinity: None
|
||||
type: ClusterIP
|
||||
status:
|
||||
loadBalancer: {}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"creationTimestamp\":\"2017-02-27T19:40:53Z\",\"labels\":{\"app\":\"svc1\",\"new-label\":\"new-value\"},\"name\":\"svc1\",\"namespace\":\"edit-test\",\"resourceVersion\":\"670\",\"selfLink\":\"/api/v1/namespaces/edit-test/services/svc1\",\"uid\":\"a6c11186-fd24-11e6-b53c-480fcf4a5275\"},\"spec\":{\"clusterIP\":\"10.0.0.204\",\"ports\":[{\"name\":\"80\",\"port\":80,\"protocol\":\"TCP\",\"targetPort\":80}],\"selector\":{\"app\":\"svc1\"},\"sessionAffinity\":\"None\",\"type\":\"ClusterIP\"},\"status\":{\"loadBalancer\":{}}}\n"
|
||||
},
|
||||
"labels": {
|
||||
"new-label": "new-value"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"kind": "Service",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"creationTimestamp\":\"2017-02-27T19:40:53Z\",\"labels\":{\"app\":\"svc1\",\"new-label\":\"new-value\"},\"name\":\"svc1\",\"namespace\":\"edit-test\",\"resourceVersion\":\"670\",\"selfLink\":\"/api/v1/namespaces/edit-test/services/svc1\",\"uid\":\"a6c11186-fd24-11e6-b53c-480fcf4a5275\"},\"spec\":{\"clusterIP\":\"10.0.0.204\",\"ports\":[{\"name\":\"80\",\"port\":80,\"protocol\":\"TCP\",\"targetPort\":80}],\"selector\":{\"app\":\"svc1\"},\"sessionAffinity\":\"None\",\"type\":\"ClusterIP\"},\"status\":{\"loadBalancer\":{}}}\n"
|
||||
},
|
||||
"name": "svc1",
|
||||
"namespace": "edit-test",
|
||||
"selfLink": "/api/v1/namespaces/edit-test/services/svc1",
|
||||
"uid": "a6c11186-fd24-11e6-b53c-480fcf4a5275",
|
||||
"resourceVersion":"1045",
|
||||
"creationTimestamp":"2017-02-27T19:40:53Z",
|
||||
"labels": {
|
||||
"app": "svc1",
|
||||
"new-label": "new-value"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"clusterIP": "10.0.0.204",
|
||||
"ports": [
|
||||
{
|
||||
"name": "80",
|
||||
"port": 80,
|
||||
"protocol": "TCP",
|
||||
"targetPort": 80
|
||||
}
|
||||
],
|
||||
"selector": {
|
||||
"app": "svc1"
|
||||
},
|
||||
"sessionAffinity": "None",
|
||||
"type": "ClusterIP"
|
||||
},
|
||||
"status": {
|
||||
"loadBalancer": {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
# kubectl create namespace edit-test
|
||||
# kubectl create service clusterip svc1 --tcp 80 --namespace=edit-test --save-config
|
||||
# kubectl edit service svc1 --namespace=edit-test --save-config=true --output-patch=true
|
||||
description: edit with flag --output-patch=true should output the patch
|
||||
mode: edit
|
||||
args:
|
||||
- service
|
||||
- svc1
|
||||
saveConfig: "true"
|
||||
outputPatch: "true"
|
||||
namespace: edit-test
|
||||
expectedStdout:
|
||||
- 'Patch: {"metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"creationTimestamp\":\"2017-02-27T19:40:53Z\",\"labels\":{\"app\":\"svc1\",\"new-label\":\"new-value\"},\"name\":\"svc1\",\"namespace\":\"edit-test\",\"resourceVersion\":\"670\",\"selfLink\":\"/api/v1/namespaces/edit-test/services/svc1\",\"uid\":\"a6c11186-fd24-11e6-b53c-480fcf4a5275\"},\"spec\":{\"clusterIP\":\"10.0.0.204\",\"ports\":[{\"name\":\"80\",\"port\":80,\"protocol\":\"TCP\",\"targetPort\":80}],\"selector\":{\"app\":\"svc1\"},\"sessionAffinity\":\"None\",\"type\":\"ClusterIP\"},\"status\":{\"loadBalancer\":{}}}\n"},"labels":{"new-label":"new-value"}}}'
|
||||
- service "svc1" edited
|
||||
expectedExitCode: 0
|
||||
steps:
|
||||
- type: request
|
||||
expectedMethod: GET
|
||||
expectedPath: /api/v1/namespaces/edit-test/services/svc1
|
||||
expectedInput: 0.request
|
||||
resultingStatusCode: 200
|
||||
resultingOutput: 0.response
|
||||
- type: edit
|
||||
expectedInput: 1.original
|
||||
resultingOutput: 1.edited
|
||||
- type: request
|
||||
expectedMethod: PATCH
|
||||
expectedPath: /api/v1/namespaces/edit-test/services/svc1
|
||||
expectedContentType: application/strategic-merge-patch+json
|
||||
expectedInput: 2.request
|
||||
resultingStatusCode: 200
|
||||
resultingOutput: 2.response
|
|
@ -53,6 +53,7 @@ type EditOptions struct {
|
|||
resource.FilenameOptions
|
||||
|
||||
Output string
|
||||
OutputPatch bool
|
||||
WindowsLineEndings bool
|
||||
|
||||
cmdutil.ValidateOptions
|
||||
|
@ -96,6 +97,10 @@ func (o *EditOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args []
|
|||
}
|
||||
o.editPrinterOptions = getPrinter(o.Output)
|
||||
|
||||
if o.OutputPatch && o.EditMode != NormalEditMode {
|
||||
return fmt.Errorf("the edit mode doesn't support output the patch")
|
||||
}
|
||||
|
||||
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -577,6 +582,10 @@ func (o *EditOptions) visitToPatch(
|
|||
}
|
||||
}
|
||||
|
||||
if o.OutputPatch {
|
||||
fmt.Fprintf(o.Out, "Patch: %s\n", string(patch))
|
||||
}
|
||||
|
||||
patched, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, patchType, patch)
|
||||
if err != nil {
|
||||
fmt.Fprintln(o.ErrOut, results.addError(err, info))
|
||||
|
|
Loading…
Reference in New Issue