Skip to content
This repository has been archived by the owner on Jan 29, 2025. It is now read-only.

Commit

Permalink
Merge pull request #1263 from anujbansal16/multi-region-support
Browse files Browse the repository at this point in the history
Added multiple region support for quota
  • Loading branch information
anujbansal16 authored Mar 12, 2021
2 parents ed609f0 + 32fd23a commit b55c27a
Show file tree
Hide file tree
Showing 21 changed files with 375 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ class ServiceBrokerApiController extends FabrikBaseController {
service_id: serviceId
}, contextLabels, namespaceLabel),
value => _.trim(value));

if(req.params.region) {
_.set(labels,'region', req.params.region);
}

_.forIn(labels, function(value, key) {
if (!isValidKubernetesLabelValue(value)) {
Expand Down Expand Up @@ -195,6 +199,14 @@ class ServiceBrokerApiController extends FabrikBaseController {
res.status(statusCode).send(body);
}

const labels = req.params.region ? { 'region':req.params.region } : undefined;

_.forIn(labels, function(value, key) {
if (!isValidKubernetesLabelValue(value)) {
throw new BadRequest(`Parameter ${key} value "${value}" must be a valid label value`);
}
});

function isUpdatePossible(previousPlanId) {
const previousPlan = _.find(plan.service.plans, ['id', previousPlanId]);
return plan === previousPlan || _.includes(plan.manager.settings.update_predecessors, previousPlan.id);
Expand Down Expand Up @@ -242,6 +254,7 @@ class ServiceBrokerApiController extends FabrikBaseController {
resourceGroup: CONST.APISERVER.RESOURCE_GROUPS.INTEROPERATOR,
resourceType: CONST.APISERVER.RESOURCE_TYPES.INTEROPERATOR_SERVICEINSTANCES,
resourceId: getKubernetesName(req.params.instance_id),
labels: labels,
spec: params,
status: {
state: CONST.APISERVER.RESOURCE_STATE.UPDATE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ exports.checkQuota = function () {
reqMethod: req.method
}
};
if(_.get(req.params, 'region') !== undefined) {
_.set(quotaClientOptions.queryParams, 'region', req.params.region);
}
const instanceBasedQuota = supportsInstanceBasedQuota(req.body.service_id);
if(!instanceBasedQuota) {
quotaClientOptions.data = _.cloneDeep(req.body);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ const router = module.exports = express.Router({
mergeParams: true
});
router.use('/v2', require('./v2'));
router.use('/region/:region/v2',require('./v2'));
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,59 @@ describe('service-broker-api-2.0', function () {
mocks.verify();
});
});

it('returns 202 Accepted -- fetching correct region value in labels', function () {
const testPayload = _.cloneDeep(payload);
testPayload.spec.plan_id = plan_id_custom_dashboard;
const testContext = {
platform: 'sapcp',
organization_guid: organization_guid,
space_guid: space_guid,
organization_name: 'test',
space_name: 'service-fabrik',
instance_name: 'bp-monitor',
landscape_label: 'cf-eu10-canary',
origin: 'cloudfoundry',
zone_id: 'service-fabrik',
global_account_id: '9808a7d5-5c36-4149-b62d-1095373bdfaa',
license_type: 'LSS script',
subaccount_id: 'service-fabrik',
subdomain: 'service-fabrik'
};
testPayload.spec.context = testContext;
testPayload.metadata.labels.region = 'eu10';
testPayload.spec = camelcaseKeys(testPayload.spec);
mocks.apiServerEventMesh.nockCreateResource(CONST.APISERVER.RESOURCE_GROUPS.LOCK, CONST.APISERVER.RESOURCE_TYPES.DEPLOYMENT_LOCKS, instance_id, {});
mocks.apiServerEventMesh.nockCreateResource(CONST.APISERVER.RESOURCE_GROUPS.INTEROPERATOR, CONST.APISERVER.RESOURCE_TYPES.INTEROPERATOR_SERVICEINSTANCES, {}, 1, testPayload);
mocks.apiServerEventMesh.nockGetResource(CONST.APISERVER.RESOURCE_GROUPS.INTEROPERATOR, CONST.APISERVER.RESOURCE_TYPES.INTEROPERATOR_SERVICEINSTANCES, instance_id, {});
mocks.apiServerEventMesh.nockGetResource(CONST.APISERVER.RESOURCE_GROUPS.INTEROPERATOR, CONST.APISERVER.RESOURCE_TYPES.INTEROPERATOR_SERVICEINSTANCES, instance_id, {
metadata:{
name: instance_id
},
spec: {
clusterId: 1,
planId: plan_id_custom_dashboard,
serviceId: service_id,
}
});
return chai.request(app)
.put(`/cf/region/eu10/v2/service_instances/${instance_id}?accepts_incomplete=true`)
.set('X-Broker-API-Version', api_version)
.auth(config.username, config.password)
.send({
service_id: service_id,
plan_id: plan_id_custom_dashboard,
context: testContext,
organization_guid: organization_guid,
space_guid: space_guid,
parameters: parameters
})
.then(res => {
expect(res).to.have.status(202);
expect(res.body.dashboard_url === dashboard_url_with_template);
mocks.verify();
});
});

it('returns UnprocessableEntity entity when dashboard template url does not evaluate to a valid URL', function () {
const oldTemp = config.services[0].plans[4].manager.settings.dashboard_url_template;
Expand Down Expand Up @@ -755,6 +808,54 @@ describe('service-broker-api-2.0', function () {
});
});

it('returns 202 Accepted if resource is already present- fetching correct region too', function () {
const context = {
platform: 'cloudfoundry',
organization_guid: organization_guid,
space_guid: space_guid
};
mocks.apiServerEventMesh.nockGetResource(CONST.APISERVER.RESOURCE_GROUPS.LOCK, CONST.APISERVER.RESOURCE_TYPES.DEPLOYMENT_LOCKS, instance_id, {
spec: {
options: JSON.stringify({
lockedResourceDetails: {
operation: 'update'
}
})
}
});
mocks.apiServerEventMesh.nockPatchResource(CONST.APISERVER.RESOURCE_GROUPS.LOCK, CONST.APISERVER.RESOURCE_TYPES.DEPLOYMENT_LOCKS, instance_id, {
metadata: {
resourceVersion: 10
}
});
const testPayload = _.cloneDeep(payload);
testPayload.spec = camelcaseKeys(payload.spec);
testPayload.metadata.labels.region = 'eu10';
mocks.apiServerEventMesh.nockPatchResource(CONST.APISERVER.RESOURCE_GROUPS.INTEROPERATOR, CONST.APISERVER.RESOURCE_TYPES.INTEROPERATOR_SERVICEINSTANCES, instance_id, {}, 1, { spec: { parameters: null } });
mocks.apiServerEventMesh.nockPatchResource(CONST.APISERVER.RESOURCE_GROUPS.INTEROPERATOR, CONST.APISERVER.RESOURCE_TYPES.INTEROPERATOR_SERVICEINSTANCES, instance_id, {}, 1, testPayload);
return chai.request(app)
.patch(`/cf/region/eu10/v2/service_instances/${instance_id}?accepts_incomplete=true`)
.send({
service_id: service_id,
plan_id: plan_id_update,
parameters: parameters,
context: context,
previous_values: {
plan_id: plan_id,
service_id: service_id
}
})
.set('X-Broker-API-Version', api_version)
.auth(config.username, config.password)
.then(res => {
mocks.verify();
expect(res).to.have.status(202);
expect(res.body.operation).to.deep.equal(commonFunctions.encodeBase64({
'type': 'update'
}));
});
});

it('returns 202 Accepted if resource is already present and plan_id is not present in the request', function () {
const context = {
platform: 'cloudfoundry',
Expand Down
7 changes: 4 additions & 3 deletions broker/applications/quota-app/src/QuotaApiController.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ class QuotaApiController extends FabrikBaseController {
case 4 : K8S + SM (CF + K8S) => subaccount based API and apiserver
*/
try {
const region = _.get(req, 'query.region');
const quotaManager = commonFunctions.isBrokerBoshDeployment() ?
quota.getQuotaManagerInstance(CONST.PLATFORM.CF) :
quota.getQuotaManagerInstance(CONST.PLATFORM.K8S);
quota.getQuotaManagerInstance(CONST.PLATFORM.CF, region) :
quota.getQuotaManagerInstance(CONST.PLATFORM.K8S, region);
const subaccountId = req.params.accountId;
const planId = _.get(req, 'query.planId');
const previousPlanId = _.get(req, 'query.previousPlanId');
Expand All @@ -35,7 +36,7 @@ class QuotaApiController extends FabrikBaseController {
const useAPIServerForConsumedQuotaCheck = _.get(req, 'query.useAPIServerForConsumedQuotaCheck');
const useAPIServerForConsumedQuotaCheckFlag = (useAPIServerForConsumedQuotaCheck === 'true');
logger.info(`[Quota APP] subaccountId: ${subaccountId}, orgId: ${orgId}, planId: ${planId}, previousPlanId: ${previousPlanId}, reqMethod: ${reqMethod}, useAPIServerForConsumedQuotaCheckFlag: ${useAPIServerForConsumedQuotaCheckFlag}`);
const validStatus = await quotaManager.checkQuota(subaccountId, orgId, planId, previousPlanId, reqMethod, useAPIServerForConsumedQuotaCheckFlag);
const validStatus = await quotaManager.checkQuota(subaccountId, orgId, planId, previousPlanId, reqMethod, useAPIServerForConsumedQuotaCheckFlag, region);
await res.status(CONST.HTTP_STATUS_CODE.OK).send({ quotaValidStatus: validStatus });
} catch (err) {
logger.error(`[Quota APP] Quota check could not be completed due to following error: ${err}`);
Expand Down
Loading

0 comments on commit b55c27a

Please sign in to comment.