Search in sources :

Example 1 with CreatedControlledGcpAiNotebookInstanceResult

use of bio.terra.workspace.model.CreatedControlledGcpAiNotebookInstanceResult in project terra-workspace-manager by DataBiosphere.

the class PrivateControlledAiNotebookInstanceLifecycle method createAControlledAiNotebookInstanceWithoutSpecifiedInstanceId_validInstanceIdIsGenerated.

private void createAControlledAiNotebookInstanceWithoutSpecifiedInstanceId_validInstanceIdIsGenerated(ControlledGcpResourceApi resourceUserApi) throws ApiException, InterruptedException {
    CreatedControlledGcpAiNotebookInstanceResult resourceWithNotebookInstanceIdNotSpecified = NotebookUtils.makeControlledNotebookUserPrivate(getWorkspaceId(), /*instanceId=*/
    null, /*location=*/
    null, resourceUserApi);
    assertNotNull(resourceWithNotebookInstanceIdNotSpecified.getAiNotebookInstance().getAttributes().getInstanceId());
    resourceUserApi.deleteAiNotebookInstance(new DeleteControlledGcpAiNotebookInstanceRequest().jobControl(new JobControl().id(UUID.randomUUID().toString())), getWorkspaceId(), resourceWithNotebookInstanceIdNotSpecified.getAiNotebookInstance().getMetadata().getResourceId());
}
Also used : CreatedControlledGcpAiNotebookInstanceResult(bio.terra.workspace.model.CreatedControlledGcpAiNotebookInstanceResult) DeleteControlledGcpAiNotebookInstanceRequest(bio.terra.workspace.model.DeleteControlledGcpAiNotebookInstanceRequest) JobControl(bio.terra.workspace.model.JobControl)

Example 2 with CreatedControlledGcpAiNotebookInstanceResult

use of bio.terra.workspace.model.CreatedControlledGcpAiNotebookInstanceResult in project terra-workspace-manager by DataBiosphere.

the class PrivateControlledAiNotebookInstanceLifecycle method createAControlledAiNotebookInstanceWithoutSpecifiedInstanceId_specifyLocation.

private void createAControlledAiNotebookInstanceWithoutSpecifiedInstanceId_specifyLocation(ControlledGcpResourceApi resourceUserApi) throws ApiException, InterruptedException {
    String location = "us-east1-b";
    CreatedControlledGcpAiNotebookInstanceResult resourceWithNotebookInstanceIdNotSpecified = NotebookUtils.makeControlledNotebookUserPrivate(getWorkspaceId(), /*instanceId=*/
    null, /*location=*/
    location, resourceUserApi);
    assertEquals(location, resourceWithNotebookInstanceIdNotSpecified.getAiNotebookInstance().getAttributes().getLocation());
    resourceUserApi.deleteAiNotebookInstance(new DeleteControlledGcpAiNotebookInstanceRequest().jobControl(new JobControl().id(UUID.randomUUID().toString())), getWorkspaceId(), resourceWithNotebookInstanceIdNotSpecified.getAiNotebookInstance().getMetadata().getResourceId());
}
Also used : CreatedControlledGcpAiNotebookInstanceResult(bio.terra.workspace.model.CreatedControlledGcpAiNotebookInstanceResult) DeleteControlledGcpAiNotebookInstanceRequest(bio.terra.workspace.model.DeleteControlledGcpAiNotebookInstanceRequest) JobControl(bio.terra.workspace.model.JobControl)

Example 3 with CreatedControlledGcpAiNotebookInstanceResult

use of bio.terra.workspace.model.CreatedControlledGcpAiNotebookInstanceResult in project terra-workspace-manager by DataBiosphere.

the class PrivateControlledAiNotebookInstanceLifecycle method doUserJourney.

