From 955dd6f5f58ae1bb1d84b5c3322771333746ea08 Mon Sep 17 00:00:00 2001 From: Wojciech Trocki Date: Tue, 28 Jun 2022 17:53:56 +0100 Subject: [PATCH] Revert "fix(kafka create): reduce number of calls made to AMS API (#1596)" (#1616) This reverts commit 5346fc15b9f91b8624d94299a8c79ff92d92cf8b. --- docs/commands/rhoas_kafka_create.md | 1 - go.mod | 2 +- go.sum | 5 +- pkg/cmd/kafka/create/api_validators.go | 28 +-- pkg/cmd/kafka/create/completions.go | 48 +++++ pkg/cmd/kafka/create/create.go | 95 ++++++---- pkg/cmd/kafka/create/data.go | 6 +- pkg/cmd/kafka/kafkacmdutil/kafka_util.go | 7 - .../localize/locales/en/cmd/kafka.en.toml | 3 - pkg/shared/accountmgmtutil/ams.go | 178 ++++++++++-------- pkg/shared/accountmgmtutil/api.go | 5 +- 11 files changed, 218 insertions(+), 160 deletions(-) diff --git a/docs/commands/rhoas_kafka_create.md b/docs/commands/rhoas_kafka_create.md index d4110dc9f..4ee666084 100644 --- a/docs/commands/rhoas_kafka_create.md +++ b/docs/commands/rhoas_kafka_create.md @@ -30,7 +30,6 @@ $ rhoas kafka create -o yaml ### Options ``` - --billing-model string Billing model to be used --dry-run Validate all user provided arguments without creating the Kafka instance --marketplace string Name of the marketplace where the instance is purchased on --marketplace-account-id string Cloud Account ID for the marketplace diff --git a/go.mod b/go.mod index 036f47d8b..fbece4083 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/redhat-developer/app-services-sdk-go/accountmgmt v0.2.0 github.com/redhat-developer/app-services-sdk-go/connectormgmt v0.7.0 github.com/redhat-developer/app-services-sdk-go/kafkainstance v0.6.0 - github.com/redhat-developer/app-services-sdk-go/kafkamgmt v0.12.1 + github.com/redhat-developer/app-services-sdk-go/kafkamgmt v0.12.0 github.com/redhat-developer/app-services-sdk-go/registryinstance v0.3.1 github.com/redhat-developer/app-services-sdk-go/registrymgmt v0.6.1 github.com/redhat-developer/service-binding-operator v0.9.0 diff --git a/go.sum b/go.sum index d7d9a3acc..10f5d2b34 100644 --- a/go.sum +++ b/go.sum @@ -640,8 +640,8 @@ github.com/redhat-developer/app-services-sdk-go/connectormgmt v0.7.0 h1:GcbNg/Ad github.com/redhat-developer/app-services-sdk-go/connectormgmt v0.7.0/go.mod h1:0WB4LlMmesjBlGKvnMXQ7twPxeSr27f5a+w4QnMoSdQ= github.com/redhat-developer/app-services-sdk-go/kafkainstance v0.6.0 h1:ExEHQaihnPNxN2nKXB0q5nrmSv4p8b3Idzt7TChxv+Q= github.com/redhat-developer/app-services-sdk-go/kafkainstance v0.6.0/go.mod h1:hMpejngP3BFnifCDH1gKRG9cU9Q4lr0WiQaW7A1LYo4= -github.com/redhat-developer/app-services-sdk-go/kafkamgmt v0.12.1 h1:Gcyn2kLlslsVT6T8qoiCJpJFPrnD2i2KIFeKQJrXkTY= -github.com/redhat-developer/app-services-sdk-go/kafkamgmt v0.12.1/go.mod h1:RoPo3tyHjv8apStFNVjChwWYdlWhg6hMzi1IrH3yQX8= +github.com/redhat-developer/app-services-sdk-go/kafkamgmt v0.12.0 h1:63UhOYB8TozKdnkkws2pXc0D1lEB+K3qX63/OxkjDas= +github.com/redhat-developer/app-services-sdk-go/kafkamgmt v0.12.0/go.mod h1:m+m7d6xkC9WbSxemslyhjv0jVhquWLysRfdh+RQ5hH0= github.com/redhat-developer/app-services-sdk-go/registryinstance v0.3.1 h1:xRq5XJzRDs/Z7e/9SDt6zbNRIyesC4LTqN9ajHKwjHo= github.com/redhat-developer/app-services-sdk-go/registryinstance v0.3.1/go.mod h1:Z/gr/snlpsqYg4vftmcx97vCR3qMQJhALGelDHx4pMA= github.com/redhat-developer/app-services-sdk-go/registrymgmt v0.6.1 h1:3sUmQ3nAawsYWg7ZCO2Q8HF2J7MW6YA38h/YFL3ao6o= @@ -933,7 +933,6 @@ golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2 h1:+jnHzr9VPj32ykQVai5DNahi9+NSp7yYuCsl5eAQtL0= golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/pkg/cmd/kafka/create/api_validators.go b/pkg/cmd/kafka/create/api_validators.go index 63d0ff818..882930a85 100644 --- a/pkg/cmd/kafka/create/api_validators.go +++ b/pkg/cmd/kafka/create/api_validators.go @@ -3,7 +3,6 @@ package create import ( "strings" - "github.com/redhat-developer/app-services-cli/pkg/core/cmdutil/flagutil" "github.com/redhat-developer/app-services-cli/pkg/core/localize" "github.com/redhat-developer/app-services-cli/pkg/shared/accountmgmtutil" "github.com/redhat-developer/app-services-cli/pkg/shared/connection" @@ -14,9 +13,10 @@ import ( ) type ValidatorInput struct { - provider string - region string - size string + provider string + region string + size string + userAMSInstanceType *accountmgmtutil.QuotaSpec f *factory.Factory @@ -24,8 +24,6 @@ type ValidatorInput struct { conn connection.Connection } -var validBillingModels []string = []string{accountmgmtutil.QuotaMarketplaceType, accountmgmtutil.QuotaStandardType} - func (input *ValidatorInput) ValidateProviderAndRegion() error { f := input.f f.Logger.Debug("Validating provider and region") @@ -110,7 +108,7 @@ func (input *ValidatorInput) ValidateSize() error { return nil } - sizes, err := FetchValidKafkaSizesLabels(input.f, input.provider, input.region, input.userAMSInstanceType) + sizes, err := FetchValidKafkaSizesLabels(input.f, input.provider, input.region, *input.userAMSInstanceType) if err != nil { return err } @@ -121,19 +119,3 @@ func (input *ValidatorInput) ValidateSize() error { return nil } - -// ValidateBillingModel validates if user provided a correct billing model -func ValidateBillingModel(billingModel string) error { - - if billingModel == "" { - return nil - } - - isValid := flagutil.IsValidInput(billingModel, validBillingModels...) - - if isValid { - return nil - } - - return flagutil.InvalidValueError("billing-model", billingModel, validBillingModels...) -} diff --git a/pkg/cmd/kafka/create/completions.go b/pkg/cmd/kafka/create/completions.go index 6ef476f9d..284c1833b 100644 --- a/pkg/cmd/kafka/create/completions.go +++ b/pkg/cmd/kafka/create/completions.go @@ -1,7 +1,10 @@ package create import ( + "github.com/redhat-developer/app-services-cli/pkg/shared/accountmgmtutil" + "github.com/redhat-developer/app-services-cli/pkg/shared/connection" "github.com/redhat-developer/app-services-cli/pkg/shared/factory" + "github.com/redhat-developer/app-services-cli/pkg/shared/remote" "github.com/spf13/cobra" ) @@ -23,3 +26,48 @@ func GetCloudProviderRegionCompletionValues(f *factory.Factory, providerID strin return validRegions, cobra.ShellCompDirectiveNoSpace } + +// GetKafkaSizeCompletionValues returns a list of valid kafka sizes for the specified region and ams instance types +func GetKafkaSizeCompletionValues(f *factory.Factory, providerID string, regionId string) (validRegions []string, directive cobra.ShellCompDirective) { + directive = cobra.ShellCompDirectiveNoSpace + + // We need both values to provide a valid list of sizes + if providerID == "" || regionId == "" { + return nil, directive + } + + err, constants := remote.GetRemoteServiceConstants(f.Context, f.Logger) + if err != nil { + return nil, directive + } + + conn, err := f.Connection(connection.DefaultConfigSkipMasAuth) + if err != nil { + return nil, directive + } + + userInstanceType, _ := accountmgmtutil.GetUserSupportedInstanceType(f.Context, &constants.Kafka.Ams, conn) + + // Not including quota in this request as it takes very long time to list quota for all regions in suggestion mode + validRegions, _ = FetchValidKafkaSizesLabels(f, providerID, regionId, *userInstanceType) + + return validRegions, cobra.ShellCompDirectiveNoSpace +} + +// GetMarketplaceAcctIdCompletionValues returns a list of valid marketplace account IDs for the organization +func GetMarketplaceAcctIdCompletionValues(f *factory.Factory) (validMarketplaceAcctIDs []string, directive cobra.ShellCompDirective) { + directive = cobra.ShellCompDirectiveNoSpace + + validMarketplaceAcctIDs, _ = accountmgmtutil.GetValidMarketplaceAcctIDs(f.Context, f.Connection, "") + + return validMarketplaceAcctIDs, directive +} + +// GetMarketplaceCompletionValues returns a list of valid marketplaces for the organization +func GetMarketplaceCompletionValues(f *factory.Factory) (validMarketplaces []string, directive cobra.ShellCompDirective) { + directive = cobra.ShellCompDirectiveNoSpace + + validMarketplaces, _ = accountmgmtutil.GetValidMarketplaces(f.Context, f.Connection) + + return validMarketplaces, directive +} diff --git a/pkg/cmd/kafka/create/create.go b/pkg/cmd/kafka/create/create.go index c1115a893..766a01200 100644 --- a/pkg/cmd/kafka/create/create.go +++ b/pkg/cmd/kafka/create/create.go @@ -52,7 +52,6 @@ type options struct { marketplaceAcctId string marketplace string - billingModel string outputFormat string autoUse bool @@ -94,7 +93,7 @@ func NewCreateCommand(f *factory.Factory) *cobra.Command { } } - if opts.bypassChecks && (opts.marketplace != "" || opts.marketplaceAcctId != "" || opts.billingModel != "") { + if opts.bypassChecks && (opts.marketplace != "" || opts.marketplaceAcctId != "") { return f.Localizer.MustLocalizeError("kafka.create.error.bypassChecks.marketplace") } @@ -112,6 +111,26 @@ func NewCreateCommand(f *factory.Factory) *cobra.Command { return flagutil.InvalidValueError("output", opts.outputFormat, validOutputFormats...) } + if !opts.bypassChecks { + validMarketplaces, err := accountmgmtutil.GetValidMarketplaces(f.Context, f.Connection) + if err != nil { + return err + } + + if opts.marketplace != "" && !flagutil.IsValidInput(opts.marketplace, validMarketplaces...) { + return flagutil.InvalidValueError(FlagMarketPlace, opts.marketplace, validMarketplaces...) + } + + validMarketplaceAcctIDs, err := accountmgmtutil.GetValidMarketplaceAcctIDs(f.Context, f.Connection, opts.marketplace) + if err != nil { + return err + } + + if opts.marketplaceAcctId != "" && !flagutil.IsValidInput(opts.marketplaceAcctId, validMarketplaceAcctIDs...) { + return flagutil.InvalidValueError(FlagMarketPlaceAcctID, opts.marketplaceAcctId, validMarketplaceAcctIDs...) + } + } + return runCreate(opts) }, } @@ -128,7 +147,6 @@ func NewCreateCommand(f *factory.Factory) *cobra.Command { flags.BoolVar(&opts.autoUse, "use", true, f.Localizer.MustLocalize("kafka.create.flag.autoUse.description")) flags.BoolVarP(&opts.wait, "wait", "w", false, f.Localizer.MustLocalize("kafka.create.flag.wait.description")) flags.BoolVarP(&opts.dryRun, "dry-run", "", false, f.Localizer.MustLocalize("kafka.create.flag.dryrun.description")) - flags.StringVar(&opts.billingModel, "billing-model", "", f.Localizer.MustLocalize("kafka.create.flag.billingModel.description")) flags.AddBypassTermsCheck(&opts.bypassChecks) _ = cmd.RegisterFlagCompletionFunc(FlagProvider, func(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { @@ -139,6 +157,18 @@ func NewCreateCommand(f *factory.Factory) *cobra.Command { return GetCloudProviderRegionCompletionValues(f, opts.provider) }) + _ = cmd.RegisterFlagCompletionFunc(FlagSize, func(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { + return GetKafkaSizeCompletionValues(f, opts.provider, opts.region) + }) + + _ = cmd.RegisterFlagCompletionFunc(FlagMarketPlaceAcctID, func(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { + return GetMarketplaceAcctIdCompletionValues(f) + }) + + _ = cmd.RegisterFlagCompletionFunc(FlagMarketPlace, func(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { + return GetMarketplaceCompletionValues(f) + }) + return cmd } @@ -182,16 +212,10 @@ func runCreate(opts *options) error { return nil } - err = ValidateBillingModel(opts.billingModel) - if err != nil { - return err + userInstanceType, err = accountmgmtutil.GetUserSupportedInstanceType(f.Context, &constants.Kafka.Ams, conn) + if err != nil || userInstanceType == nil { + return f.Localizer.MustLocalizeError("kafka.create.error.userInstanceType.notFound") } - - userInstanceType, err = accountmgmtutil.FetchQuotaCost(f, opts.billingModel, opts.marketplaceAcctId, opts.marketplace, &constants.Kafka.Ams) - if err != nil { - return err - } - } var payload *kafkamgmtclient.KafkaRequestPayload @@ -201,7 +225,7 @@ func runCreate(opts *options) error { return f.Localizer.MustLocalizeError("kafka.create.error.noInteractiveMode") } - payload, err = promptKafkaPayload(opts, userInstanceType) + payload, err = promptKafkaPayload(opts, *userInstanceType) if err != nil { return err } @@ -225,8 +249,6 @@ func runCreate(opts *options) error { payload.Marketplace.Set(&opts.marketplace) payload.BillingCloudAccountId = kafkamgmtclient.NullableString{} payload.BillingCloudAccountId.Set(&opts.marketplaceAcctId) - payload.BillingModel = kafkamgmtclient.NullableString{} - payload.BillingModel.Set(&opts.billingModel) } if !opts.bypassChecks { @@ -249,7 +271,7 @@ func runCreate(opts *options) error { return err1 } if opts.size != "" { - sizes, err1 := FetchValidKafkaSizes(opts.f, opts.provider, opts.region, userInstanceType) + sizes, err1 := FetchValidKafkaSizes(opts.f, opts.provider, opts.region, *userInstanceType) if err1 != nil { return err1 } @@ -378,7 +400,7 @@ type promptAnswers struct { // Show a prompt to allow the user to interactively insert the data for their Kafka // nolint:funlen -func promptKafkaPayload(opts *options, userQuotaType *accountmgmtutil.QuotaSpec) (*kafkamgmtclient.KafkaRequestPayload, error) { +func promptKafkaPayload(opts *options, userQuotaType accountmgmtutil.QuotaSpec) (*kafkamgmtclient.KafkaRequestPayload, error) { f := opts.f accountIDNullable := kafkamgmtclient.NullableString{} @@ -416,7 +438,7 @@ func promptKafkaPayload(opts *options, userQuotaType *accountmgmtutil.QuotaSpec) return nil, err } - regionIDs, err := GetEnabledCloudRegionIDs(opts.f, answers.CloudProvider, userQuotaType) + regionIDs, err := GetEnabledCloudRegionIDs(opts.f, answers.CloudProvider, &userQuotaType) if err != nil { return nil, err } @@ -452,34 +474,29 @@ func promptKafkaPayload(opts *options, userQuotaType *accountmgmtutil.QuotaSpec) } } - if !opts.bypassChecks && opts.billingModel == accountmgmtutil.QuotaMarketplaceType { + marketplaces, err := accountmgmtutil.GetValidMarketplaces(f.Context, f.Connection) + if err != nil { + return nil, err + } - marketplaces, err := accountmgmtutil.GetValidMarketplaces(userQuotaType) - if err != nil { + if !opts.bypassChecks && len(marketplaces) > 0 { + if err = promptMarketplaceSelect(f.Localizer, marketplaces, answers); err != nil { return nil, err } - if len(marketplaces) > 0 { - - if err = promptMarketplaceSelect(f.Localizer, marketplaces, answers); err != nil { - return nil, err - } + marketplaceAcctIDs, err := accountmgmtutil.GetValidMarketplaceAcctIDs(f.Context, f.Connection, answers.Marketplace) + if err != nil { + return nil, err + } - marketplaceAcctIDs, err := accountmgmtutil.GetValidMarketplaceAcctIDs(userQuotaType, answers.Marketplace) - if err != nil { + if len(marketplaceAcctIDs) > 0 { + if err = promptMarketplaceAcctIDSelect(f.Localizer, marketplaceAcctIDs, answers); err != nil { return nil, err } - - if len(marketplaceAcctIDs) > 0 { - if err = promptMarketplaceAcctIDSelect(f.Localizer, marketplaceAcctIDs, answers); err != nil { - return nil, err - } - } - - accountIDNullable.Set(&answers.MarketplaceAcctID) - marketplaceProviderNullable.Set(&answers.Marketplace) - } + + accountIDNullable.Set(&answers.MarketplaceAcctID) + marketplaceProviderNullable.Set(&answers.Marketplace) } payload := &kafkamgmtclient.KafkaRequestPayload{ @@ -490,7 +507,7 @@ func promptKafkaPayload(opts *options, userQuotaType *accountmgmtutil.QuotaSpec) Marketplace: marketplaceProviderNullable, } printSizeWarningIfNeeded(opts.f, answers.Size, sizes) - payload.SetPlan(mapAmsTypeToBackendType(userQuotaType) + "." + answers.Size) + payload.SetPlan(mapAmsTypeToBackendType(&userQuotaType) + "." + answers.Size) return payload, nil } diff --git a/pkg/cmd/kafka/create/data.go b/pkg/cmd/kafka/create/data.go index 23439d820..eea60feda 100644 --- a/pkg/cmd/kafka/create/data.go +++ b/pkg/cmd/kafka/create/data.go @@ -45,7 +45,7 @@ func GetValidKafkaSizesLabels(sizes []kafkamgmtclient.SupportedKafkaSize) []stri } func FetchValidKafkaSizesLabels(f *factory.Factory, - providerID string, regionId string, amsType *accountmgmtutil.QuotaSpec) ([]string, error) { + providerID string, regionId string, amsType accountmgmtutil.QuotaSpec) ([]string, error) { sizes, err := FetchValidKafkaSizes(f, providerID, regionId, amsType) if err != nil { return nil, err @@ -56,7 +56,7 @@ func FetchValidKafkaSizesLabels(f *factory.Factory, // return list of the valid instance sizes for the specified region and ams instance types func FetchValidKafkaSizes(f *factory.Factory, - providerID string, regionId string, amsType *accountmgmtutil.QuotaSpec) ([]kafkamgmtclient.SupportedKafkaSize, error) { + providerID string, regionId string, amsType accountmgmtutil.QuotaSpec) ([]kafkamgmtclient.SupportedKafkaSize, error) { conn, err := f.Connection(connection.DefaultConfigSkipMasAuth) if err != nil { @@ -73,7 +73,7 @@ func FetchValidKafkaSizes(f *factory.Factory, return nil, err } - desiredInstanceType := mapAmsTypeToBackendType(amsType) + desiredInstanceType := mapAmsTypeToBackendType(&amsType) // Temporary workaround to be removed if desiredInstanceType == DeveloperType { diff --git a/pkg/cmd/kafka/kafkacmdutil/kafka_util.go b/pkg/cmd/kafka/kafkacmdutil/kafka_util.go index f5b7848c4..3218b898b 100644 --- a/pkg/cmd/kafka/kafkacmdutil/kafka_util.go +++ b/pkg/cmd/kafka/kafkacmdutil/kafka_util.go @@ -15,13 +15,6 @@ import ( kafkamgmtclient "github.com/redhat-developer/app-services-sdk-go/kafkamgmt/apiv1/client" ) -var ( - billingModelMarketplace = "marketplace" - billingModelStandard = "standard" -) - -var ValidBillingModels = []string{billingModelMarketplace, billingModelStandard} - var ( validNameRegexp = regexp.MustCompile(`^[a-z]([-a-z0-9]*[a-z0-9])?$`) validSearchRegexp = regexp.MustCompile(`^([a-zA-Z0-9-_%]*[a-zA-Z0-9-_%])?$`) diff --git a/pkg/core/localize/locales/en/cmd/kafka.en.toml b/pkg/core/localize/locales/en/cmd/kafka.en.toml index 1dbf4be57..45dafc0fe 100644 --- a/pkg/core/localize/locales/en/cmd/kafka.en.toml +++ b/pkg/core/localize/locales/en/cmd/kafka.en.toml @@ -286,9 +286,6 @@ one = 'Wait until the Kafka instance is created' [kafka.create.flag.dryrun.description] one = 'Validate all user provided arguments without creating the Kafka instance' -[kafka.create.flag.billingModel.description] -one = 'Billing model to be used' - [kafka.create.flag.marketplaceId.description] one = 'Cloud Account ID for the marketplace' diff --git a/pkg/shared/accountmgmtutil/ams.go b/pkg/shared/accountmgmtutil/ams.go index 8256d25b6..b267c1f87 100644 --- a/pkg/shared/accountmgmtutil/ams.go +++ b/pkg/shared/accountmgmtutil/ams.go @@ -2,9 +2,7 @@ package accountmgmtutil import ( "context" - "encoding/json" "errors" - "fmt" "github.com/redhat-developer/app-services-cli/pkg/shared/connection" "github.com/redhat-developer/app-services-cli/pkg/shared/factory" @@ -38,10 +36,20 @@ func CheckTermsAccepted(ctx context.Context, spec *remote.AmsConfig, conn connec // QuotaSpec - contains quota name and remaining quota count type QuotaSpec struct { - Name string - Quota int - BillingModel string - CloudAccounts *[]amsclient.CloudAccount + Name string + Quota int + BillingModel string +} + +func GetUserSupportedInstanceType(ctx context.Context, spec *remote.AmsConfig, conn connection.Connection) (quota *QuotaSpec, err error) { + userInstanceTypes, err := GetUserSupportedInstanceTypes(ctx, spec, conn) + if err != nil { + return nil, err + } + + amsType := PickInstanceType(userInstanceTypes) + + return amsType, nil } func fetchOrgQuotaCost(ctx context.Context, conn connection.Connection) (*amsclient.QuotaCostList, error) { @@ -60,86 +68,72 @@ func fetchOrgQuotaCost(ctx context.Context, conn connection.Connection) (*amscli } -func FetchQuotaCost(f *factory.Factory, billingModel string, cloudAccountID string, marketplace string, spec *remote.AmsConfig) (userQuotaSpec *QuotaSpec, err error) { - - var conn connection.Connection - if conn, err = f.Connection(connection.DefaultConfigSkipMasAuth); err != nil { - return nil, err - } - - if billingModel == QuotaStandardType && (cloudAccountID != "" || marketplace != "") { - return nil, errors.New("accountID cant be provided with standard billing model") - } - - if (cloudAccountID != "") != (marketplace != "") { - return nil, errors.New("accountID and marketplace should be provided together") - } +func GetUserSupportedInstanceTypes(ctx context.Context, spec *remote.AmsConfig, conn connection.Connection) (quota []QuotaSpec, err error) { - if billingModel == "" && (cloudAccountID != "" || marketplace != "") { - billingModel = QuotaMarketplaceType - } else if billingModel == "" && cloudAccountID == "" && marketplace == "" { - f.Logger.Info("No billing model specified. Looking for prepaid instances") - billingModel = QuotaStandardType - } - - quotaCostGet, err := fetchOrgQuotaCost(f.Context, conn) + quotaCostGet, err := fetchOrgQuotaCost(ctx, conn) if err != nil { return nil, err } - var filteredQuotaCosts []amsclient.QuotaCost - - quotaCostList := quotaCostGet.GetItems() - - var userQuota amsclient.QuotaCost - - for _, quota := range quotaCostList { - relatedResources := quota.GetRelatedResources() - for i := range relatedResources { - if relatedResources[i].GetResourceName() == spec.ResourceName && relatedResources[i].GetProduct() == spec.InstanceQuotaID && relatedResources[i].GetBillingModel() == billingModel { - filteredQuotaCosts = append(filteredQuotaCosts, quota) + var quotas []QuotaSpec + for _, quota := range quotaCostGet.GetItems() { + quotaResources := quota.GetRelatedResources() + for i := range quotaResources { + quotaResource := quotaResources[i] + if quotaResource.GetResourceName() == spec.ResourceName { + if quotaResource.GetProduct() == spec.TrialProductQuotaID { + quotas = append(quotas, QuotaSpec{QuotaTrialType, 0, quotaResource.BillingModel}) + } else if quotaResource.GetProduct() == spec.InstanceQuotaID { + remainingQuota := int(quota.GetAllowed() - quota.GetConsumed()) + quotas = append(quotas, QuotaSpec{QuotaStandardType, remainingQuota, quotaResource.BillingModel}) + } } } } - if len(filteredQuotaCosts) == 0 { - return nil, errors.New("no quota object is available") - } - - filteredQuotasJSON, _ := json.Marshal(filteredQuotaCosts) - f.Logger.Debug(fmt.Sprintf("Filtered Quotas : %#v", string(filteredQuotasJSON))) - - if billingModel == QuotaMarketplaceType { + return BattleOfInstanceBillingModels(quotas), err +} - if len(filteredQuotaCosts) > 1 && marketplace == "" && cloudAccountID == "" { - return nil, errors.New("please specify marketplace provider and account id") - } +// This function selects the billing model that should be used +// It represents some requirement to always use the same standard billing models +// This function should not exist but it does represents some requirement that we cannot do on backend +func BattleOfInstanceBillingModels(quotas []QuotaSpec) []QuotaSpec { + var betterQuotasMap map[string]*QuotaSpec = make(map[string]*QuotaSpec) + alwaysWinsBillingModel := "standard" + for i := 0; i < len(quotas); i++ { + if quotas[i].BillingModel == alwaysWinsBillingModel { + betterQuotasMap[quotas[i].Name] = "as[i] + } else if betterQuotasMap[quotas[i].Name] == nil { + betterQuotasMap[quotas[i].Name] = "as[i] - if len(filteredQuotaCosts) == 1 && marketplace == "" && cloudAccountID == "" { - userQuota = filteredQuotaCosts[0] - } else { - for _, filteredQuotaCost := range filteredQuotaCosts { - for _, cloudAccount := range filteredQuotaCost.GetCloudAccounts() { - if cloudAccount.GetCloudAccountId() == cloudAccountID && cloudAccount.GetCloudProviderId() == marketplace { - userQuota = filteredQuotaCost - } - } - } } - } else { - userQuota = filteredQuotaCosts[0] } - - if userQuota.GetQuotaId() == "" { - return nil, errors.New("quota could not be found") + var betterQuotas []QuotaSpec + for _, v := range betterQuotasMap { + betterQuotas = append(betterQuotas, *v) } - userQuotaJSON, _ := json.Marshal(userQuota) - f.Logger.Debug(fmt.Sprintf("Selected user quota : %#v", string(userQuotaJSON))) + return betterQuotas +} - userQuotaSpec = &QuotaSpec{billingModel, int(userQuota.GetAllowed() - userQuota.GetConsumed()), billingModel, userQuota.CloudAccounts} +// PickInstanceType - Standard instance always wins! +// This function should not exist but it does represents some requirement +// from business to only pick one instance type when two are presented. +// When standard instance type is present in user instances it should always take precedence +func PickInstanceType(amsTypes []QuotaSpec) *QuotaSpec { + if amsTypes == nil || len(amsTypes) == 0 { + return nil + } + + for _, amsType := range amsTypes { + if amsType.Name == QuotaStandardType { + return &amsType + } + } - return userQuotaSpec, nil + // There is chance of having multiple instances in the future + // We will pick the first one as we do not know which one to pick + return &amsTypes[0] } func GetOrganizationID(ctx context.Context, conn connection.Connection) (accountID string, err error) { @@ -152,12 +146,28 @@ func GetOrganizationID(ctx context.Context, conn connection.Connection) (account return account.Organization.GetId(), nil } -func GetValidMarketplaceAcctIDs(userQuotaType *QuotaSpec, marketplace string) (marketplaceAcctIDs []string, err error) { +func GetValidMarketplaceAcctIDs(ctx context.Context, connectionFunc factory.ConnectionFunc, marketplace string) (marketplaceAcctIDs []string, err error) { - for _, cloudAccount := range *userQuotaType.CloudAccounts { - if marketplace != "" { - if cloudAccount.GetCloudProviderId() == marketplace { - marketplaceAcctIDs = append(marketplaceAcctIDs, cloudAccount.GetCloudAccountId()) + conn, err := connectionFunc(connection.DefaultConfigSkipMasAuth) + if err != nil { + return nil, err + } + + quotaCostGet, err := fetchOrgQuotaCost(ctx, conn) + if err != nil { + return nil, err + } + + for _, quota := range quotaCostGet.GetItems() { + if len(quota.GetCloudAccounts()) > 0 { + for _, cloudAccount := range quota.GetCloudAccounts() { + if marketplace != "" { + if cloudAccount.GetCloudProviderId() == marketplace { + marketplaceAcctIDs = append(marketplaceAcctIDs, cloudAccount.GetCloudAccountId()) + } + } else { + marketplaceAcctIDs = append(marketplaceAcctIDs, cloudAccount.GetCloudAccountId()) + } } } } @@ -165,10 +175,24 @@ func GetValidMarketplaceAcctIDs(userQuotaType *QuotaSpec, marketplace string) (m return unique(marketplaceAcctIDs), err } -func GetValidMarketplaces(userQuotaType *QuotaSpec) (marketplaces []string, err error) { +func GetValidMarketplaces(ctx context.Context, connectionFunc factory.ConnectionFunc) (marketplaces []string, err error) { + + conn, err := connectionFunc(connection.DefaultConfigSkipMasAuth) + if err != nil { + return nil, err + } - for _, cloudAccount := range *userQuotaType.CloudAccounts { - marketplaces = append(marketplaces, cloudAccount.GetCloudProviderId()) + quotaCostGet, err := fetchOrgQuotaCost(ctx, conn) + if err != nil { + return nil, err + } + + for _, quota := range quotaCostGet.GetItems() { + if len(quota.GetCloudAccounts()) > 0 { + for _, cloudAccount := range quota.GetCloudAccounts() { + marketplaces = append(marketplaces, cloudAccount.GetCloudProviderId()) + } + } } return unique(marketplaces), err diff --git a/pkg/shared/accountmgmtutil/api.go b/pkg/shared/accountmgmtutil/api.go index 2ca6ec5d7..f67b971e4 100644 --- a/pkg/shared/accountmgmtutil/api.go +++ b/pkg/shared/accountmgmtutil/api.go @@ -4,7 +4,6 @@ package accountmgmtutil type QuotaType = string const ( - QuotaTrialType QuotaType = "trial" - QuotaStandardType QuotaType = "standard" - QuotaMarketplaceType QuotaType = "marketplace" + QuotaTrialType QuotaType = "trial" + QuotaStandardType QuotaType = "standard" )