1
16
17 package rollout
18
19 import (
20 "fmt"
21
22 "github.com/spf13/cobra"
23
24 "k8s.io/apimachinery/pkg/types"
25 utilerrors "k8s.io/apimachinery/pkg/util/errors"
26 "k8s.io/cli-runtime/pkg/genericclioptions"
27 "k8s.io/cli-runtime/pkg/genericiooptions"
28 "k8s.io/cli-runtime/pkg/printers"
29 "k8s.io/cli-runtime/pkg/resource"
30 "k8s.io/kubectl/pkg/cmd/set"
31 cmdutil "k8s.io/kubectl/pkg/cmd/util"
32 "k8s.io/kubectl/pkg/polymorphichelpers"
33 "k8s.io/kubectl/pkg/scheme"
34 "k8s.io/kubectl/pkg/util/completion"
35 "k8s.io/kubectl/pkg/util/i18n"
36 "k8s.io/kubectl/pkg/util/templates"
37 )
38
39
40
41 type ResumeOptions struct {
42 PrintFlags *genericclioptions.PrintFlags
43 ToPrinter func(string) (printers.ResourcePrinter, error)
44
45 Resources []string
46
47 Builder func() *resource.Builder
48 Resumer polymorphichelpers.ObjectResumerFunc
49 Namespace string
50 EnforceNamespace bool
51 LabelSelector string
52
53 resource.FilenameOptions
54 genericiooptions.IOStreams
55
56 fieldManager string
57 }
58
59 var (
60 resumeLong = templates.LongDesc(i18n.T(`
61 Resume a paused resource.
62
63 Paused resources will not be reconciled by a controller. By resuming a
64 resource, we allow it to be reconciled again.
65 Currently only deployments support being resumed.`))
66
67 resumeExample = templates.Examples(`
68 # Resume an already paused deployment
69 kubectl rollout resume deployment/nginx`)
70 )
71
72
73 func NewRolloutResumeOptions(streams genericiooptions.IOStreams) *ResumeOptions {
74 return &ResumeOptions{
75 PrintFlags: genericclioptions.NewPrintFlags("resumed").WithTypeSetter(scheme.Scheme),
76 IOStreams: streams,
77 }
78 }
79
80
81 func NewCmdRolloutResume(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
82 o := NewRolloutResumeOptions(streams)
83
84 validArgs := []string{"deployment"}
85
86 cmd := &cobra.Command{
87 Use: "resume RESOURCE",
88 DisableFlagsInUseLine: true,
89 Short: i18n.T("Resume a paused resource"),
90 Long: resumeLong,
91 Example: resumeExample,
92 ValidArgsFunction: completion.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs),
93 Run: func(cmd *cobra.Command, args []string) {
94 cmdutil.CheckErr(o.Complete(f, cmd, args))
95 cmdutil.CheckErr(o.Validate())
96 cmdutil.CheckErr(o.RunResume())
97 },
98 }
99
100 usage := "identifying the resource to get from a server."
101 cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
102 cmdutil.AddFieldManagerFlagVar(cmd, &o.fieldManager, "kubectl-rollout")
103 cmdutil.AddLabelSelectorFlagVar(cmd, &o.LabelSelector)
104 o.PrintFlags.AddFlags(cmd)
105 return cmd
106 }
107
108
109 func (o *ResumeOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
110 o.Resources = args
111
112 o.Resumer = polymorphichelpers.ObjectResumerFn
113
114 var err error
115 o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
116 if err != nil {
117 return err
118 }
119
120 o.ToPrinter = func(operation string) (printers.ResourcePrinter, error) {
121 o.PrintFlags.NamePrintFlags.Operation = operation
122 return o.PrintFlags.ToPrinter()
123 }
124
125 o.Builder = f.NewBuilder
126
127 return nil
128 }
129
130 func (o *ResumeOptions) Validate() error {
131 if len(o.Resources) == 0 && cmdutil.IsFilenameSliceEmpty(o.Filenames, o.Kustomize) {
132 return fmt.Errorf("required resource not specified")
133 }
134 return nil
135 }
136
137
138 func (o ResumeOptions) RunResume() error {
139 r := o.Builder().
140 WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
141 NamespaceParam(o.Namespace).DefaultNamespace().
142 LabelSelectorParam(o.LabelSelector).
143 FilenameParam(o.EnforceNamespace, &o.FilenameOptions).
144 ResourceTypeOrNameArgs(true, o.Resources...).
145 ContinueOnError().
146 Latest().
147 Flatten().
148 Do()
149 if err := r.Err(); err != nil {
150 return err
151 }
152
153 allErrs := []error{}
154 infos, err := r.Infos()
155 if err != nil {
156
157
158
159
160
161 allErrs = append(allErrs, err)
162 }
163
164 patches := set.CalculatePatches(infos, scheme.DefaultJSONEncoder(), set.PatchFn(o.Resumer))
165
166 if len(patches) == 0 && len(allErrs) == 0 {
167 fmt.Fprintf(o.ErrOut, "No resources found in %s namespace.\n", o.Namespace)
168 return nil
169 }
170
171 for _, patch := range patches {
172 info := patch.Info
173
174 if patch.Err != nil {
175 resourceString := info.Mapping.Resource.Resource
176 if len(info.Mapping.Resource.Group) > 0 {
177 resourceString = resourceString + "." + info.Mapping.Resource.Group
178 }
179 allErrs = append(allErrs, fmt.Errorf("error: %s %q %v", resourceString, info.Name, patch.Err))
180 continue
181 }
182
183 if string(patch.Patch) == "{}" || len(patch.Patch) == 0 {
184 printer, err := o.ToPrinter("already resumed")
185 if err != nil {
186 allErrs = append(allErrs, err)
187 continue
188 }
189 if err = printer.PrintObj(info.Object, o.Out); err != nil {
190 allErrs = append(allErrs, err)
191 }
192 continue
193 }
194
195 obj, err := resource.NewHelper(info.Client, info.Mapping).
196 WithFieldManager(o.fieldManager).
197 Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch, nil)
198 if err != nil {
199 allErrs = append(allErrs, fmt.Errorf("failed to patch: %v", err))
200 continue
201 }
202
203 info.Refresh(obj, true)
204 printer, err := o.ToPrinter("resumed")
205 if err != nil {
206 allErrs = append(allErrs, err)
207 continue
208 }
209 if err = printer.PrintObj(info.Object, o.Out); err != nil {
210 allErrs = append(allErrs, err)
211 }
212 }
213
214 return utilerrors.NewAggregate(allErrs)
215 }
216
View as plain text