@Override
@SuppressFBWarnings(value = "DLS_DEAD_LOCAL_STORE")
protected void doUserJourney(TestUserSpecification testUser, WorkspaceApi workspaceApi) throws Exception {
    CloudContextMaker.createGcpCloudContext(getWorkspaceId(), workspaceApi);
    workspaceApi.grantRole(new GrantRoleRequestBody().memberEmail(resourceUser.userEmail), getWorkspaceId(), IamRole.WRITER);
    workspaceApi.grantRole(new GrantRoleRequestBody().memberEmail(otherWorkspaceUser.userEmail), getWorkspaceId(), IamRole.WRITER);
    ControlledGcpResourceApi resourceUserApi = ClientTestUtils.getControlledGcpResourceClient(resourceUser, server);
    CreatedControlledGcpAiNotebookInstanceResult creationResult = NotebookUtils.makeControlledNotebookUserPrivate(getWorkspaceId(), instanceId, /*location=*/
    null, resourceUserApi);
    UUID resourceId = creationResult.getAiNotebookInstance().getMetadata().getResourceId();
    GcpAiNotebookInstanceResource resource = resourceUserApi.getAiNotebookInstance(getWorkspaceId(), resourceId);
    assertEquals(instanceId, resource.getAttributes().getInstanceId(), "Notebook instance id is correct in GET response from WSM");
    assertEquals(instanceId, creationResult.getAiNotebookInstance().getAttributes().getInstanceId(), "Notebook instance id is correct in create response from WSM");
    assertEquals(resourceUser.userEmail, resource.getMetadata().getControlledResourceMetadata().getPrivateResourceUser().getUserName(), "User is the private user of the notebook");
    assertEquals("us-central1-a", resource.getAttributes().getLocation(), "The notebook uses the default location because location is not specified.");
    createAControlledAiNotebookInstanceWithoutSpecifiedInstanceId_validInstanceIdIsGenerated(resourceUserApi);
    createAControlledAiNotebookInstanceWithoutSpecifiedInstanceId_specifyLocation(resourceUserApi);
    String instanceName = String.format("projects/%s/locations/%s/instances/%s", resource.getAttributes().getProjectId(), resource.getAttributes().getLocation(), resource.getAttributes().getInstanceId());
    AIPlatformNotebooks userNotebooks = ClientTestUtils.getAIPlatformNotebooksClient(resourceUser);
    assertTrue(NotebookUtils.userHasProxyAccess(creationResult, resourceUser, resource.getAttributes().getProjectId()), "Private resource user has access to their notebook");
    assertFalse(NotebookUtils.userHasProxyAccess(creationResult, otherWorkspaceUser, resource.getAttributes().getProjectId()), "Other workspace user does not have access to a private notebook");
    // The user should be able to stop their notebook.
    userNotebooks.projects().locations().instances().stop(instanceName, new StopInstanceRequest());
    // The user should not be able to directly delete their notebook.
    GoogleJsonResponseException directDeleteForbidden = assertThrows(GoogleJsonResponseException.class, () -> userNotebooks.projects().locations().instances().delete(instanceName).execute());
    assertEquals(HttpStatus.SC_FORBIDDEN, directDeleteForbidden.getStatusCode(), "User may not delete notebook directly on GCP");
    // Any workspace user should be able to enumerate all created notebooks, even though they can't
    // read or write them.
    ResourceApi otherUserApi = ClientTestUtils.getResourceClient(otherWorkspaceUser, server);
    ResourceList notebookList = otherUserApi.enumerateResources(getWorkspaceId(), 0, 5, ResourceType.AI_NOTEBOOK, StewardshipType.CONTROLLED);
    assertEquals(3, notebookList.getResources().size());
    MultiResourcesUtils.assertResourceType(ResourceType.AI_NOTEBOOK, notebookList);
    // Delete the AI Notebook through WSM.
    DeleteControlledGcpAiNotebookInstanceResult deleteResult = resourceUserApi.deleteAiNotebookInstance(new DeleteControlledGcpAiNotebookInstanceRequest().jobControl(new JobControl().id(UUID.randomUUID().toString())), getWorkspaceId(), resourceId);
    String deleteJobId = deleteResult.getJobReport().getId();
    deleteResult = ClientTestUtils.pollWhileRunning(deleteResult, () -> resourceUserApi.getDeleteAiNotebookInstanceResult(getWorkspaceId(), deleteJobId), DeleteControlledGcpAiNotebookInstanceResult::getJobReport, Duration.ofSeconds(10));
    ClientTestUtils.assertJobSuccess("delete ai notebook", deleteResult.getJobReport(), deleteResult.getErrorReport());
    // Verify the notebook was deleted from WSM metadata.
    ApiException notebookIsMissing = assertThrows(ApiException.class, () -> resourceUserApi.getAiNotebookInstance(getWorkspaceId(), resourceId), "Notebook is deleted from WSM");
    assertEquals(HttpStatus.SC_NOT_FOUND, notebookIsMissing.getCode(), "Error from WSM is 404");
    // Verify the notebook was deleted from GCP.
    GoogleJsonResponseException notebookNotFound = assertThrows(GoogleJsonResponseException.class, () -> userNotebooks.projects().locations().instances().get(instanceName).execute(), "Notebook is deleted from GCP");
    // GCP may respond with either 403 or 404 depending on how quickly this is called after deleting
    // the notebook. Either response is valid in this case.
    assertThat("Error from GCP is 403 or 404", notebookNotFound.getStatusCode(), anyOf(equalTo(HttpStatus.SC_NOT_FOUND), equalTo(HttpStatus.SC_FORBIDDEN)));
}
Also used : GrantRoleRequestBody(bio.terra.workspace.model.GrantRoleRequestBody) CreatedControlledGcpAiNotebookInstanceResult(bio.terra.workspace.model.CreatedControlledGcpAiNotebookInstanceResult) DeleteControlledGcpAiNotebookInstanceRequest(bio.terra.workspace.model.DeleteControlledGcpAiNotebookInstanceRequest) DeleteControlledGcpAiNotebookInstanceResult(bio.terra.workspace.model.DeleteControlledGcpAiNotebookInstanceResult) JobControl(bio.terra.workspace.model.JobControl) AIPlatformNotebooks(com.google.api.services.notebooks.v1.AIPlatformNotebooks) GcpAiNotebookInstanceResource(bio.terra.workspace.model.GcpAiNotebookInstanceResource) StopInstanceRequest(com.google.api.services.notebooks.v1.model.StopInstanceRequest) GoogleJsonResponseException(com.google.api.client.googleapis.json.GoogleJsonResponseException) ControlledGcpResourceApi(bio.terra.workspace.api.ControlledGcpResourceApi) ResourceApi(bio.terra.workspace.api.ResourceApi) ResourceList(bio.terra.workspace.model.ResourceList) ControlledGcpResourceApi(bio.terra.workspace.api.ControlledGcpResourceApi) UUID(java.util.UUID) ApiException(bio.terra.workspace.client.ApiException) SuppressFBWarnings(edu.umd.cs.findbugs.annotations.SuppressFBWarnings)

