use of bio.terra.stairway.FlightState in project terra-workspace-manager by DataBiosphere.
the class CreateGcpContextFlightTest method successCreatesProjectAndContext.
@Test
@DisabledIfEnvironmentVariable(named = "TEST_ENV", matches = BUFFER_SERVICE_DISABLED_ENVS_REG_EX)
void successCreatesProjectAndContext() throws Exception {
UUID workspaceId = createWorkspace(spendUtils.defaultSpendId());
AuthenticatedUserRequest userRequest = userAccessUtils.defaultUserAuthRequest();
assertTrue(testUtils.getAuthorizedGcpCloudContext(workspaceId, userRequest).isEmpty());
// Retry steps once to validate idempotency.
Map<String, StepStatus> retrySteps = getStepNameToStepStatusMap();
FlightDebugInfo debugInfo = FlightDebugInfo.newBuilder().doStepFailures(retrySteps).build();
FlightState flightState = StairwayTestUtils.blockUntilFlightCompletes(jobService.getStairway(), CreateGcpContextFlight.class, createInputParameters(workspaceId, userRequest), STAIRWAY_FLIGHT_TIMEOUT, debugInfo);
assertEquals(FlightStatus.SUCCESS, flightState.getFlightStatus());
String projectId = flightState.getResultMap().get().get(WorkspaceFlightMapKeys.GCP_PROJECT_ID, String.class);
assertTrue(testUtils.getAuthorizedGcpCloudContext(workspaceId, userRequest).isPresent());
String contextProjectId = workspaceService.getAuthorizedRequiredGcpProject(workspaceId, userRequest);
assertEquals(projectId, contextProjectId);
Project project = crl.getCloudResourceManagerCow().projects().get(projectId).execute();
assertEquals(projectId, project.getProjectId());
assertEquals("billingAccounts/" + spendUtils.defaultBillingAccountId(), crl.getCloudBillingClientCow().getProjectBillingInfo("projects/" + projectId).getBillingAccountName());
assertRolesExist(project);
assertPolicyGroupsSynced(workspaceId, project);
}
use of bio.terra.stairway.FlightState in project terra-workspace-manager by DataBiosphere.
the class StairwayTestUtils method enumerateJobsDump.
public static void enumerateJobsDump(Alpha1Service alpha1Service, UUID workspaceUuid, AuthenticatedUserRequest userRequest) {
EnumeratedJobs jobs = alpha1Service.enumerateJobs(workspaceUuid, userRequest, 1000, null, null, null, null, null);
System.out.printf("Enumerated Jobs: total=%d, pageToken=%s%n", jobs.getTotalResults(), jobs.getPageToken());
for (EnumeratedJob job : jobs.getResults()) {
FlightState flightState = job.getFlightState();
System.out.printf(" Job %s %s%n", flightState.getFlightId(), flightState.getFlightStatus());
System.out.printf(" description: %s%n", job.getJobDescription());
System.out.printf(" submitted : %s%n", flightState.getSubmitted());
System.out.printf(" completed : %s%n", flightState.getCompleted().map(Instant::toString).orElse("<incomplete>"));
if (flightState.getException().isPresent()) {
System.out.printf(" error : %s%n", flightState.getException().get().getMessage());
}
System.out.printf(" operation : %s%n", job.getOperationType());
if (job.getResource().isPresent()) {
WsmResource resource = job.getResource().get();
System.out.println(" resource:");
System.out.printf(" name: %s%n", resource.getName());
System.out.printf(" id : %s%n", resource.getResourceId());
System.out.printf(" desc: %s%n", resource.getDescription());
System.out.printf(" stew: %s%n", resource.getStewardshipType());
System.out.printf(" type: %s%n", resource.getResourceType());
}
}
}
use of bio.terra.stairway.FlightState in project terra-workspace-manager by DataBiosphere.
the class ControlledResourceService method waitForResourceOrJob.
/**
* For async resource creation, we do not want to return to the caller until the resource row is
* in the database and, thus, visible to enumeration. This method waits for either the row to show
* up (the expected success case) or the job to complete (the expected error case). If one of
* those doesn't happen in the retry window, we throw SERVICE_UNAVAILABLE. The theory is for it
* not to complete, either WSM is so busy that it cannot schedule the flight or something bad has
* happened. Either way, SERVICE_UNAVAILABLE seems like a reasonable response.
*
* <p>There is no race condition between the two checks. For either termination test, we will make
* the async return to the client. That path returns the current job state. If the job is
* complete, the client calls the result endpoint and gets the full result.
*
* @param workspaceUuid workspace of the resource create
* @param resourceId id of resource being created
* @param jobId id of the create flight.
*/
private void waitForResourceOrJob(UUID workspaceUuid, UUID resourceId, String jobId) {
Instant exitTime = Instant.now().plus(RESOURCE_ROW_MAX_WAIT_TIME);
try {
while (Instant.now().isBefore(exitTime)) {
if (resourceDao.resourceExists(workspaceUuid, resourceId)) {
return;
}
FlightState flightState = jobService.getStairway().getFlightState(jobId);
if (flightState.getCompleted().isPresent()) {
return;
}
TimeUnit.SECONDS.sleep(RESOURCE_ROW_WAIT_SECONDS);
}
} catch (InterruptedException e) {
// fall through to throw
}
throw new ServiceUnavailableException("Failed to make prompt progress on resource");
}
use of bio.terra.stairway.FlightState in project terra-workspace-manager by DataBiosphere.
the class AwaitCloneAllResourcesFlightStep method doStep.
@Override
public StepResult doStep(FlightContext context) throws InterruptedException, RetryException {
validateRequiredEntries(context.getInputParameters(), ControlledResourceKeys.SOURCE_WORKSPACE_ID, JobMapKeys.REQUEST.getKeyName());
validateRequiredEntries(context.getWorkingMap(), ControlledResourceKeys.CLONE_ALL_RESOURCES_FLIGHT_ID);
final var cloneAllResourcesFlightId = context.getWorkingMap().get(ControlledResourceKeys.CLONE_ALL_RESOURCES_FLIGHT_ID, String.class);
final var destinationWorkspace = context.getInputParameters().get(JobMapKeys.REQUEST.getKeyName(), Workspace.class);
try {
// noinspection deprecation
final FlightState subflightState = context.getStairway().waitForFlight(cloneAllResourcesFlightId, FLIGHT_POLL_SECONDS, FLIGHT_POLL_CYCLES);
if (FlightStatus.SUCCESS != subflightState.getFlightStatus()) {
// no point in retrying the await step
return new StepResult(StepStatus.STEP_RESULT_FAILURE_FATAL, subflightState.getException().orElseGet(() -> new RuntimeException(String.format("Subflight had unexpected status %s. No exception for subflight found.", subflightState.getFlightStatus()))));
}
final FlightMap subflightResultMap = FlightUtils.getResultMapRequired(subflightState);
// Build the response object from the resource ID to details map. The map won't have been
// instantiated if there are no resources in the workspace, so just use an empty map in that
// case.
final var resourceIdToDetails = Optional.ofNullable(subflightResultMap.get(ControlledResourceKeys.RESOURCE_ID_TO_CLONE_RESULT, new TypeReference<Map<UUID, WsmResourceCloneDetails>>() {
})).orElse(Collections.emptyMap());
final var apiClonedWorkspace = new ApiClonedWorkspace();
apiClonedWorkspace.setDestinationWorkspaceId(destinationWorkspace.getWorkspaceId());
final var sourceWorkspaceId = context.getInputParameters().get(ControlledResourceKeys.SOURCE_WORKSPACE_ID, UUID.class);
apiClonedWorkspace.setSourceWorkspaceId(sourceWorkspaceId);
final List<ApiResourceCloneDetails> resources = resourceIdToDetails.values().stream().map(WsmResourceCloneDetails::toApiModel).collect(Collectors.toList());
apiClonedWorkspace.setResources(resources);
// Set overall response for workspace clone flights
FlightUtils.setResponse(context, apiClonedWorkspace, HttpStatus.OK);
} catch (DatabaseOperationException | FlightWaitTimedOutException e) {
// Retry for database issues or expired wait loop
return new StepResult(StepStatus.STEP_RESULT_FAILURE_RETRY, e);
}
return StepResult.getStepResultSuccess();
}
use of bio.terra.stairway.FlightState in project terra-workspace-manager by DataBiosphere.
the class AwaitCloneGcsBucketResourceFlightStep method doStep.
@Override
public StepResult doStep(FlightContext context) throws InterruptedException, RetryException {
// wait for the flight
try {
final FlightState subflightState = context.getStairway().waitForFlight(subflightId, FLIGHT_POLL_SECONDS, FLIGHT_POLL_CYCLES);
final FlightStatus subflightStatus = subflightState.getFlightStatus();
final WsmCloneResourceResult cloneResult = WorkspaceCloneUtils.flightStatusToCloneResult(subflightStatus, resource);
final var cloneDetails = new WsmResourceCloneDetails();
cloneDetails.setResult(cloneResult);
final FlightMap resultMap = FlightUtils.getResultMapRequired(subflightState);
final var clonedBucket = resultMap.get(JobMapKeys.RESPONSE.getKeyName(), ApiClonedControlledGcpGcsBucket.class);
cloneDetails.setStewardshipType(StewardshipType.CONTROLLED);
cloneDetails.setResourceType(WsmResourceType.CONTROLLED_GCP_GCS_BUCKET);
cloneDetails.setCloningInstructions(resource.getCloningInstructions());
cloneDetails.setSourceResourceId(resource.getResourceId());
cloneDetails.setDestinationResourceId(Optional.ofNullable(clonedBucket).map(ApiClonedControlledGcpGcsBucket::getBucket).map(ApiCreatedControlledGcpGcsBucket::getResourceId).orElse(null));
cloneDetails.setErrorMessage(FlightUtils.getFlightErrorMessage(subflightState));
cloneDetails.setName(resource.getName());
cloneDetails.setDescription(resource.getDescription());
// add to the map
final var resourceIdToResult = Optional.ofNullable(context.getWorkingMap().get(ControlledResourceKeys.RESOURCE_ID_TO_CLONE_RESULT, new TypeReference<Map<UUID, WsmResourceCloneDetails>>() {
})).orElseGet(HashMap::new);
resourceIdToResult.put(resource.getResourceId(), cloneDetails);
context.getWorkingMap().put(ControlledResourceKeys.RESOURCE_ID_TO_CLONE_RESULT, resourceIdToResult);
} catch (DatabaseOperationException | FlightWaitTimedOutException e) {
// Retry for database issues or expired wait loop
return new StepResult(StepStatus.STEP_RESULT_FAILURE_RETRY, e);
}
validateRequiredEntries(context.getWorkingMap(), ControlledResourceKeys.RESOURCE_ID_TO_CLONE_RESULT);
return StepResult.getStepResultSuccess();
}
Aggregations