Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DCJ-386] Register Azure Managed Resource Group with SAM #1697

Draft
wants to merge 9 commits into
base: develop
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -282,16 +282,26 @@ String signUrlForBlob(
throws InterruptedException;

/**
* Associate billing profile with an Azure managed resource group.
* Associate spend profile with an Azure managed resource group.
*
* @param userReq authenticated user
* @param billingProfileId billing profile id
* @param spendProfileId spend profile id
* @param managedResourceGroupCoordinates details of the managed resource group
* @throws InterruptedException throws if sam retry fails due to interruption
*/
void azureCreateManagedResourceGroup(
AuthenticatedUserRequest userReq,
String billingProfileId,
UUID spendProfileId,
ManagedResourceGroupCoordinates managedResourceGroupCoordinates)
throws InterruptedException;

/**
* Remove association between spend profile and an Azure managed resource group.
*
* @param userReq authenticated user
* @param spendProfileId billing profile id
* @throws InterruptedException throws if sam retry fails due to interruption
*/
void azureDeleteManagedResourceGroup(AuthenticatedUserRequest userReq, UUID spendProfileId)
throws InterruptedException;
}
16 changes: 16 additions & 0 deletions src/main/java/bio/terra/service/auth/iam/IamService.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.concurrent.TimeUnit;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.collections4.map.PassiveExpiringMap;
import org.broadinstitute.dsde.workbench.client.sam.model.ManagedResourceGroupCoordinates;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -442,4 +443,19 @@ public String signUrlForBlob(
AuthenticatedUserRequest userReq, String project, String path, Duration duration) {
return callProvider(() -> iamProvider.signUrlForBlob(userReq, project, path, duration));
}

public void registerManagedResourceGroup(
AuthenticatedUserRequest userReq,
UUID spendProfileId,
ManagedResourceGroupCoordinates managedResourceGroupCoordinates) {
callProvider(
() ->
iamProvider.azureCreateManagedResourceGroup(
userReq, spendProfileId, managedResourceGroupCoordinates));
}

public void deregisterManagedResourceGroup(
AuthenticatedUserRequest userReq, UUID spendProfileId) {
callProvider(() -> iamProvider.azureDeleteManagedResourceGroup(userReq, spendProfileId));
}
}
21 changes: 17 additions & 4 deletions src/main/java/bio/terra/service/auth/iam/sam/SamIam.java
Original file line number Diff line number Diff line change
Expand Up @@ -755,24 +755,37 @@ private String signUrlForBlobInner(
@Override
public void azureCreateManagedResourceGroup(
AuthenticatedUserRequest userReq,
String billingProfileId,
UUID spendProfileId,
ManagedResourceGroupCoordinates managedResourceGroupCoordinates)
throws InterruptedException {
SamRetry.retry(
configurationService,
() ->
azureCreateManagedResourceGroupInner(
userReq, billingProfileId, managedResourceGroupCoordinates));
userReq, spendProfileId.toString(), managedResourceGroupCoordinates));
}

private void azureCreateManagedResourceGroupInner(
AuthenticatedUserRequest userReq,
String billingProfileId,
String spendProfileId,
ManagedResourceGroupCoordinates managedResourceGroupCoordinates)
throws ApiException {
samApiService
.azureApi(userReq.getToken())
.createManagedResourceGroup(billingProfileId, managedResourceGroupCoordinates);
.createManagedResourceGroup(spendProfileId, managedResourceGroupCoordinates);
}

@Override
public void azureDeleteManagedResourceGroup(AuthenticatedUserRequest userReq, UUID spendProfileId)
throws InterruptedException {
SamRetry.retry(
configurationService,
() -> azureDeleteManagedResourceGroupInner(userReq, spendProfileId.toString()));
}

private void azureDeleteManagedResourceGroupInner(
AuthenticatedUserRequest userReq, String billingProfileId) throws ApiException {
samApiService.azureApi(userReq.getToken()).deleteManagedResourceGroup(billingProfileId);
}

private UserStatusInfo getUserInfoAndVerify(AuthenticatedUserRequest userReq) {
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/bio/terra/service/profile/ProfileService.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.broadinstitute.dsde.workbench.client.sam.model.ManagedResourceGroupCoordinates;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -303,4 +304,25 @@ public void verifyDeployedApplication(
+ "operation");
}
}

public void registerManagedResourceGroup(
BillingProfileRequestModel request,
AuthenticatedUserRequest user,
String azureResourceGroupName) {
UUID spendProfileId = request.getId();
ManagedResourceGroupCoordinates managedResourceGroupCoordinates =
new ManagedResourceGroupCoordinates()
.tenantId(request.getTenantId().toString())
.subscriptionId(request.getSubscriptionId().toString())
.managedResourceGroupName(azureResourceGroupName);

iamService.registerManagedResourceGroup(user, spendProfileId, managedResourceGroupCoordinates);
}