Example 4 with CreatedControlledGcpAiNotebookInstanceResult

use of bio.terra.workspace.model.CreatedControlledGcpAiNotebookInstanceResult in project terra-workspace-manager by DataBiosphere.

the class NotebookUtils method userHasProxyAccess.

/**
 * Check whether the user has access to the Notebook through the proxy with a service account.
 *
 * <p>We can't directly test that we can go through the proxy to the Jupyter notebook without a
 * real Google user auth flow, so we check the necessary ingredients instead.
 */
public static boolean userHasProxyAccess(CreatedControlledGcpAiNotebookInstanceResult createdNotebook, TestUserSpecification user, String projectId) throws GeneralSecurityException, IOException {
    String instanceName = String.format("projects/%s/locations/%s/instances/%s", createdNotebook.getAiNotebookInstance().getAttributes().getProjectId(), createdNotebook.getAiNotebookInstance().getAttributes().getLocation(), createdNotebook.getAiNotebookInstance().getAttributes().getInstanceId());
    AIPlatformNotebooks userNotebooks = ClientTestUtils.getAIPlatformNotebooksClient(user);
    Instance instance;
    try {
        instance = userNotebooks.projects().locations().instances().get(instanceName).execute();
    } catch (GoogleJsonResponseException googleException) {
        // If we get a 403 or 404 when fetching the instance, the user does not have access.
        if (googleException.getStatusCode() == HttpStatus.SC_FORBIDDEN || googleException.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
            return false;
        } else {
            // If a different status code is thrown instead, rethrow here as that's an unexpected error.
            throw googleException;
        }
    }
    // Test that the user has access to the notebook with a service account through proxy mode.
    // git secrets gets a false positive if 'service_account' is double quoted.
    assertThat("Notebook has correct proxy mode access", instance.getMetadata(), Matchers.hasEntry("proxy-mode", "service_" + "account"));
    // The user needs to have the actAs permission on the service account.
    String actAsPermission = "iam.serviceAccounts.actAs";
    String serviceAccountName = String.format("projects/%s/serviceAccounts/%s", projectId, instance.getServiceAccount());
    List<String> maybePermissionsList = ClientTestUtils.getGcpIamClient(user).projects().serviceAccounts().testIamPermissions(serviceAccountName, new TestIamPermissionsRequest().setPermissions(List.of(actAsPermission))).execute().getPermissions();
    // GCP returns null rather than an empty list when a user does not have any permissions
    return Optional.ofNullable(maybePermissionsList).map(list -> list.contains(actAsPermission)).orElse(false);
}
Also used : AccessScope(bio.terra.workspace.model.AccessScope) CloningInstructionsEnum(bio.terra.workspace.model.CloningInstructionsEnum) ControlledGcpResourceApi(bio.terra.workspace.api.ControlledGcpResourceApi) Instance(com.google.api.services.notebooks.v1.model.Instance) JobControl(bio.terra.workspace.model.JobControl) ManagedBy(bio.terra.workspace.model.ManagedBy) CreateControlledGcpAiNotebookInstanceRequestBody(bio.terra.workspace.model.CreateControlledGcpAiNotebookInstanceRequestBody) HttpStatus(org.apache.http.HttpStatus) GoogleJsonResponseException(com.google.api.client.googleapis.json.GoogleJsonResponseException) GeneralSecurityException(java.security.GeneralSecurityException) Duration(java.time.Duration) DeleteControlledGcpAiNotebookInstanceResult(bio.terra.workspace.model.DeleteControlledGcpAiNotebookInstanceResult) MatcherAssert.assertThat(org.hamcrest.MatcherAssert.assertThat) GcpAiNotebookInstanceVmImage(bio.terra.workspace.model.GcpAiNotebookInstanceVmImage) Nullable(javax.annotation.Nullable) DeleteControlledGcpAiNotebookInstanceRequest(bio.terra.workspace.model.DeleteControlledGcpAiNotebookInstanceRequest) AIPlatformNotebooks(com.google.api.services.notebooks.v1.AIPlatformNotebooks) ControlledResourceCommonFields(bio.terra.workspace.model.ControlledResourceCommonFields) ApiException(bio.terra.workspace.client.ApiException) Matchers(org.hamcrest.Matchers) IOException(java.io.IOException) UUID(java.util.UUID) CreatedControlledGcpAiNotebookInstanceResult(bio.terra.workspace.model.CreatedControlledGcpAiNotebookInstanceResult) GcpAiNotebookInstanceCreationParameters(bio.terra.workspace.model.GcpAiNotebookInstanceCreationParameters) List(java.util.List) TestUserSpecification(bio.terra.testrunner.runner.config.TestUserSpecification) Optional(java.util.Optional) RandomStringUtils(org.apache.commons.lang3.RandomStringUtils) TestIamPermissionsRequest(com.google.api.services.iam.v1.model.TestIamPermissionsRequest) GoogleJsonResponseException(com.google.api.client.googleapis.json.GoogleJsonResponseException) Instance(com.google.api.services.notebooks.v1.model.Instance) TestIamPermissionsRequest(com.google.api.services.iam.v1.model.TestIamPermissionsRequest) AIPlatformNotebooks(com.google.api.services.notebooks.v1.AIPlatformNotebooks)

