Skip to content

Commit

Permalink
ROX-16587: API for switching billing model of a central (#1598)
Browse files Browse the repository at this point in the history
parametalol authored Mar 26, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 9b0da11 commit d13db4f
Showing 17 changed files with 566 additions and 67 deletions.
6 changes: 3 additions & 3 deletions .secrets.baseline
Original file line number Diff line number Diff line change
@@ -295,14 +295,14 @@
"filename": "internal/dinosaur/pkg/services/dinosaurservice_moq.go",
"hashed_secret": "44e17306b837162269a410204daaa5ecee4ec22c",
"is_verified": false,
"line_number": 982
"line_number": 1056
},
{
"type": "Secret Keyword",
"filename": "internal/dinosaur/pkg/services/dinosaurservice_moq.go",
"hashed_secret": "d035c0406b3e8286d3427e91db3497e0e17f0f83",
"is_verified": false,
"line_number": 983
"line_number": 1057
}
],
"pkg/client/fleetmanager/mocks/client_moq.go": [
@@ -445,5 +445,5 @@
}
]
},
"generated_at": "2024-03-14T15:25:55Z"
"generated_at": "2024-03-21T17:47:35Z"
}
71 changes: 71 additions & 0 deletions internal/dinosaur/pkg/api/admin/private/api/openapi.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

108 changes: 108 additions & 0 deletions internal/dinosaur/pkg/api/admin/private/api_default.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 14 additions & 13 deletions internal/dinosaur/pkg/converters/dinosaur_request.go
Original file line number Diff line number Diff line change
@@ -9,19 +9,20 @@ func ConvertDinosaurRequest(request *dbapi.CentralRequest) []map[string]interfac
traits, _ := request.Traits.Value()
return []map[string]interface{}{
{
"id": request.ID,
"region": request.Region,
"cloud_provider": request.CloudProvider,
"multi_az": request.MultiAZ,
"name": request.Name,
"status": request.Status,
"owner": request.Owner,
"cluster_id": request.ClusterID,
"host": request.Host,
"created_at": request.Meta.CreatedAt,
"updated_at": request.Meta.UpdatedAt,
"deleted_at": request.Meta.DeletedAt.Time,
"traits": traits,
"id": request.ID,
"region": request.Region,
"cloud_provider": request.CloudProvider,
"multi_az": request.MultiAZ,
"name": request.Name,
"status": request.Status,
"owner": request.Owner,
"cluster_id": request.ClusterID,
"host": request.Host,
"created_at": request.Meta.CreatedAt,
"updated_at": request.Meta.UpdatedAt,
"deleted_at": request.Meta.DeletedAt.Time,
"traits": traits,
"subscription_id": request.SubscriptionID,
},
}
}
16 changes: 16 additions & 0 deletions internal/dinosaur/pkg/handlers/admin_dinosaur.go
Original file line number Diff line number Diff line change
@@ -53,6 +53,7 @@ type AdminCentralHandler interface {
// a tenant. In particular, avoid two Central CRs appearing in the same
// tenant namespace. This may cause conflicts due to mixed resource ownership.
PatchName(w http.ResponseWriter, r *http.Request)

// ListTraits returns all central traits
ListTraits(w http.ResponseWriter, r *http.Request)
// GetTrait tells wheter a central has the trait
@@ -61,6 +62,9 @@ type AdminCentralHandler interface {
AddTrait(w http.ResponseWriter, r *http.Request)
// DeleteTrait deletes a trait from a central
DeleteTrait(w http.ResponseWriter, r *http.Request)

// PatchBillingParameters changes the billing model of a central
PatchBillingParameters(w http.ResponseWriter, r *http.Request)
}

type adminCentralHandler struct {
@@ -387,3 +391,15 @@ func (h adminCentralHandler) DeleteTrait(w http.ResponseWriter, r *http.Request)
}
handlers.HandleDelete(w, r, cfg, http.StatusOK)
}