public void deregisterManagedResourceGroup(
BillingProfileRequestModel request, AuthenticatedUserRequest user) {
UUID spendProfileId = request.getId();

iamService.deregisterManagedResourceGroup(user, spendProfileId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public final class ProfileMapKeys {
"profileApplicationDeploymentIdList";
public static final String PROFILE_UNIQUE_STORAGE_ACCOUNT_RESOURCE_LIST =
"profileUniqueStorageAccountResourceList";
public static final String PROFILE_AZURE_APP_DEPLOYMENT_RESOURCE =
"profileAzureApplicationDeploymentResource";

private ProfileMapKeys() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package bio.terra.service.profile.flight.create;

import bio.terra.common.iam.AuthenticatedUserRequest;
import bio.terra.model.BillingProfileModel;
import bio.terra.model.BillingProfileRequestModel;
import bio.terra.service.profile.flight.ProfileMapKeys;
import bio.terra.service.resourcemanagement.azure.AzureApplicationDeploymentResource;
import bio.terra.service.resourcemanagement.azure.AzureApplicationDeploymentService;
import bio.terra.stairway.FlightContext;
import bio.terra.stairway.FlightMap;
import bio.terra.stairway.Step;
import bio.terra.stairway.StepResult;
import bio.terra.stairway.exception.RetryException;

public class CreateProfileAzureApplicationDeploymentStep implements Step {

private final AzureApplicationDeploymentService azureApplicationDeploymentService;
private final BillingProfileRequestModel request;
private final AuthenticatedUserRequest user;

public CreateProfileAzureApplicationDeploymentStep(
AzureApplicationDeploymentService azureApplicationDeploymentService,
BillingProfileRequestModel request,
AuthenticatedUserRequest user) {
this.azureApplicationDeploymentService = azureApplicationDeploymentService;
this.request = request;
this.user = user;
}

@Override
public StepResult doStep(FlightContext flightContext)
throws InterruptedException, RetryException {
FlightMap workingMap = flightContext.getWorkingMap();
BillingProfileModel billingProfile =
workingMap.get(ProfileMapKeys.PROFILE_MODEL, BillingProfileModel.class);
AzureApplicationDeploymentResource azureAppDeploymentResource =
azureApplicationDeploymentService.getOrRegisterApplicationDeployment(billingProfile);
workingMap.put(
ProfileMapKeys.PROFILE_AZURE_APP_DEPLOYMENT_RESOURCE, azureAppDeploymentResource);
return StepResult.getStepResultSuccess();
}

@Override
public StepResult undoStep(FlightContext flightContext) throws InterruptedException {
return StepResult.getStepResultSuccess();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package bio.terra.service.profile.flight.create;

import bio.terra.common.iam.AuthenticatedUserRequest;
import bio.terra.model.BillingProfileRequestModel;
import bio.terra.service.profile.ProfileService;
import bio.terra.service.profile.flight.ProfileMapKeys;
import bio.terra.service.resourcemanagement.azure.AzureApplicationDeploymentResource;
import bio.terra.stairway.FlightContext;
import bio.terra.stairway.FlightMap;
import bio.terra.stairway.Step;
import bio.terra.stairway.StepResult;
import bio.terra.stairway.exception.RetryException;

public class CreateProfileManagedResourceGroup implements Step {

private final ProfileService profileService;
private final BillingProfileRequestModel request;
private final AuthenticatedUserRequest user;

public CreateProfileManagedResourceGroup(
ProfileService profileService,
BillingProfileRequestModel request,
AuthenticatedUserRequest user) {
this.profileService = profileService;
this.request = request;
this.user = user;
}

@Override
public StepResult doStep(FlightContext flightContext)
throws InterruptedException, RetryException {
FlightMap workingMap = flightContext.getWorkingMap();
AzureApplicationDeploymentResource azureApplicationDeploymentResource =
workingMap.get(
ProfileMapKeys.PROFILE_AZURE_APP_DEPLOYMENT_RESOURCE,
AzureApplicationDeploymentResource.class);
String azureResourceGroupName = azureApplicationDeploymentResource.getAzureResourceGroupName();
profileService.registerManagedResourceGroup(request, user, azureResourceGroupName);
return StepResult.getStepResultSuccess();
}

@Override
public StepResult undoStep(FlightContext flightContext) throws InterruptedException {
profileService.deregisterManagedResourceGroup(request, user);
return StepResult.getStepResultSuccess();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import bio.terra.service.job.JobMapKeys;
import bio.terra.service.journal.JournalService;
import bio.terra.service.profile.ProfileService;
import bio.terra.service.resourcemanagement.azure.AzureApplicationDeploymentService;
import bio.terra.stairway.Flight;
import bio.terra.stairway.FlightMap;
import org.springframework.context.ApplicationContext;
Expand All @@ -17,6 +18,8 @@ public ProfileCreateFlight(FlightMap inputParameters, Object applicationContext)

ApplicationContext appContext = (ApplicationContext) applicationContext;
ProfileService profileService = appContext.getBean(ProfileService.class);
AzureApplicationDeploymentService azureApplicationDeploymentService =
appContext.getBean(AzureApplicationDeploymentService.class);
JournalService journalService = appContext.getBean(JournalService.class);

BillingProfileRequestModel request =
Expand All @@ -36,6 +39,12 @@ public ProfileCreateFlight(FlightMap inputParameters, Object applicationContext)
addStep(new CreateProfileVerifyDeployedApplicationStep(profileService, request, user));
}
addStep(new CreateProfileAuthzIamStep(profileService, request, user));
if (platform.isAzure()) {
addStep(
new CreateProfileAzureApplicationDeploymentStep(
azureApplicationDeploymentService, request, user));
addStep(new CreateProfileManagedResourceGroup(profileService, request, user));
}
addStep(new CreateProfileJournalEntryStep(journalService, user, request));
}
}
2 changes: 2 additions & 0 deletions src/test/java/bio/terra/flight/ProfileCreateFlightTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ void testConstructFlightAzure() {
"CreateProfileMetadataStep",
"CreateProfileVerifyDeployedApplicationStep",
"CreateProfileAuthzIamStep",
"CreateProfileAzureApplicationDeploymentStep",
"CreateProfileManagedResourceGroup",
"CreateProfileJournalEntryStep"));
}

Expand Down
22 changes: 22 additions & 0 deletions src/test/java/bio/terra/service/auth/iam/IamServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.broadinstitute.dsde.workbench.client.sam.model.ManagedResourceGroupCoordinates;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -202,4 +203,25 @@ void testDeriveSnapshotPolicies() {
.readers(expectedReaders)
.discoverers(policies.getDiscoverers())));
}

