Search in sources :

Example 1 with BadRequestException

use of bio.terra.common.exception.BadRequestException in project terra-workspace-manager by DataBiosphere.

the class ControlledResourceServiceTest method createAiNotebookInstanceNoWriterRoleThrowsBadRequest.

@Test
@DisabledIfEnvironmentVariable(named = "TEST_ENV", matches = BUFFER_SERVICE_DISABLED_ENVS_REG_EX)
void createAiNotebookInstanceNoWriterRoleThrowsBadRequest() throws Exception {
    String instanceId = "create-ai-notebook-instance-shared";
    ApiGcpAiNotebookInstanceCreationParameters creationParameters = ControlledResourceFixtures.defaultNotebookCreationParameters().instanceId(instanceId).location(DEFAULT_NOTEBOOK_LOCATION);
    ControlledAiNotebookInstanceResource resource = makeNotebookTestResource(workspace.getWorkspaceId(), instanceId, instanceId);
    // Shared notebooks not yet implemented.
    // Private IAM roles must include writer role.
    ControlledResourceIamRole notWriter = ControlledResourceIamRole.READER;
    BadRequestException noWriterException = assertThrows(BadRequestException.class, () -> controlledResourceService.createAiNotebookInstance(resource, creationParameters, notWriter, new ApiJobControl().id(UUID.randomUUID().toString()), "fakeResultPath", user.getAuthenticatedRequest()));
    assertEquals("A private, controlled AI Notebook instance must have the writer or editor role or else it is not useful.", noWriterException.getMessage());
}
Also used : ApiGcpAiNotebookInstanceCreationParameters(bio.terra.workspace.generated.model.ApiGcpAiNotebookInstanceCreationParameters) BadRequestException(bio.terra.common.exception.BadRequestException) ControlledResourceIamRole(bio.terra.workspace.service.iam.model.ControlledResourceIamRole) ControlledAiNotebookInstanceResource(bio.terra.workspace.service.resource.controlled.cloud.gcp.ainotebook.ControlledAiNotebookInstanceResource) ApiJobControl(bio.terra.workspace.generated.model.ApiJobControl) Test(org.junit.jupiter.api.Test) BaseConnectedTest(bio.terra.workspace.common.BaseConnectedTest) DisabledIfEnvironmentVariable(org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable)

Example 2 with BadRequestException

use of bio.terra.common.exception.BadRequestException in project terra-workspace-manager by DataBiosphere.

the class CreateGcsBucketStep method doStep.

@Override
public StepResult doStep(FlightContext flightContext) throws InterruptedException, RetryException {
    FlightMap inputMap = flightContext.getInputParameters();
    ApiGcpGcsBucketCreationParameters creationParameters = inputMap.get(CREATION_PARAMETERS, ApiGcpGcsBucketCreationParameters.class);
    String projectId = gcpCloudContextService.getRequiredGcpProject(resource.getWorkspaceId());
    BucketInfo.Builder bucketInfoBuilder = BucketInfo.newBuilder(resource.getBucketName()).setLocation(Optional.ofNullable(creationParameters.getLocation()).orElse(DEFAULT_REGION));
    // Remaining creation parameters are optional
    Optional.ofNullable(creationParameters.getDefaultStorageClass()).map(GcsApiConversions::toGcsApi).ifPresent(bucketInfoBuilder::setStorageClass);
    bucketInfoBuilder.setLifecycleRules(Optional.ofNullable(creationParameters.getLifecycle()).map(GcsApiConversions::toGcsApiRulesList).orElse(Collections.emptyList()));
    BucketInfo.IamConfiguration iamConfiguration = BucketInfo.IamConfiguration.newBuilder().setIsUniformBucketLevelAccessEnabled(true).build();
    bucketInfoBuilder.setIamConfiguration(iamConfiguration);
    // Uniqueness within the project is already verified in WSM's DB earlier in this flight.
    try {
        Optional<Bucket> existingBucket = getBucket(resource.getBucketName());
        if (existingBucket.isEmpty()) {
            StorageCow storageCow = crlService.createStorageCow(projectId);
            storageCow.create(bucketInfoBuilder.build());
        } else if (bucketInProject(existingBucket.get(), projectId)) {
            logger.info("Bucket {} already exists in workspace project, this is a Stairway retry. Continuing.", resource.getBucketName());
        } else {
            throw new DuplicateResourceException("The provided bucket name is already in use, please choose another.");
        }
    } catch (StorageException storageException) {
        // in GCP's global bucket namespace, even if we don't have permission to GET it.
        if (storageException.getCode() == HttpStatus.SC_CONFLICT) {
            throw new DuplicateResourceException("The provided bucket name is already in use, please choose another.", storageException);
        }
        if (storageException.getCode() == HttpStatus.SC_BAD_REQUEST) {
            throw new BadRequestException("Received 400 BAD_REQUEST exception when creating a new gcs-bucket", storageException);
        }
        // Other cloud errors are unexpected here, rethrow.
        throw storageException;
    }
    return StepResult.getStepResultSuccess();
}
Also used : StorageCow(bio.terra.cloudres.google.storage.StorageCow) DuplicateResourceException(bio.terra.workspace.service.resource.exception.DuplicateResourceException) Bucket(com.google.api.services.storage.model.Bucket) BadRequestException(bio.terra.common.exception.BadRequestException) FlightMap(bio.terra.stairway.FlightMap) BucketInfo(com.google.cloud.storage.BucketInfo) ApiGcpGcsBucketCreationParameters(bio.terra.workspace.generated.model.ApiGcpGcsBucketCreationParameters) StorageException(com.google.cloud.storage.StorageException)

