diff --git a/docs/commands/rhoas_kafka_create.md b/docs/commands/rhoas_kafka_create.md index 4ee666084..d4110dc9f 100644 --- a/docs/commands/rhoas_kafka_create.md +++ b/docs/commands/rhoas_kafka_create.md @@ -30,6 +30,7 @@ $ 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 fbece4083..036f47d8b 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.0 + github.com/redhat-developer/app-services-sdk-go/kafkamgmt v0.12.1 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 10f5d2b34..d7d9a3acc 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.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/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/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,6 +933,7 @@ 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 882930a85..63d0ff818 100644 --- a/pkg/cmd/kafka/create/api_validators.go +++ b/pkg/cmd/kafka/create/api_validators.go @@ -3,6 +3,7 @@ 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" @@ -13,10 +14,9 @@ import ( ) type ValidatorInput struct { - provider string - region string - size string - + provider string + region string + size string userAMSInstanceType *accountmgmtutil.QuotaSpec f *factory.Factory @@ -24,6 +24,8 @@ 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") @@ -108,7 +110,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 } @@ -119,3 +121,19 @@ 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 284c1833b..6ef476f9d 100644 --- a/pkg/cmd/kafka/create/completions.go +++ b/pkg/cmd/kafka/create/completions.go @@ -1,10 +1,7 @@ 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" ) @@ -26,48 +23,3 @@ 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 766a01200..c1115a893 100644 --- a/pkg/cmd/kafka/create/create.go +++ b/pkg/cmd/kafka/create/create.go @@ -52,6 +52,7 @@ type options struct { marketplaceAcctId string marketplace string + billingModel string outputFormat string autoUse bool @@ -93,7 +94,7 @@ func NewCreateCommand(f *factory.Factory) *cobra.Command { } } - if opts.bypassChecks && (opts.marketplace != "" || opts.marketplaceAcctId != "") { + if opts.bypassChecks && (opts.marketplace != "" || opts.marketplaceAcctId != "" || opts.billingModel != "") { return f.Localizer.MustLocalizeError("kafka.create.error.bypassChecks.marketplace") } @@ -111,26 +112,6 @@ 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) }, } @@ -147,6 +128,7 @@ 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) { @@ -157,18 +139,6 @@ 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 } @@ -212,10 +182,16 @@ func runCreate(opts *options) error { return nil } - userInstanceType, err = accountmgmtutil.GetUserSupportedInstanceType(f.Context, &constants.Kafka.Ams, conn) - if err != nil || userInstanceType == nil { - return f.Localizer.MustLocalizeError("kafka.create.error.userInstanceType.notFound") + err = ValidateBillingModel(opts.billingModel) + if err != nil { + return err } + + userInstanceType, err = accountmgmtutil.FetchQuotaCost(f, opts.billingModel, opts.marketplaceAcctId, opts.marketplace, &constants.Kafka.Ams) + if err != nil { + return err + } + } var payload *kafkamgmtclient.KafkaRequestPayload @@ -225,7 +201,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 } @@ -249,6 +225,8 @@ 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 { @@ -271,7 +249,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 } @@ -400,7 +378,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{} @@ -438,7 +416,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 } @@ -474,29 +452,34 @@ func promptKafkaPayload(opts *options, userQuotaType accountmgmtutil.QuotaSpec) } } - marketplaces, err := accountmgmtutil.GetValidMarketplaces(f.Context, f.Connection) - if err != nil { - return nil, err - } - - if !opts.bypassChecks && len(marketplaces) > 0 { - if err = promptMarketplaceSelect(f.Localizer, marketplaces, answers); err != nil { - return nil, err - } + if !opts.bypassChecks && opts.billingModel == accountmgmtutil.QuotaMarketplaceType { - marketplaceAcctIDs, err := accountmgmtutil.GetValidMarketplaceAcctIDs(f.Context, f.Connection, answers.Marketplace) + marketplaces, err := accountmgmtutil.GetValidMarketplaces(userQuotaType) if err != nil { return nil, err } - if len(marketplaceAcctIDs) > 0 { - if err = promptMarketplaceAcctIDSelect(f.Localizer, marketplaceAcctIDs, answers); err != nil { + if len(marketplaces) > 0 { + + if err = promptMarketplaceSelect(f.Localizer, marketplaces, answers); err != nil { + return nil, err + } + + marketplaceAcctIDs, err := accountmgmtutil.GetValidMarketplaceAcctIDs(userQuotaType, answers.Marketplace) + if err != nil { return nil, err } - } - accountIDNullable.Set(&answers.MarketplaceAcctID) - marketplaceProviderNullable.Set(&answers.Marketplace) + if len(marketplaceAcctIDs) > 0 { + if err = promptMarketplaceAcctIDSelect(f.Localizer, marketplaceAcctIDs, answers); err != nil { + return nil, err + } + } + + accountIDNullable.Set(&answers.MarketplaceAcctID) + marketplaceProviderNullable.Set(&answers.Marketplace) + + } } payload := &kafkamgmtclient.KafkaRequestPayload{ @@ -507,7 +490,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 eea60feda..23439d820 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 3218b898b..f5b7848c4 100644 --- a/pkg/cmd/kafka/kafkacmdutil/kafka_util.go +++ b/pkg/cmd/kafka/kafkacmdutil/kafka_util.go @@ -15,6 +15,13 @@ 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 45dafc0fe..1dbf4be57 100644 --- a/pkg/core/localize/locales/en/cmd/kafka.en.toml +++ b/pkg/core/localize/locales/en/cmd/kafka.en.toml @@ -286,6 +286,9 @@ 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 b267c1f87..8256d25b6 100644 --- a/pkg/shared/accountmgmtutil/ams.go +++ b/pkg/shared/accountmgmtutil/ams.go @@ -2,7 +2,9 @@ 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" @@ -36,20 +38,10 @@ 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 -} - -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 + Name string + Quota int + BillingModel string + CloudAccounts *[]amsclient.CloudAccount } func fetchOrgQuotaCost(ctx context.Context, conn connection.Connection) (*amsclient.QuotaCostList, error) { @@ -68,72 +60,86 @@ func fetchOrgQuotaCost(ctx context.Context, conn connection.Connection) (*amscli } -func GetUserSupportedInstanceTypes(ctx context.Context, spec *remote.AmsConfig, conn connection.Connection) (quota []QuotaSpec, err error) { +func FetchQuotaCost(f *factory.Factory, billingModel string, cloudAccountID string, marketplace string, spec *remote.AmsConfig) (userQuotaSpec *QuotaSpec, err error) { - quotaCostGet, err := fetchOrgQuotaCost(ctx, conn) - if err != nil { + var conn connection.Connection + if conn, err = f.Connection(connection.DefaultConfigSkipMasAuth); err != nil { return nil, err } - 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 billingModel == QuotaStandardType && (cloudAccountID != "" || marketplace != "") { + return nil, errors.New("accountID cant be provided with standard billing model") } - return BattleOfInstanceBillingModels(quotas), err -} + if (cloudAccountID != "") != (marketplace != "") { + return nil, errors.New("accountID and marketplace should be provided together") + } -// 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 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) + 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 betterQuotas []QuotaSpec - for _, v := range betterQuotasMap { - betterQuotas = append(betterQuotas, *v) + + if len(filteredQuotaCosts) == 0 { + return nil, errors.New("no quota object is available") } - return betterQuotas -} + filteredQuotasJSON, _ := json.Marshal(filteredQuotaCosts) + f.Logger.Debug(fmt.Sprintf("Filtered Quotas : %#v", string(filteredQuotasJSON))) -// 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 - } + if billingModel == QuotaMarketplaceType { + + if len(filteredQuotaCosts) > 1 && marketplace == "" && cloudAccountID == "" { + return nil, errors.New("please specify marketplace provider and account id") + } - for _, amsType := range amsTypes { - if amsType.Name == QuotaStandardType { - return &amsType + 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] } - // 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] + if userQuota.GetQuotaId() == "" { + return nil, errors.New("quota could not be found") + } + + userQuotaJSON, _ := json.Marshal(userQuota) + f.Logger.Debug(fmt.Sprintf("Selected user quota : %#v", string(userQuotaJSON))) + + userQuotaSpec = &QuotaSpec{billingModel, int(userQuota.GetAllowed() - userQuota.GetConsumed()), billingModel, userQuota.CloudAccounts} + + return userQuotaSpec, nil } func GetOrganizationID(ctx context.Context, conn connection.Connection) (accountID string, err error) { @@ -146,28 +152,12 @@ func GetOrganizationID(ctx context.Context, conn connection.Connection) (account return account.Organization.GetId(), nil } -func GetValidMarketplaceAcctIDs(ctx context.Context, connectionFunc factory.ConnectionFunc, marketplace string) (marketplaceAcctIDs []string, err error) { +func GetValidMarketplaceAcctIDs(userQuotaType *QuotaSpec, marketplace string) (marketplaceAcctIDs []string, err error) { - 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()) - } + for _, cloudAccount := range *userQuotaType.CloudAccounts { + if marketplace != "" { + if cloudAccount.GetCloudProviderId() == marketplace { + marketplaceAcctIDs = append(marketplaceAcctIDs, cloudAccount.GetCloudAccountId()) } } } @@ -175,24 +165,10 @@ func GetValidMarketplaceAcctIDs(ctx context.Context, connectionFunc factory.Conn return unique(marketplaceAcctIDs), err } -func GetValidMarketplaces(ctx context.Context, connectionFunc factory.ConnectionFunc) (marketplaces []string, err error) { - - conn, err := connectionFunc(connection.DefaultConfigSkipMasAuth) - if err != nil { - return nil, err - } +func GetValidMarketplaces(userQuotaType *QuotaSpec) (marketplaces []string, err error) { - 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()) - } - } + for _, cloudAccount := range *userQuotaType.CloudAccounts { + marketplaces = append(marketplaces, cloudAccount.GetCloudProviderId()) } return unique(marketplaces), err diff --git a/pkg/shared/accountmgmtutil/api.go b/pkg/shared/accountmgmtutil/api.go index f67b971e4..2ca6ec5d7 100644 --- a/pkg/shared/accountmgmtutil/api.go +++ b/pkg/shared/accountmgmtutil/api.go @@ -4,6 +4,7 @@ package accountmgmtutil type QuotaType = string const ( - QuotaTrialType QuotaType = "trial" - QuotaStandardType QuotaType = "standard" + QuotaTrialType QuotaType = "trial" + QuotaStandardType QuotaType = "standard" + QuotaMarketplaceType QuotaType = "marketplace" )