@@ -17,12 +17,22 @@ limitations under the License.
17
17
package create
18
18
19
19
import (
20
+ "context"
21
+ "fmt"
22
+ "strings"
23
+
20
24
"github.com/spf13/cobra"
21
25
26
+ corev1 "k8s.io/api/core/v1"
27
+ resourceapi "k8s.io/apimachinery/pkg/api/resource"
28
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29
+ "k8s.io/apimachinery/pkg/runtime"
22
30
"k8s.io/cli-runtime/pkg/genericclioptions"
31
+ resourcecli "k8s.io/cli-runtime/pkg/resource"
32
+ coreclient "k8s.io/client-go/kubernetes/typed/core/v1"
23
33
cmdutil "k8s.io/kubectl/pkg/cmd/util"
24
- "k8s.io/kubectl/pkg/generate "
25
- generateversioned "k8s.io/kubectl/pkg/generate/versioned "
34
+ "k8s.io/kubectl/pkg/scheme "
35
+ "k8s.io/kubectl/pkg/util "
26
36
"k8s.io/kubectl/pkg/util/i18n"
27
37
"k8s.io/kubectl/pkg/util/templates"
28
38
)
@@ -41,14 +51,38 @@ var (
41
51
42
52
// QuotaOpts holds the command-line options for 'create quota' sub command
43
53
type QuotaOpts struct {
44
- CreateSubcommandOptions * CreateSubcommandOptions
54
+ // PrintFlags holds options necessary for obtaining a printer
55
+ PrintFlags * genericclioptions.PrintFlags
56
+ PrintObj func (obj runtime.Object ) error
57
+ // The name of a quota object.
58
+ Name string
59
+ // The hard resource limit string before parsing.
60
+ Hard string
61
+ // The scopes of a quota object before parsing.
62
+ Scopes string
63
+ CreateAnnotation bool
64
+ FieldManager string
65
+ Namespace string
66
+ EnforceNamespace bool
67
+
68
+ Client * coreclient.CoreV1Client
69
+ DryRunStrategy cmdutil.DryRunStrategy
70
+ DryRunVerifier * resourcecli.DryRunVerifier
71
+
72
+ genericclioptions.IOStreams
73
+ }
74
+
75
+ // NewQuotaOpts creates a new *QuotaOpts with sane defaults
76
+ func NewQuotaOpts (ioStreams genericclioptions.IOStreams ) * QuotaOpts {
77
+ return & QuotaOpts {
78
+ PrintFlags : genericclioptions .NewPrintFlags ("created" ).WithTypeSetter (scheme .Scheme ),
79
+ IOStreams : ioStreams ,
80
+ }
45
81
}
46
82
47
83
// NewCmdCreateQuota is a macro command to create a new quota
48
84
func NewCmdCreateQuota (f cmdutil.Factory , ioStreams genericclioptions.IOStreams ) * cobra.Command {
49
- options := & QuotaOpts {
50
- CreateSubcommandOptions : NewCreateSubcommandOptions (ioStreams ),
51
- }
85
+ o := NewQuotaOpts (ioStreams )
52
86
53
87
cmd := & cobra.Command {
54
88
Use : "quota NAME [--hard=key1=value1,key2=value2] [--scopes=Scope1,Scope2] [--dry-run=server|client|none]" ,
@@ -58,45 +92,184 @@ func NewCmdCreateQuota(f cmdutil.Factory, ioStreams genericclioptions.IOStreams)
58
92
Long : quotaLong ,
59
93
Example : quotaExample ,
60
94
Run : func (cmd * cobra.Command , args []string ) {
61
- cmdutil .CheckErr (options .Complete (f , cmd , args ))
62
- cmdutil .CheckErr (options .Run ())
95
+ cmdutil .CheckErr (o .Complete (f , cmd , args ))
96
+ cmdutil .CheckErr (o .Validate ())
97
+ cmdutil .CheckErr (o .Run ())
63
98
},
64
99
}
65
100
66
- options . CreateSubcommandOptions .PrintFlags .AddFlags (cmd )
101
+ o .PrintFlags .AddFlags (cmd )
67
102
68
103
cmdutil .AddApplyAnnotationFlags (cmd )
69
104
cmdutil .AddValidateFlags (cmd )
70
- cmdutil .AddGeneratorFlags (cmd , generateversioned . ResourceQuotaV1GeneratorName )
71
- cmd .Flags ().String ( "hard" , "" , i18n .T ("A comma-delimited set of resource=quantity pairs that define a hard limit." ))
72
- cmd .Flags ().String ( "scopes" , "" , i18n .T ("A comma-delimited set of quota scopes that must all match each object tracked by the quota." ))
73
- cmdutil .AddFieldManagerFlagVar (cmd , & options . CreateSubcommandOptions .FieldManager , "kubectl-create" )
105
+ cmdutil .AddDryRunFlag (cmd )
106
+ cmd .Flags ().StringVar ( & o . Hard , "hard" , o . Hard , i18n .T ("A comma-delimited set of resource=quantity pairs that define a hard limit." ))
107
+ cmd .Flags ().StringVar ( & o . Scopes , "scopes" , o . Scopes , i18n .T ("A comma-delimited set of quota scopes that must all match each object tracked by the quota." ))
108
+ cmdutil .AddFieldManagerFlagVar (cmd , & o .FieldManager , "kubectl-create" )
74
109
return cmd
75
110
}
76
111
77
112
// Complete completes all the required options
78
113
func (o * QuotaOpts ) Complete (f cmdutil.Factory , cmd * cobra.Command , args []string ) error {
79
- name , err := NameFromCommandArgs (cmd , args )
114
+ var err error
115
+ o .Name , err = NameFromCommandArgs (cmd , args )
80
116
if err != nil {
81
117
return err
82
118
}
83
119
84
- var generator generate.StructuredGenerator
85
- switch generatorName := cmdutil .GetFlagString (cmd , "generator" ); generatorName {
86
- case generateversioned .ResourceQuotaV1GeneratorName :
87
- generator = & generateversioned.ResourceQuotaGeneratorV1 {
88
- Name : name ,
89
- Hard : cmdutil .GetFlagString (cmd , "hard" ),
90
- Scopes : cmdutil .GetFlagString (cmd , "scopes" ),
91
- }
92
- default :
93
- return errUnsupportedGenerator (cmd , generatorName )
120
+ restConfig , err := f .ToRESTConfig ()
121
+ if err != nil {
122
+ return err
123
+ }
124
+ o .Client , err = coreclient .NewForConfig (restConfig )
125
+ if err != nil {
126
+ return err
94
127
}
95
128
96
- return o .CreateSubcommandOptions .Complete (f , cmd , args , generator )
129
+ o .CreateAnnotation = cmdutil .GetFlagBool (cmd , cmdutil .ApplyAnnotationsFlag )
130
+
131
+ o .DryRunStrategy , err = cmdutil .GetDryRunStrategy (cmd )
132
+ if err != nil {
133
+ return err
134
+ }
135
+ dynamicClient , err := f .DynamicClient ()
136
+ if err != nil {
137
+ return err
138
+ }
139
+ discoveryClient , err := f .ToDiscoveryClient ()
140
+ if err != nil {
141
+ return err
142
+ }
143
+ o .DryRunVerifier = resourcecli .NewDryRunVerifier (dynamicClient , discoveryClient )
144
+
145
+ o .Namespace , o .EnforceNamespace , err = f .ToRawKubeConfigLoader ().Namespace ()
146
+ if err != nil {
147
+ return err
148
+ }
149
+
150
+ cmdutil .PrintFlagsWithDryRunStrategy (o .PrintFlags , o .DryRunStrategy )
151
+
152
+ printer , err := o .PrintFlags .ToPrinter ()
153
+ if err != nil {
154
+ return err
155
+ }
156
+
157
+ o .PrintObj = func (obj runtime.Object ) error {
158
+ return printer .PrintObj (obj , o .Out )
159
+ }
160
+
161
+ return nil
97
162
}
98
163
99
- // Run calls the CreateSubcommandOptions.Run in QuotaOpts instance
164
+ // Validate checks to the QuotaOpts to see if there is sufficient information run the command.
165
+ func (o * QuotaOpts ) Validate () error {
166
+ if len (o .Name ) == 0 {
167
+ return fmt .Errorf ("name must be specified" )
168
+ }
169
+ return nil
170
+ }
171
+
172
+ // Run does the work
100
173
func (o * QuotaOpts ) Run () error {
101
- return o .CreateSubcommandOptions .Run ()
174
+ resourceQuota , err := o .createQuota ()
175
+ if err != nil {
176
+ return err
177
+ }
178
+
179
+ if err := util .CreateOrUpdateAnnotation (o .CreateAnnotation , resourceQuota , scheme .DefaultJSONEncoder ()); err != nil {
180
+ return err
181
+ }
182
+
183
+ if o .DryRunStrategy != cmdutil .DryRunClient {
184
+ createOptions := metav1.CreateOptions {}
185
+ if o .FieldManager != "" {
186
+ createOptions .FieldManager = o .FieldManager
187
+ }
188
+ if o .DryRunStrategy == cmdutil .DryRunServer {
189
+ if err := o .DryRunVerifier .HasSupport (resourceQuota .GroupVersionKind ()); err != nil {
190
+ return err
191
+ }
192
+ createOptions .DryRun = []string {metav1 .DryRunAll }
193
+ }
194
+ resourceQuota , err = o .Client .ResourceQuotas (o .Namespace ).Create (context .TODO (), resourceQuota , createOptions )
195
+ if err != nil {
196
+ return fmt .Errorf ("failed to create quota: %v" , err )
197
+ }
198
+ }
199
+ return o .PrintObj (resourceQuota )
200
+ }
201
+
202
+ func (o * QuotaOpts ) createQuota () (* corev1.ResourceQuota , error ) {
203
+ namespace := ""
204
+ if o .EnforceNamespace {
205
+ namespace = o .Namespace
206
+ }
207
+ fmt .Println (corev1 .SchemeGroupVersion .String ())
208
+ resourceQuota := & corev1.ResourceQuota {
209
+ TypeMeta : metav1.TypeMeta {APIVersion : corev1 .SchemeGroupVersion .String (), Kind : "ResourceQuota" },
210
+ ObjectMeta : metav1.ObjectMeta {
211
+ Name : o .Name ,
212
+ Namespace : namespace ,
213
+ },
214
+ }
215
+
216
+ resourceList , err := populateResourceListV1 (o .Hard )
217
+ if err != nil {
218
+ return nil , err
219
+ }
220
+
221
+ scopes , err := parseScopes (o .Scopes )
222
+ if err != nil {
223
+ return nil , err
224
+ }
225
+
226
+ resourceQuota .Spec .Hard = resourceList
227
+ resourceQuota .Spec .Scopes = scopes
228
+
229
+ return resourceQuota , nil
230
+ }
231
+
232
+ // populateResourceListV1 takes strings of form <resourceName1>=<value1>,<resourceName1>=<value2>
233
+ // and returns ResourceList.
234
+ func populateResourceListV1 (spec string ) (corev1.ResourceList , error ) {
235
+ // empty input gets a nil response to preserve generator test expected behaviors
236
+ if spec == "" {
237
+ return nil , nil
238
+ }
239
+
240
+ result := corev1.ResourceList {}
241
+ resourceStatements := strings .Split (spec , "," )
242
+ for _ , resourceStatement := range resourceStatements {
243
+ parts := strings .Split (resourceStatement , "=" )
244
+ if len (parts ) != 2 {
245
+ return nil , fmt .Errorf ("Invalid argument syntax %v, expected <resource>=<value>" , resourceStatement )
246
+ }
247
+ resourceName := corev1 .ResourceName (parts [0 ])
248
+ resourceQuantity , err := resourceapi .ParseQuantity (parts [1 ])
249
+ if err != nil {
250
+ return nil , err
251
+ }
252
+ result [resourceName ] = resourceQuantity
253
+ }
254
+ return result , nil
255
+ }
256
+
257
+ func parseScopes (spec string ) ([]corev1.ResourceQuotaScope , error ) {
258
+ // empty input gets a nil response to preserve test expected behaviors
259
+ if spec == "" {
260
+ return nil , nil
261
+ }
262
+
263
+ scopes := strings .Split (spec , "," )
264
+ result := make ([]corev1.ResourceQuotaScope , 0 , len (scopes ))
265
+ for _ , scope := range scopes {
266
+ // intentionally do not verify the scope against the valid scope list. This is done by the apiserver anyway.
267
+
268
+ if scope == "" {
269
+ return nil , fmt .Errorf ("invalid resource quota scope \" \" " )
270
+ }
271
+
272
+ result = append (result , corev1 .ResourceQuotaScope (scope ))
273
+ }
274
+ return result , nil
102
275
}
0 commit comments