mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 05:27:21 +00:00
Remove the dependency between create quota command and generators
This commit is contained in:
parent
def8fe3b4e
commit
0222f2d033
@ -30,6 +30,7 @@ go_library(
|
|||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
|
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
@ -43,6 +44,7 @@ go_library(
|
|||||||
"//staging/src/k8s.io/client-go/kubernetes/typed/apps/v1:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes/typed/apps/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/kubernetes/typed/batch/v1:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes/typed/batch/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/kubernetes/typed/batch/v1beta1:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes/typed/batch/v1beta1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/component-base/cli/flag:go_default_library",
|
"//staging/src/k8s.io/component-base/cli/flag:go_default_library",
|
||||||
"//staging/src/k8s.io/kubectl/pkg/cmd/util:go_default_library",
|
"//staging/src/k8s.io/kubectl/pkg/cmd/util:go_default_library",
|
||||||
|
@ -17,12 +17,22 @@ limitations under the License.
|
|||||||
package create
|
package create
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
resourceapi "k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||||
|
resourcecli "k8s.io/cli-runtime/pkg/resource"
|
||||||
|
coreclient "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||||
"k8s.io/kubectl/pkg/generate"
|
"k8s.io/kubectl/pkg/scheme"
|
||||||
generateversioned "k8s.io/kubectl/pkg/generate/versioned"
|
"k8s.io/kubectl/pkg/util"
|
||||||
"k8s.io/kubectl/pkg/util/i18n"
|
"k8s.io/kubectl/pkg/util/i18n"
|
||||||
"k8s.io/kubectl/pkg/util/templates"
|
"k8s.io/kubectl/pkg/util/templates"
|
||||||
)
|
)
|
||||||
@ -41,14 +51,38 @@ var (
|
|||||||
|
|
||||||
// QuotaOpts holds the command-line options for 'create quota' sub command
|
// QuotaOpts holds the command-line options for 'create quota' sub command
|
||||||
type QuotaOpts struct {
|
type QuotaOpts struct {
|
||||||
CreateSubcommandOptions *CreateSubcommandOptions
|
// PrintFlags holds options necessary for obtaining a printer
|
||||||
|
PrintFlags *genericclioptions.PrintFlags
|
||||||
|
PrintObj func(obj runtime.Object) error
|
||||||
|
// The name of a quota object.
|
||||||
|
Name string
|
||||||
|
// The hard resource limit string before parsing.
|
||||||
|
Hard string
|
||||||
|
// The scopes of a quota object before parsing.
|
||||||
|
Scopes string
|
||||||
|
CreateAnnotation bool
|
||||||
|
FieldManager string
|
||||||
|
Namespace string
|
||||||
|
EnforceNamespace bool
|
||||||
|
|
||||||
|
Client *coreclient.CoreV1Client
|
||||||
|
DryRunStrategy cmdutil.DryRunStrategy
|
||||||
|
DryRunVerifier *resourcecli.DryRunVerifier
|
||||||
|
|
||||||
|
genericclioptions.IOStreams
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewQuotaOpts creates a new *QuotaOpts with sane defaults
|
||||||
|
func NewQuotaOpts(ioStreams genericclioptions.IOStreams) *QuotaOpts {
|
||||||
|
return &QuotaOpts{
|
||||||
|
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
|
||||||
|
IOStreams: ioStreams,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCmdCreateQuota is a macro command to create a new quota
|
// NewCmdCreateQuota is a macro command to create a new quota
|
||||||
func NewCmdCreateQuota(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
func NewCmdCreateQuota(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||||
options := &QuotaOpts{
|
o := NewQuotaOpts(ioStreams)
|
||||||
CreateSubcommandOptions: NewCreateSubcommandOptions(ioStreams),
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "quota NAME [--hard=key1=value1,key2=value2] [--scopes=Scope1,Scope2] [--dry-run=server|client|none]",
|
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)
|
|||||||
Long: quotaLong,
|
Long: quotaLong,
|
||||||
Example: quotaExample,
|
Example: quotaExample,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
cmdutil.CheckErr(options.Complete(f, cmd, args))
|
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||||
cmdutil.CheckErr(options.Run())
|
cmdutil.CheckErr(o.Validate())
|
||||||
|
cmdutil.CheckErr(o.Run())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
options.CreateSubcommandOptions.PrintFlags.AddFlags(cmd)
|
o.PrintFlags.AddFlags(cmd)
|
||||||
|
|
||||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||||
cmdutil.AddValidateFlags(cmd)
|
cmdutil.AddValidateFlags(cmd)
|
||||||
cmdutil.AddGeneratorFlags(cmd, generateversioned.ResourceQuotaV1GeneratorName)
|
cmdutil.AddDryRunFlag(cmd)
|
||||||
cmd.Flags().String("hard", "", i18n.T("A comma-delimited set of resource=quantity pairs that define a hard limit."))
|
cmd.Flags().StringVar(&o.Hard, "hard", o.Hard, i18n.T("A comma-delimited set of resource=quantity pairs that define a hard limit."))
|
||||||
cmd.Flags().String("scopes", "", i18n.T("A comma-delimited set of quota scopes that must all match each object tracked by the quota."))
|
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."))
|
||||||
cmdutil.AddFieldManagerFlagVar(cmd, &options.CreateSubcommandOptions.FieldManager, "kubectl-create")
|
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
// Complete completes all the required options
|
// Complete completes all the required options
|
||||||
func (o *QuotaOpts) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
func (o *QuotaOpts) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||||
name, err := NameFromCommandArgs(cmd, args)
|
var err error
|
||||||
|
o.Name, err = NameFromCommandArgs(cmd, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var generator generate.StructuredGenerator
|
restConfig, err := f.ToRESTConfig()
|
||||||
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
|
if err != nil {
|
||||||
case generateversioned.ResourceQuotaV1GeneratorName:
|
return err
|
||||||
generator = &generateversioned.ResourceQuotaGeneratorV1{
|
}
|
||||||
Name: name,
|
o.Client, err = coreclient.NewForConfig(restConfig)
|
||||||
Hard: cmdutil.GetFlagString(cmd, "hard"),
|
if err != nil {
|
||||||
Scopes: cmdutil.GetFlagString(cmd, "scopes"),
|
return err
|
||||||
}
|
|
||||||
default:
|
|
||||||
return errUnsupportedGenerator(cmd, generatorName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return o.CreateSubcommandOptions.Complete(f, cmd, args, generator)
|
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
|
||||||
|
|
||||||
|
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dynamicClient, err := f.DynamicClient()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
discoveryClient, err := f.ToDiscoveryClient()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
o.DryRunVerifier = resourcecli.NewDryRunVerifier(dynamicClient, discoveryClient)
|
||||||
|
|
||||||
|
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
|
||||||
|
|
||||||
|
printer, err := o.PrintFlags.ToPrinter()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
o.PrintObj = func(obj runtime.Object) error {
|
||||||
|
return printer.PrintObj(obj, o.Out)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run calls the CreateSubcommandOptions.Run in QuotaOpts instance
|
// Validate checks to the QuotaOpts to see if there is sufficient information run the command.
|
||||||
func (o *QuotaOpts) Run() error {
|
func (o *QuotaOpts) Validate() error {
|
||||||
return o.CreateSubcommandOptions.Run()
|
if len(o.Name) == 0 {
|
||||||
|
return fmt.Errorf("name must be specified")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run does the work
|
||||||
|
func (o *QuotaOpts) Run() error {
|
||||||
|
resourceQuota, err := o.createQuota()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, resourceQuota, scheme.DefaultJSONEncoder()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.DryRunStrategy != cmdutil.DryRunClient {
|
||||||
|
createOptions := metav1.CreateOptions{}
|
||||||
|
if o.FieldManager != "" {
|
||||||
|
createOptions.FieldManager = o.FieldManager
|
||||||
|
}
|
||||||
|
if o.DryRunStrategy == cmdutil.DryRunServer {
|
||||||
|
if err := o.DryRunVerifier.HasSupport(resourceQuota.GroupVersionKind()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
createOptions.DryRun = []string{metav1.DryRunAll}
|
||||||
|
}
|
||||||
|
resourceQuota, err = o.Client.ResourceQuotas(o.Namespace).Create(context.TODO(), resourceQuota, createOptions)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create quota: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return o.PrintObj(resourceQuota)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *QuotaOpts) createQuota() (*corev1.ResourceQuota, error) {
|
||||||
|
namespace := ""
|
||||||
|
if o.EnforceNamespace {
|
||||||
|
namespace = o.Namespace
|
||||||
|
}
|
||||||
|
fmt.Println(corev1.SchemeGroupVersion.String())
|
||||||
|
resourceQuota := &corev1.ResourceQuota{
|
||||||
|
TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String(), Kind: "ResourceQuota"},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: o.Name,
|
||||||
|
Namespace: namespace,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceList, err := populateResourceListV1(o.Hard)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
scopes, err := parseScopes(o.Scopes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceQuota.Spec.Hard = resourceList
|
||||||
|
resourceQuota.Spec.Scopes = scopes
|
||||||
|
|
||||||
|
return resourceQuota, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// populateResourceListV1 takes strings of form <resourceName1>=<value1>,<resourceName1>=<value2>
|
||||||
|
// and returns ResourceList.
|
||||||
|
func populateResourceListV1(spec string) (corev1.ResourceList, error) {
|
||||||
|
// empty input gets a nil response to preserve generator test expected behaviors
|
||||||
|
if spec == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := corev1.ResourceList{}
|
||||||
|
resourceStatements := strings.Split(spec, ",")
|
||||||
|
for _, resourceStatement := range resourceStatements {
|
||||||
|
parts := strings.Split(resourceStatement, "=")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return nil, fmt.Errorf("Invalid argument syntax %v, expected <resource>=<value>", resourceStatement)
|
||||||
|
}
|
||||||
|
resourceName := corev1.ResourceName(parts[0])
|
||||||
|
resourceQuantity, err := resourceapi.ParseQuantity(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result[resourceName] = resourceQuantity
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseScopes(spec string) ([]corev1.ResourceQuotaScope, error) {
|
||||||
|
// empty input gets a nil response to preserve test expected behaviors
|
||||||
|
if spec == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
scopes := strings.Split(spec, ",")
|
||||||
|
result := make([]corev1.ResourceQuotaScope, 0, len(scopes))
|
||||||
|
for _, scope := range scopes {
|
||||||
|
// intentionally do not verify the scope against the valid scope list. This is done by the apiserver anyway.
|
||||||
|
|
||||||
|
if scope == "" {
|
||||||
|
return nil, fmt.Errorf("invalid resource quota scope \"\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, corev1.ResourceQuotaScope(scope))
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
@ -19,49 +19,116 @@ package create
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||||
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateQuota(t *testing.T) {
|
func TestCreateQuota(t *testing.T) {
|
||||||
resourceQuotaObject := &v1.ResourceQuota{}
|
hards := []string{"cpu=1", "cpu=1,pods=42"}
|
||||||
resourceQuotaObject.Name = "my-quota"
|
var resourceQuotaSpecLists []corev1.ResourceList
|
||||||
|
for _, hard := range hards {
|
||||||
|
resourceQuotaSpecList, err := populateResourceListV1(hard)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
resourceQuotaSpecLists = append(resourceQuotaSpecLists, resourceQuotaSpecList)
|
||||||
|
}
|
||||||
|
|
||||||
tests := map[string]struct {
|
tests := map[string]struct {
|
||||||
flags []string
|
options *QuotaOpts
|
||||||
expectedOutput string
|
expected *corev1.ResourceQuota
|
||||||
}{
|
}{
|
||||||
"single resource": {
|
"single resource": {
|
||||||
flags: []string{"--hard=cpu=1"},
|
options: &QuotaOpts{
|
||||||
expectedOutput: "resourcequota/" + resourceQuotaObject.Name + "\n",
|
Name: "my-quota",
|
||||||
|
Hard: hards[0],
|
||||||
|
Scopes: "",
|
||||||
|
},
|
||||||
|
expected: &corev1.ResourceQuota{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: "ResourceQuota",
|
||||||
|
APIVersion: "v1",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "my-quota",
|
||||||
|
},
|
||||||
|
Spec: corev1.ResourceQuotaSpec{
|
||||||
|
Hard: resourceQuotaSpecLists[0],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"single resource with a scope": {
|
"single resource with a scope": {
|
||||||
flags: []string{"--hard=cpu=1", "--scopes=BestEffort"},
|
options: &QuotaOpts{
|
||||||
expectedOutput: "resourcequota/" + resourceQuotaObject.Name + "\n",
|
Name: "my-quota",
|
||||||
|
Hard: hards[0],
|
||||||
|
Scopes: "BestEffort",
|
||||||
|
},
|
||||||
|
expected: &corev1.ResourceQuota{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: "ResourceQuota",
|
||||||
|
APIVersion: "v1",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "my-quota",
|
||||||
|
},
|
||||||
|
Spec: corev1.ResourceQuotaSpec{
|
||||||
|
Hard: resourceQuotaSpecLists[0],
|
||||||
|
Scopes: []corev1.ResourceQuotaScope{"BestEffort"},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"multiple resources": {
|
"multiple resources": {
|
||||||
flags: []string{"--hard=cpu=1,pods=42", "--scopes=BestEffort"},
|
options: &QuotaOpts{
|
||||||
expectedOutput: "resourcequota/" + resourceQuotaObject.Name + "\n",
|
Name: "my-quota",
|
||||||
|
Hard: hards[1],
|
||||||
|
Scopes: "BestEffort",
|
||||||
|
},
|
||||||
|
expected: &corev1.ResourceQuota{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: "ResourceQuota",
|
||||||
|
APIVersion: "v1",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "my-quota",
|
||||||
|
},
|
||||||
|
Spec: corev1.ResourceQuotaSpec{
|
||||||
|
Hard: resourceQuotaSpecLists[1],
|
||||||
|
Scopes: []corev1.ResourceQuotaScope{"BestEffort"},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"single resource with multiple scopes": {
|
"single resource with multiple scopes": {
|
||||||
flags: []string{"--hard=cpu=1", "--scopes=BestEffort,NotTerminating"},
|
options: &QuotaOpts{
|
||||||
expectedOutput: "resourcequota/" + resourceQuotaObject.Name + "\n",
|
Name: "my-quota",
|
||||||
|
Hard: hards[0],
|
||||||
|
Scopes: "BestEffort,NotTerminating",
|
||||||
|
},
|
||||||
|
expected: &corev1.ResourceQuota{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: "ResourceQuota",
|
||||||
|
APIVersion: "v1",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "my-quota",
|
||||||
|
},
|
||||||
|
Spec: corev1.ResourceQuotaSpec{
|
||||||
|
Hard: resourceQuotaSpecLists[0],
|
||||||
|
Scopes: []corev1.ResourceQuotaScope{"BestEffort", "NotTerminating"},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for name, test := range tests {
|
|
||||||
|
for name, tc := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
resourceQuota, err := tc.options.createQuota()
|
||||||
defer tf.Cleanup()
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error:\n%#v\n", err)
|
||||||
ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
return
|
||||||
cmd := NewCmdCreateQuota(tf, ioStreams)
|
}
|
||||||
cmd.Flags().Parse(test.flags)
|
if !apiequality.Semantic.DeepEqual(resourceQuota, tc.expected) {
|
||||||
cmd.Flags().Set("output", "name")
|
t.Errorf("expected:\n%#v\ngot:\n%#v", tc.expected, resourceQuota)
|
||||||
cmd.Run(cmd, []string{resourceQuotaObject.Name})
|
|
||||||
|
|
||||||
if buf.String() != test.expectedOutput {
|
|
||||||
t.Errorf("%s: expected output: %s, but got: %s", name, test.expectedOutput, buf.String())
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user