Example 5 with CreatedControlledGcpAiNotebookInstanceResult

use of bio.terra.workspace.model.CreatedControlledGcpAiNotebookInstanceResult in project terra-cli by DataBiosphere.

the class WorkspaceManagerService method createControlledGcpNotebookInstance.

/**
 * Call the Workspace Manager POST
 * "/api/workspaces/v1/{workspaceId}/resources/controlled/gcp/ai-notebook-instance" endpoint to
 * add a GCP notebook instance as a controlled resource in the workspace.
 *
 * @param workspaceId the workspace to add the resource to
 * @param createParams resource definition to create
 * @return the GCP notebook instance resource object
 */
public GcpAiNotebookInstanceResource createControlledGcpNotebookInstance(UUID workspaceId, CreateGcpNotebookParams createParams) {
    // convert the CLI object to a WSM request object
    String jobId = UUID.randomUUID().toString();
    CreateControlledGcpAiNotebookInstanceRequestBody createRequest = new CreateControlledGcpAiNotebookInstanceRequestBody().common(createCommonFields(createParams.resourceFields)).aiNotebookInstance(fromCLIObject(createParams)).jobControl(new JobControl().id(jobId));
    logger.debug("Create controlled GCP notebook request {}", createRequest);
    return handleClientExceptions(() -> {
        ControlledGcpResourceApi controlledGcpResourceApi = new ControlledGcpResourceApi(apiClient);
        // Start the GCP notebook creation job.
        HttpUtils.callWithRetries(() -> controlledGcpResourceApi.createAiNotebookInstance(createRequest, workspaceId), WorkspaceManagerService::isRetryable);
        // Poll the result endpoint until the job is no longer RUNNING.
        CreatedControlledGcpAiNotebookInstanceResult createResult = HttpUtils.pollWithRetries(() -> controlledGcpResourceApi.getCreateAiNotebookInstanceResult(workspaceId, jobId), (result) -> isDone(result.getJobReport()), WorkspaceManagerService::isRetryable, // Creating a GCP notebook instance should take less than ~10 minutes.
        60, Duration.ofSeconds(10));
        logger.debug("Create controlled GCP notebook result {}", createResult);
        throwIfJobNotCompleted(createResult.getJobReport(), createResult.getErrorReport());
        return createResult.getAiNotebookInstance();
    }, "Error creating controlled GCP Notebook instance in the workspace.");
}
Also used : CreatedControlledGcpAiNotebookInstanceResult(bio.terra.workspace.model.CreatedControlledGcpAiNotebookInstanceResult) CreateControlledGcpAiNotebookInstanceRequestBody(bio.terra.workspace.model.CreateControlledGcpAiNotebookInstanceRequestBody) JobControl(bio.terra.workspace.model.JobControl) ControlledGcpResourceApi(bio.terra.workspace.api.ControlledGcpResourceApi)

