...

Source file src/k8s.io/kubectl/pkg/cmd/create/create_quota.go

Documentation: k8s.io/kubectl/pkg/cmd/create

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    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  // QuotaOpts holds the command-line options for 'create quota' sub command
    53  type QuotaOpts struct {
    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  	ValidationDirective string
    71  
    72  	genericiooptions.IOStreams
    73  }
    74  
    75  // NewQuotaOpts creates a new *QuotaOpts with sane defaults
    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  // NewCmdCreateQuota is a macro command to create a new quota
    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  // Complete completes all the required options
   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  // Validate checks to the QuotaOpts to see if there is sufficient information run the command.
   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  // Run does the work
   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  // populateResourceListV1 takes strings of form <resourceName1>=<value1>,<resourceName1>=<value2>
   226  // and returns ResourceList.
   227  func populateResourceListV1(spec string) (corev1.ResourceList, error) {
   228  	// empty input gets a nil response to preserve generator test expected behaviors
   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  	// empty input gets a nil response to preserve test expected behaviors
   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  		// intentionally do not verify the scope against the valid scope list. This is done by the apiserver anyway.
   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