Example 3 with BadRequestException

use of bio.terra.common.exception.BadRequestException in project terra-resource-buffer by DataBiosphere.

the class PoolService method handoutResourceTransactionally.

/**
 * Process handout resource in on transaction (anything failure will cause database rollback).
 */
private Resource handoutResourceTransactionally(PoolId poolId, RequestHandoutId requestHandoutId) {
    Optional<Pool> pool = bufferDao.retrievePool(poolId);
    if (pool.isEmpty() || !pool.get().status().equals(PoolStatus.ACTIVE)) {
        throw new BadRequestException(String.format("Invalid pool id: %s.", poolId));
    }
    try {
        // Retry 20 times of 2 seconds each.
        Optional<Resource> resource = executeAndRetry(() -> bufferDao.updateOneReadyResourceToHandedOut(poolId, requestHandoutId), Duration.ofSeconds(2), 20);
        Resource result = resource.orElseThrow(() -> new NotFoundException(String.format("No resource is ready to use at this moment for pool: %s. Please try later", poolId)));
        logger.info("Handed out resource ID {}, Handout ID {}, Pool ID {}", result.cloudResourceUid(), result.requestHandoutId(), poolId);
        return result;
    } catch (InterruptedException | DataAccessException e) {
        throw new InternalServerErrorException(String.format("Failed to update one resource state from READY to HANDED_OUT for pool %s", poolId));
    }
}
Also used : Resource(bio.terra.buffer.common.Resource) BadRequestException(bio.terra.common.exception.BadRequestException) NotFoundException(bio.terra.buffer.common.exception.NotFoundException) InternalServerErrorException(bio.terra.common.exception.InternalServerErrorException) Pool(bio.terra.buffer.common.Pool) DataAccessException(org.springframework.dao.DataAccessException)

Example 4 with BadRequestException

use of bio.terra.common.exception.BadRequestException in project terra-workspace-manager by DataBiosphere.

the class ControlledResourceService method createAiNotebookInstance.

/**
 * Starts a create controlled AI Notebook instance resource job, returning the job id.
 */