Aggregations

CreatedControlledGcpAiNotebookInstanceResult (bio.terra.workspace.model.CreatedControlledGcpAiNotebookInstanceResult)5 JobControl (bio.terra.workspace.model.JobControl)5 DeleteControlledGcpAiNotebookInstanceRequest (bio.terra.workspace.model.DeleteControlledGcpAiNotebookInstanceRequest)4 ControlledGcpResourceApi (bio.terra.workspace.api.ControlledGcpResourceApi)3 ApiException (bio.terra.workspace.client.ApiException)2 CreateControlledGcpAiNotebookInstanceRequestBody (bio.terra.workspace.model.CreateControlledGcpAiNotebookInstanceRequestBody)2 DeleteControlledGcpAiNotebookInstanceResult (bio.terra.workspace.model.DeleteControlledGcpAiNotebookInstanceResult)2 GoogleJsonResponseException (com.google.api.client.googleapis.json.GoogleJsonResponseException)2 AIPlatformNotebooks (com.google.api.services.notebooks.v1.AIPlatformNotebooks)2 UUID (java.util.UUID)2 TestUserSpecification (bio.terra.testrunner.runner.config.TestUserSpecification)1 ResourceApi (bio.terra.workspace.api.ResourceApi)1 AccessScope (bio.terra.workspace.model.AccessScope)1 CloningInstructionsEnum (bio.terra.workspace.model.CloningInstructionsEnum)1 ControlledResourceCommonFields (bio.terra.workspace.model.ControlledResourceCommonFields)1 GcpAiNotebookInstanceCreationParameters (bio.terra.workspace.model.GcpAiNotebookInstanceCreationParameters)1 GcpAiNotebookInstanceResource (bio.terra.workspace.model.GcpAiNotebookInstanceResource)1 GcpAiNotebookInstanceVmImage (bio.terra.workspace.model.GcpAiNotebookInstanceVmImage)1 GrantRoleRequestBody (bio.terra.workspace.model.GrantRoleRequestBody)1 ManagedBy (bio.terra.workspace.model.ManagedBy)1