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());
}
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());
}
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)));
}
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);
}
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.");
}
Aggregations