Skip to content

Commit

Permalink
Add config for enabling lzs
Browse files Browse the repository at this point in the history
  • Loading branch information
aherbst-broad committed Apr 17, 2024
1 parent 13612ca commit cb2df49
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ public ApiAzureLandingZoneList listLandingZonesByBillingProfile(
List<LandingZone> landingZones =
landingZoneService.getLandingZonesByBillingProfile(bearerToken, billingProfileId);
landingZones.forEach(
landingZone -> result.addLandingzonesItem(typeAdapter.toApiAzureLandingZone(landingZone)));
landingZone ->
result.addLandingzonesItem(
typeAdapter.toApiAzureLandingZoneFromApiClient(landingZone)));

return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import bio.terra.common.iam.BearerToken;
import bio.terra.landingzone.library.landingzones.deployment.LandingZonePurpose;
import bio.terra.landingzone.library.landingzones.deployment.ResourcePurpose;
import bio.terra.landingzone.service.landingzone.azure.LandingZoneService;
import bio.terra.workspace.app.configuration.external.FeatureConfiguration;
import bio.terra.workspace.common.utils.Rethrow;
import bio.terra.workspace.generated.model.ApiAzureLandingZone;
import bio.terra.workspace.generated.model.ApiAzureLandingZoneDefinitionList;
import bio.terra.workspace.generated.model.ApiAzureLandingZoneDeployedResource;
Expand Down Expand Up @@ -47,10 +47,10 @@ public class LandingZoneApiDispatch {
private final WorkspaceLandingZoneService amalgamated;

public LandingZoneApiDispatch(
LandingZoneService landingZoneService, FeatureConfiguration features, SamService samService) {
FeatureConfiguration features, SamService samService, LandingZoneServiceFactory lzsFactory) {
this.features = features;
this.samService = samService;
this.amalgamated = new AmalgamatedLandingZoneService(landingZoneService);
this.amalgamated = lzsFactory.getLandingZoneService();
}

public ApiCreateLandingZoneResult createAzureLandingZone(
Expand All @@ -66,32 +66,38 @@ public ApiCreateLandingZoneResult createAzureLandingZone(
// Prevent deploying more than 1 landing zone per billing profile
verifyLandingZoneDoesNotExistForBillingProfile(bearerToken, body);

return amalgamated.startLandingZoneCreationJob(
bearerToken,
body.getJobControl().getId(),
body.getLandingZoneId(),
body.getDefinition(),
body.getVersion(),
body.getParameters(),
body.getBillingProfileId(),
asyncResultEndpoint);
return Rethrow.onInterrupted(
() ->
amalgamated.startLandingZoneCreationJob(
bearerToken,
body.getJobControl().getId(),
body.getLandingZoneId(),
body.getDefinition(),
body.getVersion(),
body.getParameters(),
body.getBillingProfileId(),
asyncResultEndpoint),
"startLandingZoneCreationJob");
}

private void verifyLandingZoneDoesNotExistForBillingProfile(
BearerToken bearerToken, ApiCreateAzureLandingZoneRequestBody body) {
// TODO: Catching the exception is a temp solution.
// A better approach would be to return an empty list instead of throwing an exception
try {
amalgamated
.listLandingZonesByBillingProfile(bearerToken, body.getBillingProfileId())
.getLandingzones()
.stream()
.findFirst()
.ifPresent(
t -> {
throw new LandingZoneInvalidInputException(
"A Landing Zone already exists in the requested billing profile");
});
Rethrow.onInterrupted(
() ->
amalgamated
.listLandingZonesByBillingProfile(bearerToken, body.getBillingProfileId())
.getLandingzones()
.stream()
.findFirst()
.ifPresent(
t -> {
throw new LandingZoneInvalidInputException(
"A Landing Zone already exists in the requested billing profile");
}),
"listLandingZonesByBillingProfile");
} catch (bio.terra.landingzone.db.exception.LandingZoneNotFoundException ex) {
logger.info("The billing profile does not have a landing zone. ", ex);
}
Expand All @@ -100,13 +106,15 @@ private void verifyLandingZoneDoesNotExistForBillingProfile(
public ApiAzureLandingZoneResult getCreateAzureLandingZoneResult(
BearerToken bearerToken, String jobId) {
features.azureEnabledCheck();
return amalgamated.getAsyncJobResult(bearerToken, jobId);
return Rethrow.onInterrupted(
() -> amalgamated.getAsyncJobResult(bearerToken, jobId), "getAsyncJobResult");
}

public ApiAzureLandingZoneDefinitionList listAzureLandingZonesDefinitions(
BearerToken bearerToken) {
features.azureEnabledCheck();
return amalgamated.listLandingZoneDefinitions(bearerToken);
return Rethrow.onInterrupted(
() -> amalgamated.listLandingZoneDefinitions(bearerToken), "listLandingZoneDefinitions");
}

public ApiDeleteAzureLandingZoneResult deleteLandingZone(
Expand All @@ -115,14 +123,19 @@ public ApiDeleteAzureLandingZoneResult deleteLandingZone(
ApiDeleteAzureLandingZoneRequestBody body,
String resultEndpoint) {
features.azureEnabledCheck();
return amalgamated.startLandingZoneDeletionJob(
bearerToken, body.getJobControl().getId(), landingZoneId, resultEndpoint);
return Rethrow.onInterrupted(
() ->
amalgamated.startLandingZoneDeletionJob(
bearerToken, body.getJobControl().getId(), landingZoneId, resultEndpoint),
"startLandingZoneDeletionJob");
}

public ApiAzureLandingZoneResourcesList listAzureLandingZoneResources(
BearerToken bearerToken, UUID landingZoneId) {
features.azureEnabledCheck();
return amalgamated.listResourcesWithPurposes(bearerToken, landingZoneId);
return Rethrow.onInterrupted(
() -> amalgamated.listResourcesWithPurposes(bearerToken, landingZoneId),
"listResourcesWithPurpose");
}

public Optional<ApiAzureLandingZoneDeployedResource> getSharedStorageAccount(
Expand Down Expand Up @@ -155,7 +168,9 @@ public Optional<ApiAzureLandingZoneDeployedResource> getSharedDatabaseAdminIdent
public ApiAzureLandingZoneResourcesList listAzureLandingZoneResourcesByPurpose(
BearerToken bearerToken, UUID landingZoneId, LandingZonePurpose resourcePurpose) {
features.azureEnabledCheck();
return amalgamated.listResourcesMatchingPurpose(bearerToken, landingZoneId, resourcePurpose);
return Rethrow.onInterrupted(
() -> amalgamated.listResourcesMatchingPurpose(bearerToken, landingZoneId, resourcePurpose),
"listResourcesMatchingPurpose");
}

public UUID getLandingZoneId(BearerToken token, Workspace workspace) {
Expand All @@ -169,10 +184,11 @@ public UUID getLandingZoneId(BearerToken token, Workspace workspace) {
}

// getLandingZonesByBillingProfile returns a list. But it always contains only one item
return amalgamated
.listLandingZonesByBillingProfile(token, profileId.get())
.getLandingzones()
.stream()
var response =
Rethrow.onInterrupted(
() -> amalgamated.listLandingZonesByBillingProfile(token, profileId.get()),
"listLandingZonesByBillingProfile");
return response.getLandingzones().stream()
.findFirst()
.map(ApiAzureLandingZone::getLandingZoneId)
.orElseThrow(
Expand All @@ -188,21 +204,27 @@ public UUID getLandingZoneId(BearerToken token, Workspace workspace) {
public ApiDeleteAzureLandingZoneJobResult getDeleteAzureLandingZoneResult(
BearerToken token, UUID landingZoneId, String jobId) {
features.azureEnabledCheck();
return amalgamated.getDeleteLandingZoneResult(token, landingZoneId, jobId);
return Rethrow.onInterrupted(
() -> amalgamated.getDeleteLandingZoneResult(token, landingZoneId, jobId),
"getDeleteLandingZoneResult");
}

public ApiAzureLandingZone getAzureLandingZone(BearerToken bearerToken, UUID landingZoneId) {
features.azureEnabledCheck();
return amalgamated.getAzureLandingZone(bearerToken, landingZoneId);
return Rethrow.onInterrupted(
() -> amalgamated.getAzureLandingZone(bearerToken, landingZoneId), "getAzureLandingZone");
}

public ApiAzureLandingZoneList listAzureLandingZones(
BearerToken bearerToken, UUID billingProfileId) {
features.azureEnabledCheck();
if (billingProfileId != null) {
return amalgamated.listLandingZonesByBillingProfile(bearerToken, billingProfileId);
return Rethrow.onInterrupted(
() -> amalgamated.listLandingZonesByBillingProfile(bearerToken, billingProfileId),
"listLandingZonesByBillingProfile");
}
return amalgamated.listLandingZones(bearerToken);
return Rethrow.onInterrupted(
() -> amalgamated.listLandingZones(bearerToken), "listLandingZones");
}

public ApiResourceQuota getResourceQuota(
Expand Down Expand Up @@ -255,6 +277,7 @@ public String getLandingZoneRegionForWorkspaceUsingWsmToken(Workspace workspace)
public String getLandingZoneRegionUsingWsmToken(UUID landingZoneId) {
features.azureEnabledCheck();
var token = new BearerToken(samService.getWsmServiceAccountToken());
return amalgamated.getLandingZoneRegion(token, landingZoneId);
return Rethrow.onInterrupted(
() -> amalgamated.getLandingZoneRegion(token, landingZoneId), "getLandingZoneRegion");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public LandingZoneServiceApiException(
super(message, cause, causes, statusCode);
}

/** Get the HTTP status code of the underlying response from Policy Service. */
/** Get the HTTP status code of the underlying response from LZS. */
public int getApiExceptionStatus() {
return apiException.getCode();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package bio.terra.workspace.amalgam.landingzone.azure;

import bio.terra.landingzone.service.landingzone.azure.LandingZoneService;
import bio.terra.workspace.app.configuration.external.FeatureConfiguration;
import io.opentelemetry.api.OpenTelemetry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class LandingZoneServiceFactory {

private final LandingZoneService amalgamatedLandingZoneService;
private FeatureConfiguration configuration;
private final OpenTelemetry openTelemetry;

@Autowired
public LandingZoneServiceFactory(
FeatureConfiguration configuration,
OpenTelemetry openTelemetry,
LandingZoneService landingZoneService) {
this.configuration = configuration;
this.openTelemetry = openTelemetry;
this.amalgamatedLandingZoneService = landingZoneService;
}

public WorkspaceLandingZoneService getLandingZoneService() {
if (this.configuration.isLzsEnabled()) {
return new HttpLandingZoneService(openTelemetry);
} else {
return new AmalgamatedLandingZoneService(amalgamatedLandingZoneService);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class FeatureConfiguration {
private boolean tpsEnabled;
private boolean bpmGcpEnabled;
private boolean temporaryGrantEnabled;
private boolean lzsEnabled;
private WsmResourceStateRule stateRule;

public boolean isAzureEnabled() {
Expand Down Expand Up @@ -62,6 +63,14 @@ public void setBpmGcpEnabled(boolean bpmGcpEnabled) {
this.bpmGcpEnabled = bpmGcpEnabled;
}

public boolean isLzsEnabled() {
return lzsEnabled;
}

public void setLzsEnabled(boolean lzsEnabled) {
this.lzsEnabled = lzsEnabled;
}

public boolean isTemporaryGrantEnabled() {
return temporaryGrantEnabled;
}
Expand Down Expand Up @@ -110,5 +119,7 @@ public void logFeatures() {
logger.info("Feature: bpm-gcp-enabled: {}", isBpmGcpEnabled());
logger.info("Feature: temporary-grant-enabled: {}", isTemporaryGrantEnabled());
logger.info("Feature: state-rule: {}", getStateRule());
logger.info("Feature: lzs-enabled: {}", isLzsEnabled());
;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class LzsRetry {
private static final Duration INITIAL_WAIT = Duration.ofSeconds(10);
private static final Duration OPERATION_TIMEOUT = Duration.ofSeconds(300);

// Tps calls which timeout will throw ApiExceptions wrapping SocketTimeoutExceptions and will have
// LZS calls which timeout will throw ApiExceptions wrapping SocketTimeoutExceptions and will have
// an errorCode 0. This isn't a real HTTP status code, but we can check for it anyway.
private static final int TIMEOUT_STATUS_CODE = 0;

Expand Down Expand Up @@ -55,7 +55,7 @@ public interface LzsFunction<R> {
}

/**
* Requests made through the Tps client library sometimes fail with timeouts, generally due to
* Requests made through the LZS client library sometimes fail with timeouts, generally due to
* transient network or connection issues. When this happens, the client library will throw an API
* exceptions with status code 0 wrapping a SocketTimeoutException. These errors should always be
* retried.
Expand Down Expand Up @@ -127,20 +127,20 @@ private void performVoid(LzsRetry.LzsVoidFunction function)
}

/**
* Given an exception from Tps, either timeout and rethrow the error from Tps or sleep for
* Given an exception from LZS, either timeout and rethrow the error from LZS or sleep for
* retryDuration. If the thread times out while sleeping, throw the initial exception.
*
* <p>With the current values of INITIAL_WAIT and MAXIMUM_WAIT, this will sleep with the pattern
* 10, 20, 30, 30, 30... seconds.
*
* @param previousException The error Tps threw
* @param previousException The error LZS threw
* @throws E, InterruptedException
*/
private <E extends Exception> void sleepOrTimeoutBeforeRetrying(E previousException)
throws E, InterruptedException {
if (operationTimeout.minus(retryDuration).isBefore(now())) {
logger.error("LzsRetry: operation timed out after " + operationTimeout);
// If we timed out, throw the error from Tps that caused us to need to retry.
// If we timed out, throw the error from LZS that caused us to need to retry.
throw previousException;
}
logger.info("LzsRetry: sleeping " + retryDuration.getSeconds() + " seconds");
Expand Down
Loading

0 comments on commit cb2df49

Please sign in to comment.