public String createAiNotebookInstance(ControlledAiNotebookInstanceResource resource, ApiGcpAiNotebookInstanceCreationParameters creationParameters, @Nullable ControlledResourceIamRole privateResourceIamRole, @Nullable ApiJobControl jobControl, String resultPath, AuthenticatedUserRequest userRequest) {
    // Special check for notebooks: READER is not a useful role
    if (privateResourceIamRole == ControlledResourceIamRole.READER) {
        throw new BadRequestException("A private, controlled AI Notebook instance must have the writer or editor role or else it is not useful.");
    }
    JobBuilder jobBuilder = commonCreationJobBuilder(resource, privateResourceIamRole, jobControl, resultPath, userRequest);
    String petSaEmail = SamRethrow.onInterrupted(() -> samService.getOrCreatePetSaEmail(gcpCloudContextService.getRequiredGcpProject(resource.getWorkspaceId()), userRequest.getRequiredToken()), "enablePet");
    jobBuilder.addParameter(ControlledResourceKeys.CREATE_NOTEBOOK_PARAMETERS, creationParameters);
    jobBuilder.addParameter(ControlledResourceKeys.NOTEBOOK_PET_SERVICE_ACCOUNT, petSaEmail);
    String jobId = jobBuilder.submit();
    waitForResourceOrJob(resource.getWorkspaceId(), resource.getResourceId(), jobId);
    return jobId;
}
Also used : JobBuilder(bio.terra.workspace.service.job.JobBuilder) BadRequestException(bio.terra.common.exception.BadRequestException)

Example 5 with BadRequestException

use of bio.terra.common.exception.BadRequestException in project terra-workspace-manager by DataBiosphere.

the class ControllerBase method computePrivateUserRole.

/**
 * Validate and provide defaulting for the private resource user. The property is never required.
 * The only time it is allowed is for application-private resources. If it is populated, we
 * validate the user email and the specified IAM roles.
 *
 * <p>user-private resources are always assigned to the caller. You can't create a user-private
 * resource and assign it to someone else. Because we can read the caller's email from the
 * AuthenticatedUserRequest, we don't need to supply assignedUser in the request body.
 *
 * <p>application-private resources can be assigned to users other than the caller. For example,
 * Leo could call WSM to create a VM (using the Leo SA's auth token) and request it be assigned to
 * user X, not to the Leo SA.
 *
 * @param commonFields common fields from a controlled resource create request
 * @param userRequest authenticate user
 * @return PrivateUserRole holding the user email and the role list
 */
public PrivateUserRole computePrivateUserRole(UUID workspaceId, ApiControlledResourceCommonFields commonFields, AuthenticatedUserRequest userRequest) {
    AccessScopeType accessScope = AccessScopeType.fromApi(commonFields.getAccessScope());
    ManagedByType managedBy = ManagedByType.fromApi(commonFields.getManagedBy());
    ApiPrivateResourceUser inputUser = commonFields.getPrivateResourceUser();
    // Shared access has no private user role
    if (accessScope == AccessScopeType.ACCESS_SCOPE_SHARED) {
        validateNoInputUser(inputUser);
        return new PrivateUserRole.Builder().present(false).build();
    }
    // Private access scope
    switch(managedBy) {
        case MANAGED_BY_APPLICATION:
            {
                // Supplying a user is optional for applications
                if (inputUser == null) {
                    return new PrivateUserRole.Builder().present(false).build();
                }
                // We have a private user, so make sure the email is present and valid
                String userEmail = commonFields.getPrivateResourceUser().getUserName();
                ControllerValidationUtils.validateEmail(userEmail);
                // Validate that the assigned user is a member of the workspace. It must have at least
                // READ action.
                SamRethrow.onInterrupted(() -> samService.userIsAuthorized(SamConstants.SamResource.WORKSPACE, workspaceId.toString(), SamConstants.SamWorkspaceAction.READ, userEmail, userRequest), "validate private user is workspace member");
                // Translate the incoming role list into our internal model form
                // This also validates that the incoming API model values are correct.
                List<ControlledResourceIamRole> roles = commonFields.getPrivateResourceUser().getPrivateResourceIamRoles().stream().map(ControlledResourceIamRole::fromApiModel).collect(Collectors.toList());
                if (roles.isEmpty()) {
                    throw new ValidationException("You must specify at least one role when you specify PrivateResourceIamRoles");
                }
                // The legal options for the assigned user of an application is READER
                // or WRITER. EDITOR is not allowed. We take the "max" of READER and WRITER.
                var maxRole = ControlledResourceIamRole.READER;
                for (ControlledResourceIamRole role : roles) {
                    if (role == ControlledResourceIamRole.WRITER) {
                        if (maxRole == ControlledResourceIamRole.READER) {
                            maxRole = role;
                        }
                    } else if (role != ControlledResourceIamRole.READER) {
                        throw new ValidationException("For application private controlled resources, only READER and WRITER roles are allowed. Found " + role.toApiModel());
                    }
                }
                return new PrivateUserRole.Builder().present(true).userEmail(userEmail).role(maxRole).build();
            }
        case MANAGED_BY_USER:
            {
                // TODO: PF-1218 The target state is that supplying a user is not allowed.
                // However, current CLI and maybe UI are supplying all or part of the structure,
                // so tolerate all states: no-input, only roles, roles and user
                /* Target state:
          // Supplying a user is not allowed. The creating user is always the assigned user.
          validateNoInputUser(inputUser);
          */
                // Fill in the user role for the creating user
                String userEmail = SamRethrow.onInterrupted(() -> samService.getUserEmailFromSam(userRequest), "getUserEmailFromSam");
                // matches the requesting name.
                if (inputUser != null && inputUser.getUserName() != null) {
                    if (!StringUtils.equalsIgnoreCase(userEmail, inputUser.getUserName())) {
                        throw new BadRequestException("User (" + userEmail + ") may only assign a private controlled resource to themselves");
                    }
                }
                // to different objects.
                return new PrivateUserRole.Builder().present(true).userEmail(userEmail).role(ControlledResourceIamRole.EDITOR).build();
            }
        default:
            throw new InternalLogicException("Unknown managedBy enum");
    }
}
Also used : ManagedByType(bio.terra.workspace.service.resource.controlled.model.ManagedByType) ValidationException(bio.terra.common.exception.ValidationException) InternalLogicException(bio.terra.workspace.common.exception.InternalLogicException) AccessScopeType(bio.terra.workspace.service.resource.controlled.model.AccessScopeType) ApiPrivateResourceUser(bio.terra.workspace.generated.model.ApiPrivateResourceUser) BadRequestException(bio.terra.common.exception.BadRequestException) List(java.util.List) ControlledResourceIamRole(bio.terra.workspace.service.iam.model.ControlledResourceIamRole) PrivateUserRole(bio.terra.workspace.service.resource.controlled.model.PrivateUserRole)

