Skip to content

Commit f3406bf

Browse files
author
Kubernetes Submit Queue
authored
Merge pull request #42256 from shiywang/edit
Automatic merge from submit-queue (batch tested with PRs 42256, 46479, 45436, 46440, 46417) Add `kubectl apply edit-last-applied` subcommand third command of kubernetes/community#287 Fixes #44905 @pwittrock @adohe @ymqytw @kubernetes/sig-cli-feature-requests could you guys have an early review ? cause some of feature I'm not sure about, will add unit tests if you think it's ok.
2 parents 9f3a3e3 + 4597658 commit f3406bf

37 files changed

+881
-34
lines changed

Diff for: docs/.generated_docs

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ docs/man/man1/kube-scheduler.1
1313
docs/man/man1/kubectl-annotate.1
1414
docs/man/man1/kubectl-api-versions.1
1515
docs/man/man1/kubectl-apiversions.1
16+
docs/man/man1/kubectl-apply-edit-last-applied.1
1617
docs/man/man1/kubectl-apply-set-last-applied.1
1718
docs/man/man1/kubectl-apply-view-last-applied.1
1819
docs/man/man1/kubectl-apply.1
@@ -111,6 +112,7 @@ docs/user-guide/kubectl/kubectl.md
111112
docs/user-guide/kubectl/kubectl_annotate.md
112113
docs/user-guide/kubectl/kubectl_api-versions.md
113114
docs/user-guide/kubectl/kubectl_apply.md
115+
docs/user-guide/kubectl/kubectl_apply_edit-last-applied.md
114116
docs/user-guide/kubectl/kubectl_apply_set-last-applied.md
115117
docs/user-guide/kubectl/kubectl_apply_view-last-applied.md
116118
docs/user-guide/kubectl/kubectl_attach.md

Diff for: docs/man/man1/kubectl-apply-edit-last-applied.1

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
This file is autogenerated, but we've stopped checking such files into the
2+
repository to reduce the need for rebases. Please run hack/generate-docs.sh to
3+
populate this file.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
This file is autogenerated, but we've stopped checking such files into the
2+
repository to reduce the need for rebases. Please run hack/generate-docs.sh to
3+
populate this file.