@Test
void testRegisterManagedResourceGroup() throws InterruptedException {
ManagedResourceGroupCoordinates managedResourceGroupCoordinates =
new ManagedResourceGroupCoordinates()
.tenantId("tenantId")
.subscriptionId("subscriptionId")
.managedResourceGroupName("managedResourceGroupName");

iamService.registerManagedResourceGroup(TEST_USER, ID, managedResourceGroupCoordinates);

verify(iamProvider)
.azureCreateManagedResourceGroup(TEST_USER, ID, managedResourceGroupCoordinates);
}

@Test
void testDeleteManagedResourceGroup() throws InterruptedException {
iamService.deregisterManagedResourceGroup(TEST_USER, ID);

verify(iamProvider).azureDeleteManagedResourceGroup(TEST_USER, ID);
}
}
17 changes: 13 additions & 4 deletions src/test/java/bio/terra/service/auth/iam/sam/SamIamTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -390,19 +390,28 @@ void testSignUrl() throws InterruptedException, ApiException {
}

@Test
void testAzureBillingProfileCreateManagedResourcieGroup()
void testAzureBillingProfileCreateManagedResourceGroup()
throws ApiException, InterruptedException {
mockSamAzureApi();
String billingProfileName = "billingProfileName";
final UUID spendProfileId = UUID.randomUUID();
ManagedResourceGroupCoordinates managedResourceGroupCoordinates =
new ManagedResourceGroupCoordinates()
.tenantId("tenantId")
.subscriptionId("subscriptionId")
.managedResourceGroupName("managedResourceGroupName");
samIam.azureCreateManagedResourceGroup(
TEST_USER, billingProfileName, managedResourceGroupCoordinates);
TEST_USER, spendProfileId, managedResourceGroupCoordinates);
verify(samAzureApi)
.createManagedResourceGroup(billingProfileName, managedResourceGroupCoordinates);
.createManagedResourceGroup(spendProfileId.toString(), managedResourceGroupCoordinates);
}

@Test
void testAzureBillingProfileDeleteManagedResourceGroup()
throws ApiException, InterruptedException {
mockSamAzureApi();
final UUID spendProfileId = UUID.randomUUID();
samIam.azureDeleteManagedResourceGroup(TEST_USER, spendProfileId);
verify(samAzureApi).deleteManagedResourceGroup(spendProfileId.toString());
}

@Nested
Expand Down
Loading