func (h adminCentralHandler) PatchBillingParameters(w http.ResponseWriter, r *http.Request) {
var request *private.CentralBillingChangeRequest
cfg := &handlers.HandlerConfig{
MarshalInto: &request,
Action: func() (i interface{}, serviceError *errors.ServiceError) {
return nil, h.service.ChangeBillingParameters(r.Context(), mux.Vars(r)["id"],
request.Model, request.CloudAccountId, request.CloudProvider, request.Product)
},
}
handlers.Handle(w, r, cfg, http.StatusOK)
}
3 changes: 3 additions & 0 deletions internal/dinosaur/pkg/routes/route_loader.go
Original file line number Diff line number Diff line change
@@ -259,6 +259,9 @@ func (s *options) buildAPIBaseRouter(mainRouter *mux.Router, basePath string, op
adminCentralsRouter.HandleFunc("/{id}/name", adminCentralHandler.PatchName).
Name(logger.NewLogEvent("admin-name", "[admin] set `name` central property").ToString()).
Methods(http.MethodPatch)
adminCentralsRouter.HandleFunc("/{id}/billing", adminCentralHandler.PatchBillingParameters).
Name(logger.NewLogEvent("admin-billing", "[admin] change central billing parameters").ToString()).
Methods(http.MethodPatch)

adminCentralsRouter.HandleFunc("/{id}/traits", adminCentralHandler.ListTraits).
Name(logger.NewLogEvent("admin-list-traits", "[admin] list central traits").ToString()).
63 changes: 60 additions & 3 deletions internal/dinosaur/pkg/services/dinosaur.go
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"fmt"
"reflect"
"sync"
"time"

@@ -114,6 +115,7 @@ type DinosaurService interface {
// This is currently the only way to update secret backups, an automatic approach should be implemented
// to accomated for regular processes like central TLS cert rotation.
ResetCentralSecretBackup(ctx context.Context, centralRequest *dbapi.CentralRequest) *errors.ServiceError
ChangeBillingParameters(ctx context.Context, centralID string, billingModel string, cloudAccountID string, cloudProvider string, product string) *errors.ServiceError
}

var _ DinosaurService = &dinosaurService{}
@@ -233,7 +235,7 @@ func (k *dinosaurService) DetectInstanceType(dinosaurRequest *dbapi.CentralReque
}

// reserveQuota - reserves quota for the given dinosaur request. If a RHACS quota has been assigned, it will try to reserve RHACS quota, otherwise it will try with RHACSTrial
func (k *dinosaurService) reserveQuota(ctx context.Context, dinosaurRequest *dbapi.CentralRequest) (subscriptionID string, err *errors.ServiceError) {
func (k *dinosaurService) reserveQuota(ctx context.Context, dinosaurRequest *dbapi.CentralRequest, bm string, product string) (subscriptionID string, err *errors.ServiceError) {
if dinosaurRequest.InstanceType == types.EVAL.String() &&
!(environments.GetEnvironmentStrFromEnv() == environments.DevelopmentEnv || environments.GetEnvironmentStrFromEnv() == environments.TestingEnv) {
if !k.dinosaurConfig.Quota.AllowEvaluatorInstance {
@@ -261,7 +263,7 @@ func (k *dinosaurService) reserveQuota(ctx context.Context, dinosaurRequest *dba
if factoryErr != nil {
return "", errors.NewWithCause(errors.ErrorGeneral, factoryErr, "unable to check quota")
}
subscriptionID, err = quotaService.ReserveQuota(ctx, dinosaurRequest, types.DinosaurInstanceType(dinosaurRequest.InstanceType))
subscriptionID, err = quotaService.ReserveQuota(ctx, dinosaurRequest, bm, product)
return subscriptionID, err
}

@@ -291,7 +293,7 @@ func (k *dinosaurService) RegisterDinosaurJob(ctx context.Context, dinosaurReque
return errors.TooManyDinosaurInstancesReached(fmt.Sprintf("Region %s cannot accept instance type: %s at this moment", dinosaurRequest.Region, dinosaurRequest.InstanceType))
}
dinosaurRequest.ClusterID = cluster.ClusterID
subscriptionID, err := k.reserveQuota(ctx, dinosaurRequest)
subscriptionID, err := k.reserveQuota(ctx, dinosaurRequest, "", "")
if err != nil {
return err
}
@@ -1038,3 +1040,58 @@ func convertCentralRequestToString(req *dbapi.CentralRequest) string {
}
return fmt.Sprintf("%+v", requestAsMap)
}

type billingParameters struct {
cloudAccountID string
cloudProvider string
subscriptionID string
instanceType string
product string
}

func makeBillingParameters(central *dbapi.CentralRequest) *billingParameters {
return &billingParameters{
cloudAccountID: central.CloudAccountID,
cloudProvider: central.CloudProvider,
subscriptionID: central.SubscriptionID,
instanceType: central.InstanceType,
product: types.DinosaurInstanceType(central.InstanceType).GetQuotaType().GetProduct(),
}
}

func (k *dinosaurService) ChangeBillingParameters(ctx context.Context, centralID string, billingModel string, cloudAccountID string, cloudProvider string, product string) *errors.ServiceError {
centralRequest, svcErr := k.GetByID(centralID)
if svcErr != nil {
return svcErr
}

original := makeBillingParameters(centralRequest)

centralRequest.CloudAccountID = cloudAccountID
centralRequest.CloudProvider = cloudProvider

// Changing product is allowed (by OCM) only from RHACSTrial to RHACS today.
// This change should also change the instance type.
if original.product == string(ocm.RHACSTrialProduct) && product == string(ocm.RHACSProduct) {
centralRequest.InstanceType = string(types.STANDARD)
}

newSubscriptionID, svcErr := k.reserveQuota(ctx, centralRequest, billingModel, product)
updated := makeBillingParameters(centralRequest)
if svcErr != nil {
glog.Errorf("Failed to reserve quota with updated billing parameters (%+v): %v", updated, svcErr)
return svcErr
}
updated.subscriptionID = newSubscriptionID

if !reflect.DeepEqual(original, updated) {
if svcErr = k.UpdateIgnoreNils(centralRequest); svcErr != nil {
glog.Errorf("Failed to update central %q record with updated billing parameters (%v): %v", centralID, updated, svcErr)
return svcErr
}
glog.Infof("Central %q billing parameters have been changed from %v to %v", centralID, original, updated)
} else {
glog.Infof("Central %q has no change in billing parameters")
}
return nil
}
59 changes: 59 additions & 0 deletions internal/dinosaur/pkg/services/dinosaur_test.go
Original file line number Diff line number Diff line change
@@ -11,10 +11,13 @@ import (
"github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/dbapi"
"github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/config"
"github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/converters"
"github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/dinosaurs/types"
"github.com/stackrox/acs-fleet-manager/pkg/api"
"github.com/stackrox/acs-fleet-manager/pkg/auth"
"github.com/stackrox/acs-fleet-manager/pkg/db"
"github.com/stackrox/acs-fleet-manager/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
)

@@ -243,3 +246,59 @@ func Test_dinosaurService_RestoreExpiredDinosaurs(t *testing.T) {
assert.True(t, updateQuery.Triggered)
assert.True(t, expiredChecked)
}

func Test_dinosaurService_ChangeBillingParameters(t *testing.T) {
quotaService := &QuotaServiceMock{
HasQuotaAllowanceFunc: func(dinosaur *dbapi.CentralRequest, instanceType types.DinosaurInstanceType) (bool, *errors.ServiceError) {
return true, nil
},
ReserveQuotaFunc: func(ctx context.Context, dinosaur *dbapi.CentralRequest, _ string, _ string) (string, *errors.ServiceError) {
return dinosaur.SubscriptionID, nil
},
}
quotaServiceFactory := &QuotaServiceFactoryMock{
GetQuotaServiceFunc: func(quotaType api.QuotaType) (QuotaService, *errors.ServiceError) {
return quotaService, nil
},
}
k := &dinosaurService{
dinosaurConfig: config.NewCentralConfig(),
connectionFactory: db.NewMockConnectionFactory(nil),
quotaServiceFactory: quotaServiceFactory,
}
central := buildCentralRequest(func(centralRequest *dbapi.CentralRequest) {
centralRequest.QuotaType = "standard"
centralRequest.OrganisationID = "original org ID"
centralRequest.CloudProvider = ""
centralRequest.CloudAccountID = ""
centralRequest.SubscriptionID = "original subscription ID"
})

catcher := mocket.Catcher.Reset()
m0 := catcher.NewMock().WithQuery(`SELECT * FROM "central_requests" ` +
`WHERE id = $1 AND "central_requests"."deleted_at" IS NULL ` +
`ORDER BY "central_requests"."id" LIMIT 1`).
OneTime().WithArgs(testID).
WithReply(converters.ConvertDinosaurRequest(central))
m1 := catcher.NewMock().WithQuery(`UPDATE "central_requests" ` +
`SET "updated_at"=$1,"deleted_at"=$2,"region"=$3,"cluster_id"=$4,` +
`"cloud_provider"=$5,"cloud_account_id"=$6,"name"=$7,"subscription_id"=$8,"owner"=$9 ` +
`WHERE status not IN ($10,$11) AND "central_requests"."deleted_at" IS NULL AND "id" = $12`).
OneTime()

svcErr := k.ChangeBillingParameters(context.Background(), central.ID, "marketplace", "aws_account_id", "aws", "")
assert.Nil(t, svcErr)

assert.True(t, m0.Triggered)
assert.True(t, m1.Triggered)

qsfCalls := quotaServiceFactory.GetQuotaServiceCalls()
require.Len(t, qsfCalls, 1)

reserveQuotaCalls := quotaService.ReserveQuotaCalls()
require.Len(t, reserveQuotaCalls, 1)
assert.Equal(t, testID, reserveQuotaCalls[0].Dinosaur.ID)

deleteQuotaCalls := quotaService.DeleteQuotaCalls()
require.Len(t, deleteQuotaCalls, 0)
}
74 changes: 74 additions & 0 deletions internal/dinosaur/pkg/services/dinosaurservice_moq.go
2 changes: 1 addition & 1 deletion internal/dinosaur/pkg/services/quota.go
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ type QuotaService interface {
// HasQuotaAllowance checks if allowed quota is not zero for the given instance type
HasQuotaAllowance(dinosaur *dbapi.CentralRequest, instanceType types.DinosaurInstanceType) (bool, *errors.ServiceError)
// ReserveQuota reserves a quota for a user and return the reservation id or an error in case of failure
ReserveQuota(ctx context.Context, dinosaur *dbapi.CentralRequest, instanceType types.DinosaurInstanceType) (string, *errors.ServiceError)
ReserveQuota(ctx context.Context, dinosaur *dbapi.CentralRequest, forcedBillingModel string, forcedProduct string) (string, *errors.ServiceError)
// DeleteQuota deletes a reserved quota
DeleteQuota(subscriptionID string) *errors.ServiceError
}
52 changes: 35 additions & 17 deletions internal/dinosaur/pkg/services/quota/ams_quota_service.go
Original file line number Diff line number Diff line change
@@ -74,16 +74,17 @@ func (q amsQuotaService) HasQuotaAllowance(central *dbapi.CentralRequest, instan
return true, nil
}

// selectBillingModelFromDinosaurInstanceType select the billing model of a
// dinosaur instance type by looking at the resource name and product of the
// instanceType, as well as cloudAccountID and cloudProviderID. Only QuotaCosts that have available quota, or that contain a
// RelatedResource with "cost" 0 are considered. Only
// "standard" and "marketplace" and "marketplace-aws" billing models are considered.
// If both marketplace and standard billing models are available, marketplace will be given preference.
func (q amsQuotaService) selectBillingModelFromDinosaurInstanceType(orgID, cloudProviderID, cloudAccountID string, instanceType types.DinosaurInstanceType) (string, error) {
quotaCosts, err := q.amsClient.GetQuotaCostsForProduct(orgID, instanceType.GetQuotaType().GetResourceName(), instanceType.GetQuotaType().GetProduct())
// selectBillingModel selects the billing model of an instance by looking
// at the resource name and product, cloudProviderID and cloudAccountID.
// Only QuotaCosts that have available quota, or that contain a RelatedResource
// with "cost" 0 are considered.
// Only "standard", "marketplace" and "marketplace-aws" billing models are
// considered. If both "marketplace" and "standard" billing models are
// available, "marketplace" will be given preference.
func (q amsQuotaService) selectBillingModel(orgID, cloudProviderID, cloudAccountID string, resourceName string, product string) (string, error) {
quotaCosts, err := q.amsClient.GetQuotaCostsForProduct(orgID, resourceName, product)
if err != nil {
return "", errors.InsufficientQuotaError("%v: error getting quotas for product %s", err, instanceType.GetQuotaType().GetProduct())
return "", errors.InsufficientQuotaError("%v: error getting quotas for product %s", err, product)
}

hasBillingModelMarketplace := false
@@ -114,8 +115,10 @@ func (q amsQuotaService) selectBillingModelFromDinosaurInstanceType(orgID, cloud
return "", errors.InsufficientQuotaError("No available billing model found")
}

// ReserveQuota ...
func (q amsQuotaService) ReserveQuota(ctx context.Context, dinosaur *dbapi.CentralRequest, instanceType types.DinosaurInstanceType) (string, *errors.ServiceError) {
// ReserveQuota calls AMS to reserve quota for the central request. It computes
// the central billing parameters if they're not forced.
func (q amsQuotaService) ReserveQuota(ctx context.Context, dinosaur *dbapi.CentralRequest, forcedBillingModel string, forcedProduct string) (string, *errors.ServiceError) {
instanceType := types.DinosaurInstanceType(dinosaur.InstanceType)
dinosaurID := dinosaur.ID
rr := newBaseQuotaReservedResourceResourceBuilder()

@@ -129,10 +132,22 @@ func (q amsQuotaService) ReserveQuota(ctx context.Context, dinosaur *dbapi.Centr
if err != nil {
return "", errors.OrganisationNotFound(dinosaur.OrganisationID, err)
}
bm, err := q.selectBillingModelFromDinosaurInstanceType(org.ID(), dinosaur.CloudProvider, dinosaur.CloudAccountID, instanceType)
if err != nil {
svcErr := errors.ToServiceError(err)
return "", errors.NewWithCause(svcErr.Code, svcErr, "Error getting billing model")

product := instanceType.GetQuotaType().GetProduct()
if forcedProduct != "" {
product = forcedProduct
}

var bm string
if forcedBillingModel == "" {
resourceName := instanceType.GetQuotaType().GetResourceName()
bm, err = q.selectBillingModel(org.ID(), dinosaur.CloudProvider, dinosaur.CloudAccountID, resourceName, product)
if err != nil {
svcErr := errors.ToServiceError(err)
return "", errors.NewWithCause(svcErr.Code, svcErr, "Error getting billing model")
}
} else {
bm = forcedBillingModel
}
rr.BillingModel(amsv1.BillingModel(bm))
glog.Infof("Billing model of Central request %q with quota type %q has been set to %q.", dinosaur.ID, instanceType.GetQuotaType(), bm)
@@ -141,13 +156,16 @@ func (q amsQuotaService) ReserveQuota(ctx context.Context, dinosaur *dbapi.Centr
if err := q.verifyCloudAccountInAMS(dinosaur, org.ID()); err != nil {
return "", err
}
rr.BillingMarketplaceAccount(dinosaur.CloudAccountID)
if bm != string(amsv1.BillingModelMarketplace) &&
bm != string(amsv1.BillingModelMarketplaceRHM) {
rr.BillingMarketplaceAccount(dinosaur.CloudAccountID)
}
}

requestBuilder := amsv1.NewClusterAuthorizationRequest().
AccountUsername(dinosaur.Owner).
CloudProviderID(dinosaur.CloudProvider).
ProductID(instanceType.GetQuotaType().GetProduct()).
ProductID(product).
Managed(true).
ClusterID(dinosaurID).
ExternalClusterID(dinosaurID).
21 changes: 12 additions & 9 deletions internal/dinosaur/pkg/services/quota/ams_quota_service_test.go
Original file line number Diff line number Diff line change
@@ -197,7 +197,8 @@ func Test_AMSCheckQuota(t *testing.T) {
Meta: api.Meta{
ID: tt.args.dinosaurID,
},
Owner: tt.args.owner,
Owner: tt.args.owner,
InstanceType: string(tt.args.dinosaurInstanceType),
}
standardAllowance, err := quotaService.HasQuotaAllowance(dinosaur, types.STANDARD)
gomega.Expect(err).ToNot(gomega.HaveOccurred())
@@ -206,7 +207,7 @@ func Test_AMSCheckQuota(t *testing.T) {
gomega.Expect(standardAllowance).To(gomega.Equal(tt.args.hasStandardQuota))
gomega.Expect(evalAllowance).To(gomega.Equal(tt.args.hasEvalQuota))

_, err = quotaService.ReserveQuota(emptyCtx, dinosaur, tt.args.dinosaurInstanceType)
_, err = quotaService.ReserveQuota(emptyCtx, dinosaur, "", "")
gomega.Expect(err != nil).To(gomega.Equal(tt.wantErr))
})
}
@@ -573,9 +574,10 @@ func Test_AMSReserveQuota(t *testing.T) {
{
name: "cloud account matches cloud_accounts response results in successful call",
args: args{
dinosaurID: "12231",
owner: "testUser",
cloudAccountID: "cloudAccountID",
dinosaurID: "12231",
owner: "testUser",
cloudAccountID: "cloudAccountID",
cloudProviderID: "aws",
},
fields: fields{
ocmClient: &ocmClientMock.ClientMock{
@@ -587,23 +589,23 @@ func Test_AMSReserveQuota(t *testing.T) {
return org, nil
},
GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) {
rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocmImpl.RHACSTrialProduct)).ResourceName(resourceName).Cost(0)
rrbq1 := v1.NewRelatedResource().CloudProvider("aws").BillingModel(string(v1.BillingModelMarketplaceAWS)).Product(string(ocmImpl.RHACSTrialProduct)).ResourceName(resourceName).Cost(0)
qcb1, err := v1.NewQuotaCost().Allowed(0).Consumed(2).OrganizationID(organizationID).RelatedResources(rrbq1).Build()
require.NoError(t, err)
return []*v1.QuotaCost{qcb1}, nil
},
GetCustomerCloudAccountsFunc: func(externalID string, quotaIDs []string) ([]*v1.CloudAccount, error) {
cloudAccount, _ := v1.NewCloudAccount().
CloudAccountID("cloudAccountID").
CloudProviderID("cloudProviderID").
CloudProviderID("aws").
Build()
return []*v1.CloudAccount{
cloudAccount,
}, nil
},
},
},
wantBillingModel: string(v1.BillingModelMarketplace),
wantBillingModel: string(v1.BillingModelMarketplaceAWS),
wantBillingMarketplaceAccount: "cloudAccountID",
want: "1234",
wantErr: false,
@@ -659,10 +661,11 @@ func Test_AMSReserveQuota(t *testing.T) {
ID: tt.args.dinosaurID,
},
Owner: tt.args.owner,
InstanceType: string(types.STANDARD),
CloudAccountID: tt.args.cloudAccountID,
CloudProvider: utils.IfThenElse(tt.args.cloudProviderID == "", "cloudProviderID", tt.args.cloudProviderID),
}
subID, err := quotaService.ReserveQuota(emptyCtx, dinosaur, types.STANDARD)
subID, err := quotaService.ReserveQuota(emptyCtx, dinosaur, "", "")
gomega.Expect(subID).To(gomega.Equal(tt.want))
gomega.Expect(err != nil).To(gomega.Equal(tt.wantErr))

Original file line number Diff line number Diff line change
@@ -45,7 +45,9 @@ func (q QuotaManagementListService) HasQuotaAllowance(dinosaur *dbapi.CentralReq
}

// ReserveQuota ...
func (q QuotaManagementListService) ReserveQuota(_ context.Context, dinosaur *dbapi.CentralRequest, instanceType types.DinosaurInstanceType) (string, *errors.ServiceError) {
func (q QuotaManagementListService) ReserveQuota(_ context.Context, dinosaur *dbapi.CentralRequest, _ string, _ string) (string, *errors.ServiceError) {
instanceType := types.DinosaurInstanceType(dinosaur.InstanceType)

if !q.quotaManagementList.EnableInstanceLimitControl {
return "", nil
}
Original file line number Diff line number Diff line change
@@ -370,8 +370,9 @@ func Test_QuotaManagementListReserveQuota(t *testing.T) {
dinosaur := &dbapi.CentralRequest{
Owner: "username",
OrganisationID: "org-id",
InstanceType: string(tt.args.instanceType),
}
_, err := quotaService.ReserveQuota(context.Background(), dinosaur, tt.args.instanceType)
_, err := quotaService.ReserveQuota(context.Background(), dinosaur, "", "")
gomega.Expect(tt.wantErr).To(gomega.Equal(err))
})
}
42 changes: 24 additions & 18 deletions internal/dinosaur/pkg/services/quotaservice_moq.go
63 changes: 62 additions & 1 deletion openapi/fleet-manager-private-admin.yaml
Original file line number Diff line number Diff line change
@@ -382,7 +382,46 @@ paths:
application/json:
schema:
$ref: 'fleet-manager.yaml#/components/schemas/Error'

'/api/rhacs/v1/admin/centrals/{id}/billing':
patch:
summary: Change central billing parameters
operationId: changeBillingParameters
parameters:
- $ref: "fleet-manager.yaml#/components/parameters/id"
requestBody:
description: Billing target parameters
content:
application/json:
schema:
$ref: "#/components/schemas/CentralBillingChangeRequest"
required: true
responses:
"200":
description: Billing parameters changed
"401":
description: Auth token is invalid
content:
application/json:
schema:
$ref: 'fleet-manager.yaml#/components/schemas/Error'
"403":
description: User is not authorised to access the service
content:
application/json:
schema:
$ref: 'fleet-manager.yaml#/components/schemas/Error'
"404":
description: No Central found with the specified ID or dynamic clients are not configured
content:
application/json:
schema:
$ref: 'fleet-manager.yaml#/components/schemas/Error'
"500":
description: Unexpected error occurred
content:
application/json:
schema:
$ref: 'fleet-manager.yaml#/components/schemas/Error'
'/api/rhacs/v1/admin/centrals/db/{id}':
delete:
summary: Delete a Central directly in the Database by ID
@@ -659,6 +698,28 @@ components:
type: string
reason:
type: string

CentralBillingChangeRequest:
type: object
properties:
model:
type: string
enum:
- standard
- marketplace
- marketplace-aws
- marketplace-gcp
- marketplace-rhm
- marketplace-azure
cloud_account_id:
type: string
cloud_provider:
type: string
product:
type: string
enum:
- RHACS

parameters:
trait:
name: trait

0 comments on commit d13db4f

Please sign in to comment.