Diff for: pkg/kubectl/cmd/BUILD

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ go_library(
1212
"annotate.go",
1313
"apiversions.go",
1414
"apply.go",
15+
"apply_edit_last_applied.go",
1516
"apply_set_last_applied.go",
1617
"apply_view_last_applied.go",
1718
"attach.go",

Diff for: pkg/kubectl/cmd/apply.go

+1
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ func NewCmdApply(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
131131
// apply subcommands
132132
cmd.AddCommand(NewCmdApplyViewLastApplied(f, out, errOut))
133133
cmd.AddCommand(NewCmdApplySetLastApplied(f, out, errOut))
134+
cmd.AddCommand(NewCmdApplyEditLastApplied(f, out, errOut))
134135

135136
return cmd
136137
}

Diff for: pkg/kubectl/cmd/apply_edit_last_applied.go

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
Copyright 2017 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cmd
18+
19+
import (
20+
"io"
21+
gruntime "runtime"
22+
23+
"github.com/spf13/cobra"
24+
25+
"k8s.io/kubernetes/pkg/kubectl"
26+
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
27+
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
28+
"k8s.io/kubernetes/pkg/kubectl/cmd/util/editor"
29+
"k8s.io/kubernetes/pkg/printers"
30+
)
31+
32+
var (
33+
applyEditLastAppliedLong = templates.LongDesc(`
34+
Edit the latest last-applied-configuration annotations of resources from the default editor.
35+
36+
The edit-last-applied command allows you to directly edit any API resource you can retrieve via the
37+
command line tools. It will open the editor defined by your KUBE_EDITOR, or EDITOR
38+
environment variables, or fall back to 'vi' for Linux or 'notepad' for Windows.
39+
You can edit multiple objects, although changes are applied one at a time. The command
40+
accepts filenames as well as command line arguments, although the files you point to must
41+
be previously saved versions of resources.
42+
43+
The default format is YAML. To edit in JSON, specify "-o json".
44+
45+
The flag --windows-line-endings can be used to force Windows line endings,
46+
otherwise the default for your operating system will be used.
47+
48+
In the event an error occurs while updating, a temporary file will be created on disk
49+
that contains your unapplied changes. The most common error when updating a resource
50+
is another editor changing the resource on the server. When this occurs, you will have
51+
to apply your changes to the newer version of the resource, or update your temporary
52+
saved copy to include the latest resource version.`)
53+
54+
applyEditLastAppliedExample = templates.Examples(`
55+
# Edit the last-applied-configuration annotations by type/name in YAML.
56+
kubectl apply edit-last-applied deployment/nginx
57+
58+
# Edit the last-applied-configuration annotations by file in JSON.
59+
kubectl apply edit-last-applied -f deploy.yaml -o json`)
60+
)
61+
62+
func NewCmdApplyEditLastApplied(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
63+
options := &editor.EditOptions{
64+
EditMode: editor.ApplyEditMode,
65+
}
66+
67+
// retrieve a list of handled resources from printer as valid args
68+
validArgs, argAliases := []string{}, []string{}
69+
p, err := f.Printer(nil, printers.PrintOptions{
70+
ColumnLabels: []string{},
71+
})
72+
cmdutil.CheckErr(err)
73+
if p != nil {
74+
validArgs = p.HandledResources()
75+
argAliases = kubectl.ResourceAliases(validArgs)
76+
}
77+
78+
cmd := &cobra.Command{
79+
Use: "edit-last-applied (RESOURCE/NAME | -f FILENAME)",
80+
Short: "Edit latest last-applied-configuration annotations of a resource/object",
81+
Long: applyEditLastAppliedLong,
82+
Example: applyEditLastAppliedExample,
83+
Run: func(cmd *cobra.Command, args []string) {
84+
options.ChangeCause = f.Command(cmd, false)
85+
if err := options.Complete(f, out, errOut, args); err != nil {
86+
cmdutil.CheckErr(err)
87+
}
88+
if err := options.Run(); err != nil {
89+
cmdutil.CheckErr(err)
90+
}
91+
},
92+
ValidArgs: validArgs,
93+
ArgAliases: argAliases,
94+
}
95+
96+
usage := "to use to edit the resource"
97+
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
98+
cmd.Flags().StringVarP(&options.Output, "output", "o", "yaml", "Output format. One of: yaml|json.")
99+
cmd.Flags().BoolVar(&options.WindowsLineEndings, "windows-line-endings", gruntime.GOOS == "windows", "Use Windows line-endings (default Unix line-endings)")
100+
cmdutil.AddRecordVarFlag(cmd, &options.Record)
101+
102+
return cmd
103+
}

Diff for: pkg/kubectl/cmd/apply_set_last_applied.go

+15-9
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
"k8s.io/kubernetes/pkg/kubectl"
3636
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
3737
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
38+
"k8s.io/kubernetes/pkg/kubectl/cmd/util/editor"
3839
"k8s.io/kubernetes/pkg/kubectl/resource"
3940
"k8s.io/kubernetes/pkg/util/i18n"
4041
)
@@ -52,12 +53,17 @@ type SetLastAppliedOptions struct {
5253
CreateAnnotation bool
5354
Output string
5455
Codec runtime.Encoder
55-
PatchBufferList [][]byte
56+
PatchBufferList []PatchBuffer
5657
Factory cmdutil.Factory
5758
Out io.Writer
5859
ErrOut io.Writer
5960
}
6061

