1
16
17 package create
18
19 import (
20 "context"
21 "fmt"
22 "strings"
23
24 "github.com/spf13/cobra"
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"
30 "k8s.io/cli-runtime/pkg/genericclioptions"
31 "k8s.io/cli-runtime/pkg/genericiooptions"
32 coreclient "k8s.io/client-go/kubernetes/typed/core/v1"
33 cmdutil "k8s.io/kubectl/pkg/cmd/util"
34 "k8s.io/kubectl/pkg/scheme"
35 "k8s.io/kubectl/pkg/util"
36 "k8s.io/kubectl/pkg/util/i18n"
37 "k8s.io/kubectl/pkg/util/templates"
38 )
39
40 var (
41 quotaLong = templates.LongDesc(i18n.T(`
42 Create a resource quota with the specified name, hard limits, and optional scopes.`))
43
44 quotaExample = templates.Examples(i18n.T(`
45 # Create a new resource quota named my-quota
46 kubectl create quota my-quota --hard=cpu=1,memory=1G,pods=2,services=3,replicationcontrollers=2,resourcequotas=1,secrets=5,persistentvolumeclaims=10
47
48 # Create a new resource quota named best-effort
49 kubectl create quota best-effort --hard=pods=100 --scopes=BestEffort`))
50 )
51
52
53 type QuotaOpts struct {
54
55 PrintFlags *genericclioptions.PrintFlags
56 PrintObj func(obj runtime.Object) error
57
58 Name string
59
60 Hard string
61
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 ValidationDirective string
71
72 genericiooptions.IOStreams
73 }
74
75
76 func NewQuotaOpts(ioStreams genericiooptions.IOStreams) *QuotaOpts {
77 return &QuotaOpts{
78 PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
79 IOStreams: ioStreams,
80 }
81 }
82
83
84 func NewCmdCreateQuota(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
85 o := NewQuotaOpts(ioStreams)
86
87 cmd := &cobra.Command{
88 Use: "quota NAME [--hard=key1=value1,key2=value2] [--scopes=Scope1,Scope2] [--dry-run=server|client|none]",
89 DisableFlagsInUseLine: true,
90 Aliases: []string{"resourcequota"},
91 Short: i18n.T("Create a quota with the specified name"),
92 Long: quotaLong,
93 Example: quotaExample,
94 Run: func(cmd *cobra.Command, args []string) {
95 cmdutil.CheckErr(o.Complete(f, cmd, args))
96 cmdutil.CheckErr(o.Validate())
97 cmdutil.CheckErr(o.Run())
98 },
99 }
100
101 o.PrintFlags.AddFlags(cmd)
102
103 cmdutil.AddApplyAnnotationFlags(cmd)
104 cmdutil.AddValidateFlags(cmd)
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")
109 return cmd
110 }
111
112
113 func (o *QuotaOpts) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
114 var err error
115 o.Name, err = NameFromCommandArgs(cmd, args)
116 if err != nil {
117 return err
118 }
119
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
127 }
128
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
136 o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
137 if err != nil {
138 return err
139 }
140
141 cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
142
143 printer, err := o.PrintFlags.ToPrinter()
144 if err != nil {
145 return err
146 }
147
148 o.PrintObj = func(obj runtime.Object) error {
149 return printer.PrintObj(obj, o.Out)
150 }
151
152 o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
153 if err != nil {
154 return err
155 }
156
157 return nil
158 }
159
160
161 func (o *QuotaOpts) Validate() error {
162 if len(o.Name) == 0 {
163 return fmt.Errorf("name must be specified")
164 }
165 return nil
166 }
167
168
169 func (o *QuotaOpts) Run() error {
170 resourceQuota, err := o.createQuota()
171 if err != nil {
172 return err
173 }
174
175 if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, resourceQuota, scheme.DefaultJSONEncoder()); err != nil {
176 return err
177 }
178
179 if o.DryRunStrategy != cmdutil.DryRunClient {
180 createOptions := metav1.CreateOptions{}
181 if o.FieldManager != "" {
182 createOptions.FieldManager = o.FieldManager
183 }
184 createOptions.FieldValidation = o.ValidationDirective
185 if o.DryRunStrategy == cmdutil.DryRunServer {
186 createOptions.DryRun = []string{metav1.DryRunAll}
187 }
188 resourceQuota, err = o.Client.ResourceQuotas(o.Namespace).Create(context.TODO(), resourceQuota, createOptions)
189 if err != nil {
190 return fmt.Errorf("failed to create quota: %v", err)
191 }
192 }
193 return o.PrintObj(resourceQuota)
194 }
195
196 func (o *QuotaOpts) createQuota() (*corev1.ResourceQuota, error) {
197 namespace := ""
198 if o.EnforceNamespace {
199 namespace = o.Namespace
200 }
201 resourceQuota := &corev1.ResourceQuota{
202 TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String(), Kind: "ResourceQuota"},
203 ObjectMeta: metav1.ObjectMeta{
204 Name: o.Name,
205 Namespace: namespace,
206 },
207 }
208
209 resourceList, err := populateResourceListV1(o.Hard)
210 if err != nil {
211 return nil, err
212 }
213
214 scopes, err := parseScopes(o.Scopes)
215 if err != nil {
216 return nil, err
217 }
218
219 resourceQuota.Spec.Hard = resourceList
220 resourceQuota.Spec.Scopes = scopes
221
222 return resourceQuota, nil
223 }
224
225
226
227 func populateResourceListV1(spec string) (corev1.ResourceList, error) {
228
229 if spec == "" {
230 return nil, nil
231 }
232
233 result := corev1.ResourceList{}
234 resourceStatements := strings.Split(spec, ",")
235 for _, resourceStatement := range resourceStatements {
236 parts := strings.Split(resourceStatement, "=")
237 if len(parts) != 2 {
238 return nil, fmt.Errorf("Invalid argument syntax %v, expected <resource>=<value>", resourceStatement)
239 }
240 resourceName := corev1.ResourceName(parts[0])
241 resourceQuantity, err := resourceapi.ParseQuantity(parts[1])
242 if err != nil {
243 return nil, err
244 }
245 result[resourceName] = resourceQuantity
246 }
247 return result, nil
248 }
249
250 func parseScopes(spec string) ([]corev1.ResourceQuotaScope, error) {
251
252 if spec == "" {
253 return nil, nil
254 }
255
256 scopes := strings.Split(spec, ",")
257 result := make([]corev1.ResourceQuotaScope, 0, len(scopes))
258 for _, scope := range scopes {
259
260
261 if scope == "" {
262 return nil, fmt.Errorf("invalid resource quota scope \"\"")
263 }
264
265 result = append(result, corev1.ResourceQuotaScope(scope))
266 }
267 return result, nil
268 }
269
View as plain text