Aggregations

BadRequestException (bio.terra.common.exception.BadRequestException)7 ControlledResourceIamRole (bio.terra.workspace.service.iam.model.ControlledResourceIamRole)2 Pool (bio.terra.buffer.common.Pool)1 Resource (bio.terra.buffer.common.Resource)1 NotFoundException (bio.terra.buffer.common.exception.NotFoundException)1 StorageCow (bio.terra.cloudres.google.storage.StorageCow)1 InternalServerErrorException (bio.terra.common.exception.InternalServerErrorException)1 ValidationException (bio.terra.common.exception.ValidationException)1 FlightMap (bio.terra.stairway.FlightMap)1 BaseConnectedTest (bio.terra.workspace.common.BaseConnectedTest)1 InternalLogicException (bio.terra.workspace.common.exception.InternalLogicException)1 ApiGcpAiNotebookInstanceAcceleratorConfig (bio.terra.workspace.generated.model.ApiGcpAiNotebookInstanceAcceleratorConfig)1 ApiGcpAiNotebookInstanceContainerImage (bio.terra.workspace.generated.model.ApiGcpAiNotebookInstanceContainerImage)1 ApiGcpAiNotebookInstanceCreationParameters (bio.terra.workspace.generated.model.ApiGcpAiNotebookInstanceCreationParameters)1 ApiGcpAiNotebookInstanceVmImage (bio.terra.workspace.generated.model.ApiGcpAiNotebookInstanceVmImage)1 ApiGcpGcsBucketCreationParameters (bio.terra.workspace.generated.model.ApiGcpGcsBucketCreationParameters)1 ApiJobControl (bio.terra.workspace.generated.model.ApiJobControl)1 ApiPrivateResourceUser (bio.terra.workspace.generated.model.ApiPrivateResourceUser)1 JobBuilder (bio.terra.workspace.service.job.JobBuilder)1 ControlledAiNotebookInstanceResource (bio.terra.workspace.service.resource.controlled.cloud.gcp.ainotebook.ControlledAiNotebookInstanceResource)1