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