62+
type PatchBuffer struct {
63+
Patch []byte
64+
PatchType types.PatchType
65+
}
66+
6167
var (
6268
applySetLastAppliedLong = templates.LongDesc(i18n.T(`
6369
Set the latest last-applied-configuration annotations by setting it to match the contents of a file.
@@ -137,8 +143,7 @@ func (o *SetLastAppliedOptions) Validate(f cmdutil.Factory, cmd *cobra.Command)
137143
return err
138144
}
139145

140-
var diffBuf, patchBuf []byte
141-
patchBuf, diffBuf, err = o.getPatch(info)
146+
patchBuf, diffBuf, patchType, err := editor.GetApplyPatch(info.VersionedObject, o.Codec)
142147
if err != nil {
143148
return err
144149
}
@@ -161,7 +166,8 @@ func (o *SetLastAppliedOptions) Validate(f cmdutil.Factory, cmd *cobra.Command)
161166

162167
//only add to PatchBufferList when changed
163168
if !bytes.Equal(cmdutil.StripComments(oringalBuf), cmdutil.StripComments(diffBuf)) {
164-
o.PatchBufferList = append(o.PatchBufferList, patchBuf)
169+
p := PatchBuffer{Patch: patchBuf, PatchType: patchType}
170+
o.PatchBufferList = append(o.PatchBufferList, p)
165171
o.InfoList = append(o.InfoList, info)
166172
} else {
167173
fmt.Fprintf(o.Out, "set-last-applied %s: no changes required.\n", info.Name)
@@ -185,7 +191,7 @@ func (o *SetLastAppliedOptions) RunSetLastApplied(f cmdutil.Factory, cmd *cobra.
185191
return err
186192
}
187193
helper := resource.NewHelper(client, mapping)
188-
patchedObj, err := helper.Patch(o.Namespace, info.Name, types.MergePatchType, patch)
194+
patchedObj, err := helper.Patch(o.Namespace, info.Name, patch.PatchType, patch.Patch)
189195
if err != nil {
190196
return err
191197
}
@@ -197,7 +203,7 @@ func (o *SetLastAppliedOptions) RunSetLastApplied(f cmdutil.Factory, cmd *cobra.
197203
cmdutil.PrintSuccess(o.Mapper, o.ShortOutput, o.Out, info.Mapping.Resource, info.Name, o.DryRun, "configured")
198204

199205
} else {
200-
err := o.formatPrinter(o.Output, patch)
206+
err := o.formatPrinter(o.Output, patch.Patch, o.Out)
201207
if err != nil {
202208
return err
203209
}
@@ -207,7 +213,7 @@ func (o *SetLastAppliedOptions) RunSetLastApplied(f cmdutil.Factory, cmd *cobra.
207213
return nil
208214
}
209215

210-
func (o *SetLastAppliedOptions) formatPrinter(output string, buf []byte) error {
216+
func (o *SetLastAppliedOptions) formatPrinter(output string, buf []byte, w io.Writer) error {
211217
yamlOutput, err := yaml.JSONToYAML(buf)
212218
if err != nil {
213219
return err
@@ -219,9 +225,9 @@ func (o *SetLastAppliedOptions) formatPrinter(output string, buf []byte) error {
219225
if err != nil {
220226
return err
221227
}
222-
fmt.Fprintf(o.Out, string(jsonBuffer.Bytes()))
228+
fmt.Fprintf(w, string(jsonBuffer.Bytes()))
223229
case "yaml":
224-
fmt.Fprintf(o.Out, string(yamlOutput))
230+
fmt.Fprintf(w, string(yamlOutput))
225231
}
226232
return nil
227233
}

Diff for: pkg/kubectl/cmd/edit_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,8 @@ func TestEdit(t *testing.T) {
238238
case "create":
239239
cmd = NewCmdCreate(f, buf, errBuf)
240240
cmd.Flags().Set("edit", "true")
241+
case "edit-last-applied":
242+
cmd = NewCmdApplyEditLastApplied(f, buf, errBuf)
241243
default:
242244
t.Errorf("%s: unexpected mode %s", name, testcase.Mode)
243245
continue

Diff for: pkg/kubectl/cmd/testdata/edit/testcase-apply-edit-last-applied-list/0.request

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"kind": "ConfigMap",
3+
"apiVersion": "v1",
4+
"metadata": {
5+
"name": "cm1",
6+
"namespace": "myproject",
7+
"selfLink": "/api/v1/namespaces/myproject/configmaps/cm1",
8+
"uid": "cc08a131-3d6f-11e7-8ef0-c85b76034b7b",
9+
"resourceVersion": "3518",
10+
"creationTimestamp": "2017-05-20T15:20:03Z",
11+
"annotations": {
12+
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"data\":{\"baz\":\"qux\",\"foo\":\"changed-value\",\"new-data\":\"new-value\",\"new-data2\":\"new-value\"},\"kind\":\"ConfigMap\",\"metadata\":{\"annotations\":{},\"name\":\"cm1\",\"namespace\":\"myproject\"}}\n"
13+
}
14+
},
15+
"data": {
16+
"baz": "qux",
17+
"foo": "changed-value",
18+
"new-data": "new-value",
19+
"new-data2": "new-value"
20+
}
21+
}

Diff for: pkg/kubectl/cmd/testdata/edit/testcase-apply-edit-last-applied-list/1.request

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"kind": "Service",
3+
"apiVersion": "v1",
4+
"metadata": {
5+
"name": "svc1",
6+
"namespace": "myproject",
7+
"selfLink": "/api/v1/namespaces/myproject/services/svc1",
8+
"uid": "d8b96f0b-3d6f-11e7-8ef0-c85b76034b7b",
9+
"resourceVersion": "3525",
10+
"creationTimestamp": "2017-05-20T15:20:24Z",
11+
"labels": {
12+
"app": "svc1",
13+
"new-label": "foo"
14+
},
15+
"annotations": {
16+
"kubectl.kubernetes.io/last-applied-configuration": "{\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"svc1\",\"new-label\":\"foo\"},\"name\":\"svc1\",\"namespace\":\"myproject\"},\"spec\":{\"ports\":[{\"name\":\"80\",\"port\":81,\"protocol\":\"TCP\",\"targetPort\":81}],\"sessionAffinity\":\"None\",\"type\":\"ClusterIP\"},\"status\":{\"loadBalancer\":{}}}\n"
17+
}
18+
},
19+
"spec": {
20+
"ports": [
21+
{
22+
"name": "80",
23+
"protocol": "TCP",
24+
"port": 81,
25+
"targetPort": 81
26+
}
27+
],
28+
"clusterIP": "172.30.32.183",
29+
"type": "ClusterIP",
30+
"sessionAffinity": "None"
31+
},
32+
"status": {
33+
"loadBalancer": {}
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Please edit the 'last-applied-configuration' annotations below.
2+
# Lines beginning with a '#' will be ignored, and an empty file will abort the edit.
3+
#
4+
apiVersion: v1
5+
items:
6+
- apiVersion: v1
7+
data:
8+
baz: qux
9+
foo: changed-value
10+
new-data: new-value
11+
new-data2: new-value
12+
new-data3: newivalue
13+
kind: ConfigMap
14+
metadata:
15+
annotations: {}
16+
name: cm1
17+
namespace: myproject
18+
- kind: Service
19+
metadata:
20+
annotations: {}
21+
labels:
22+
app: svc1
23+
new-label: foo
24+
new-label2: foo2
25+
name: svc1
26+
namespace: myproject
27+
spec:
28+
ports:
29+
- name: "80"
30+
port: 82
31+
protocol: TCP
32+
targetPort: 81
33+
sessionAffinity: None
34+
type: ClusterIP
35+
status:
36+
loadBalancer: {}
37+
kind: List
38+
metadata: {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Please edit the 'last-applied-configuration' annotations below.
2+
# Lines beginning with a '#' will be ignored, and an empty file will abort the edit.
3+
#
4+
apiVersion: v1
5+
items:
6+
- apiVersion: v1
7+
data:
8+
baz: qux
9+
foo: changed-value
10+
new-data: new-value
11+
new-data2: new-value
12+
kind: ConfigMap
13+
metadata:
14+
annotations: {}
15+
name: cm1
16+
namespace: myproject
17+
- kind: Service
18+
metadata:
19+
annotations: {}
20+
labels:
21+
app: svc1
22+
new-label: foo
23+
name: svc1
24+
namespace: myproject
25+
spec:
26+
ports:
27+
- name: "80"
28+
port: 81
29+
protocol: TCP
30+
targetPort: 81
31+
sessionAffinity: None
32+
type: ClusterIP
33+
status:
34+
loadBalancer: {}
35+
kind: List
36+
metadata: {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"metadata": {
3+
"annotations": {
4+
"kubectl.kubernetes.io~1last-applied-configuration": "{\"apiVersion\":\"v1\",\"data\":{\"baz\":\"qux\",\"foo\":\"changed-value\",\"new-data\":\"new-value\",\"new-data2\":\"new-value\",\"new-data3\":\"newivalue\"},\"kind\":\"ConfigMap\",\"metadata\":{\"annotations\":{},\"name\":\"cm1\",\"namespace\":\"myproject\"}}\n"
5+
}
6+
}
7+
}

0 commit comments

Comments
 (0)