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 RestartOptions struct {
42 PrintFlags *genericclioptions.PrintFlags
43 ToPrinter func(string) (printers.ResourcePrinter, error)
44
45 Resources []string
46
47 Builder func() *resource.Builder
48 Restarter polymorphichelpers.ObjectRestarterFunc
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 restartLong = templates.LongDesc(i18n.T(`
61 Restart a resource.
62
63 Resource rollout will be restarted.`))
64
65 restartExample = templates.Examples(`
66 # Restart all deployments in test-namespace namespace
67 kubectl rollout restart deployment -n test-namespace
68
69 # Restart a deployment
70 kubectl rollout restart deployment/nginx
71
72 # Restart a daemon set
73 kubectl rollout restart daemonset/abc
74
75 # Restart deployments with the app=nginx label
76 kubectl rollout restart deployment --selector=app=nginx`)
77 )
78
79
80 func NewRolloutRestartOptions(streams genericiooptions.IOStreams) *RestartOptions {
81 return &RestartOptions{
82 PrintFlags: genericclioptions.NewPrintFlags("restarted").WithTypeSetter(scheme.Scheme),
83 IOStreams: streams,
84 }
85 }
86
87
88 func NewCmdRolloutRestart(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
89 o := NewRolloutRestartOptions(streams)
90
91 validArgs := []string{"deployment", "daemonset", "statefulset"}
92
93 cmd := &cobra.Command{
94 Use: "restart RESOURCE",
95 DisableFlagsInUseLine: true,
96 Short: i18n.T("Restart a resource"),
97 Long: restartLong,
98 Example: restartExample,
99 ValidArgsFunction: completion.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs),
100 Run: func(cmd *cobra.Command, args []string) {
101 cmdutil.CheckErr(o.Complete(f, cmd, args))
102 cmdutil.CheckErr(o.Validate())
103 cmdutil.CheckErr(o.RunRestart())
104 },
105 }
106
107 usage := "identifying the resource to get from a server."
108 cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
109 cmdutil.AddFieldManagerFlagVar(cmd, &o.fieldManager, "kubectl-rollout")
110 cmdutil.AddLabelSelectorFlagVar(cmd, &o.LabelSelector)
111 o.PrintFlags.AddFlags(cmd)
112 return cmd
113 }
114
115
116 func (o *RestartOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
117 o.Resources = args
118
119 o.Restarter = polymorphichelpers.ObjectRestarterFn
120
121 var err error
122 o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
123 if err != nil {
124 return err
125 }
126
127 o.ToPrinter = func(operation string) (printers.ResourcePrinter, error) {
128 o.PrintFlags.NamePrintFlags.Operation = operation
129 return o.PrintFlags.ToPrinter()
130 }
131
132 o.Builder = f.NewBuilder
133
134 return nil
135 }
136
137 func (o *RestartOptions) Validate() error {
138 if len(o.Resources) == 0 && cmdutil.IsFilenameSliceEmpty(o.Filenames, o.Kustomize) {
139 return fmt.Errorf("required resource not specified")
140 }
141 return nil
142 }
143
144
145 func (o RestartOptions) RunRestart() error {
146 r := o.Builder().
147 WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
148 NamespaceParam(o.Namespace).DefaultNamespace().
149 FilenameParam(o.EnforceNamespace, &o.FilenameOptions).
150 LabelSelectorParam(o.LabelSelector).
151 ResourceTypeOrNameArgs(true, o.Resources...).
152 ContinueOnError().
153 Latest().
154 Flatten().
155 Do()
156 if err := r.Err(); err != nil {
157 return err
158 }
159
160 allErrs := []error{}
161 infos, err := r.Infos()
162 if err != nil {
163
164
165
166
167
168 allErrs = append(allErrs, err)
169 }
170
171 patches := set.CalculatePatches(infos, scheme.DefaultJSONEncoder(), set.PatchFn(o.Restarter))
172
173 if len(patches) == 0 && len(allErrs) == 0 {
174 fmt.Fprintf(o.ErrOut, "No resources found in %s namespace.\n", o.Namespace)
175 return nil
176 }
177
178 for _, patch := range patches {
179 info := patch.Info
180
181 if patch.Err != nil {
182 resourceString := info.Mapping.Resource.Resource
183 if len(info.Mapping.Resource.Group) > 0 {
184 resourceString = resourceString + "." + info.Mapping.Resource.Group
185 }
186 allErrs = append(allErrs, fmt.Errorf("error: %s %q %v", resourceString, info.Name, patch.Err))
187 continue
188 }
189
190 if string(patch.Patch) == "{}" || len(patch.Patch) == 0 {
191 allErrs = append(allErrs, fmt.Errorf("failed to create patch for %v: if restart has already been triggered within the past second, please wait before attempting to trigger another", info.Name))
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("restarted")
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