@Override public void onCascadeEvent(BeforeAccountRemovedEvent event) throws Exception { for (WorkspaceImpl workspace : iterate( (maxItems, skipCount) -> workspaceManager.getByNamespace( event.getAccount().getName(), false, maxItems, skipCount))) { workspaceManager.removeWorkspace(workspace.getId()); } } }
@DELETE @Path("/{id}/environment/{name}") @ApiOperation( value = "Remove the environment from the workspace", notes = "This operation can be performed only by the workspace owner") @ApiResponses({ @ApiResponse(code = 204, message = "The environment successfully removed"), @ApiResponse(code = 403, message = "The user does not have access remove the environment"), @ApiResponse(code = 404, message = "The workspace not found"), @ApiResponse(code = 500, message = "Internal server error occurred") }) public void deleteEnvironment( @ApiParam("The workspace id") @PathParam("id") String id, @ApiParam("The name of the environment") @PathParam("name") String envName) throws ServerException, BadRequestException, NotFoundException, ConflictException, ForbiddenException { final WorkspaceImpl workspace = workspaceManager.getWorkspace(id); if (workspace.getConfig().getEnvironments().remove(envName) != null) { doUpdate(id, workspace); } }
@POST @Path("/{id}/runtime") @Produces(APPLICATION_JSON) @ApiOperation( value = "Start the workspace by the id", notes = "This operation can be performed only by the workspace owner." + "The workspace starts asynchronously") @ApiResponses({ @ApiResponse(code = 200, message = "The workspace is starting"), @ApiResponse(code = 404, message = "The workspace with specified id doesn't exist"), @ApiResponse( code = 403, message = "The user is not workspace owner." + "The operation is not allowed for the user"), @ApiResponse(code = 409, message = "Any conflict occurs during the workspace start"), @ApiResponse(code = 500, message = "Internal server error occurred") }) public WorkspaceDto startById( @ApiParam("The workspace id") @PathParam("id") String workspaceId, @ApiParam("The name of the workspace environment that should be used for start") @QueryParam("environment") String envName) throws ServerException, BadRequestException, NotFoundException, ForbiddenException, ConflictException { return asDtoWithLinksAndToken( workspaceManager.startWorkspace(workspaceId, envName, emptyMap())); }
/** Asynchronously starts given workspace. */ private void startAsync( WorkspaceImpl workspace, @Nullable String envName, Map<String, String> options) throws ConflictException, NotFoundException, ServerException { String env = getValidatedEnvironmentName(workspace, envName); workspace.getAttributes().put(UPDATED_ATTRIBUTE_NAME, Long.toString(currentTimeMillis())); workspaceDao.update(workspace); runtimes .startAsync(workspace, env, firstNonNull(options, Collections.emptyMap())) .thenAccept(aVoid -> handleStartupSuccess(workspace)) .exceptionally( ex -> { if (workspace.isTemporary()) { removeWorkspaceQuietly(workspace); } else { handleStartupError(workspace, ex.getCause()); } return null; }); }
/** * Asynchronously starts workspace from the given configuration. * * @param config workspace configuration from which workspace is created and started * @param namespace workspace name is unique in this namespace * @return starting workspace * @throws NullPointerException when {@code workspaceId} is null * @throws NotFoundException when workspace with given {@code workspaceId} doesn't exist * @throws ServerException when any other error occurs during workspace start */ public WorkspaceImpl startWorkspace( WorkspaceConfig config, String namespace, boolean isTemporary, Map<String, String> options) throws ServerException, NotFoundException, ConflictException, ValidationException { requireNonNull(config, "Required non-null configuration"); requireNonNull(namespace, "Required non-null namespace"); validator.validateConfig(config); final WorkspaceImpl workspace = doCreateWorkspace( config, accountManager.getByName(namespace), Collections.emptyMap(), isTemporary); startAsync(workspace, workspace.getConfig().getDefaultEnv(), options); return normalizeState(workspace, true); }
@GET @Path("/namespace/{namespace:.*}") @Produces(APPLICATION_JSON) @ApiOperation( value = "Get workspaces by given namespace", notes = "This operation can be performed only by authorized user", response = WorkspaceDto.class, responseContainer = "List") @ApiResponses({ @ApiResponse(code = 200, message = "The workspaces successfully fetched"), @ApiResponse(code = 500, message = "Internal server error occurred during workspaces fetching") }) public List<WorkspaceDto> getByNamespace( @ApiParam("Workspace status") @QueryParam("status") String status, @ApiParam("The namespace") @PathParam("namespace") String namespace) throws ServerException, BadRequestException { return withLinks( Pages.stream( (maxItems, skipCount) -> workspaceManager.getByNamespace(namespace, false, maxItems, skipCount)) .filter(ws -> status == null || status.equalsIgnoreCase(ws.getStatus().toString())) .map(DtoConverter::asDto) .collect(toList())); }
workspace = workspaceManager.createWorkspace(config, namespace, attributes); } catch (ValidationException x) { throw new BadRequestException(x.getMessage()); workspaceManager.startWorkspace(workspace.getId(), null, new HashMap<>());
private Workspace doUpdate(String id, Workspace update) throws BadRequestException, ConflictException, NotFoundException, ServerException { try { return workspaceManager.updateWorkspace(id, update); } catch (ValidationException x) { throw new BadRequestException(x.getMessage()); } }
/** * Asynchronously stops the workspace. * * @param workspaceId the id of the workspace to stop * @throws ServerException when any server error occurs * @throws NullPointerException when {@code workspaceId} is null * @throws NotFoundException when workspace {@code workspaceId} doesn't have runtime */ public void stopWorkspace(String workspaceId, Map<String, String> options) throws ServerException, NotFoundException, ConflictException { requireNonNull(workspaceId, "Required non-null workspace id"); final WorkspaceImpl workspace = normalizeState(workspaceDao.get(workspaceId), true); checkWorkspaceIsRunningOrStarting(workspace); if (!workspace.isTemporary()) { workspace.getAttributes().put(STOPPED_ATTRIBUTE_NAME, Long.toString(currentTimeMillis())); workspace.getAttributes().put(STOPPED_ABNORMALLY_ATTRIBUTE_NAME, Boolean.toString(false)); workspaceDao.update(workspace); } runtimes .stopAsync(workspace, options) .whenComplete( (aVoid, throwable) -> { if (workspace.isTemporary()) { removeWorkspaceQuietly(workspace); } }); }
@DELETE @Path("/{id}") @ApiOperation( value = "Removes the workspace", notes = "This operation can be performed only by the workspace owner") @ApiResponses({ @ApiResponse(code = 204, message = "The workspace successfully removed"), @ApiResponse(code = 403, message = "The user does not have access to remove the workspace"), @ApiResponse(code = 404, message = "The workspace doesn't exist"), @ApiResponse(code = 409, message = "The workspace is not stopped(has runtime)"), @ApiResponse(code = 500, message = "Internal server error occurred") }) public void delete(@ApiParam("The workspace id") @PathParam("id") String id) throws BadRequestException, ServerException, NotFoundException, ConflictException, ForbiddenException { workspaceManager.removeWorkspace(id); }
@Override public WorkspaceImpl createWorkspace( WorkspaceConfig config, String namespace, @Nullable Map<String, String> attributes) throws ServerException, ConflictException, NotFoundException, ValidationException { checkMaxEnvironmentRam(config); String accountId = accountManager.getByName(namespace).getId(); try (@SuppressWarnings("unused") Unlocker u = resourcesLocks.lock(accountId)) { checkWorkspaceResourceAvailability(accountId); return super.createWorkspace(config, namespace, attributes); } }
/** * Creates a new {@link Workspace} instance based on the given configuration and the instance * attributes. * * @param config the workspace config to create the new workspace instance * @param namespace workspace name is unique in this namespace * @param attributes workspace instance attributes * @return new workspace instance * @throws NullPointerException when either {@code config} or {@code namespace} is null * @throws NotFoundException when account with given id was not found * @throws ConflictException when any conflict occurs (e.g Workspace with such name already exists * for {@code owner}) * @throws ServerException when any other error occurs * @throws ValidationException when incoming configuration or attributes are not valid */ @Traced public WorkspaceImpl createWorkspace( WorkspaceConfig config, String namespace, @Nullable Map<String, String> attributes) throws ServerException, NotFoundException, ConflictException, ValidationException { requireNonNull(config, "Required non-null config"); requireNonNull(namespace, "Required non-null namespace"); validator.validateConfig(config); if (attributes != null) { validator.validateAttributes(attributes); } return doCreateWorkspace(config, accountManager.getByName(namespace), attributes, false); }
private String getValidatedEnvironmentName(WorkspaceImpl workspace, @Nullable String envName) throws NotFoundException, ServerException { if (envName != null && !workspace.getConfig().getEnvironments().containsKey(envName)) { throw new NotFoundException( format( "Workspace '%s:%s' doesn't contain environment '%s'", workspace.getNamespace(), workspace.getConfig().getName(), envName)); } envName = firstNonNull(envName, workspace.getConfig().getDefaultEnv()); if (envName == null && SidecarToolingWorkspaceUtil.isSidecarBasedWorkspace( workspace.getConfig().getAttributes())) { // Sidecar-based workspaces are allowed not to have any environments return null; } // validate environment in advance if (envName == null) { throw new NotFoundException( format( "Workspace %s:%s can't use null environment", workspace.getNamespace(), workspace.getConfig().getName())); } try { runtimes.validate(workspace.getConfig().getEnvironments().get(envName)); } catch (InfrastructureException | ValidationException e) { throw new ServerException(e); } return envName; }
@Override public Optional<Resource> getUsedResource(String accountId) throws NotFoundException, ServerException { final Account account = accountManager.getById(accountId); final List<WorkspaceImpl> accountWorkspaces = Pages.stream( (maxItems, skipCount) -> workspaceManagerProvider .get() .getByNamespace(account.getName(), false, maxItems, skipCount)) .collect(Collectors.toList()); if (!accountWorkspaces.isEmpty()) { return Optional.of( new ResourceImpl( WorkspaceResourceType.ID, accountWorkspaces.size(), WorkspaceResourceType.UNIT)); } else { return Optional.empty(); } } }
private void handleStartupSuccess(Workspace workspace) { workspace.getAttributes().remove(STOPPED_ATTRIBUTE_NAME); workspace.getAttributes().remove(STOPPED_ABNORMALLY_ATTRIBUTE_NAME); workspace.getAttributes().remove(ERROR_MESSAGE_ATTRIBUTE_NAME); try { updateWorkspace(workspace.getId(), workspace); } catch (NotFoundException | ServerException | ValidationException | ConflictException e) { LOG.warn( String.format( "Cannot clear error status status of the workspace %s. Error is: %s", workspace.getId(), e.getMessage())); } }
@Override public void onCascadeEvent(BeforeAccountRemovedEvent event) throws Exception { for (WorkspaceImpl workspace : iterate( (maxItems, skipCount) -> workspaceManager.getByNamespace( event.getAccount().getName(), false, maxItems, skipCount))) { workspaceManager.removeWorkspace(workspace.getId()); } } }
@DELETE @Path("/{id}/project/{path:.*}") @ApiOperation( value = "Remove the project from the workspace", notes = "This operation can be performed only by the workspace owner") @ApiResponses({ @ApiResponse(code = 204, message = "The project successfully removed"), @ApiResponse(code = 403, message = "The user does not have access remove the project"), @ApiResponse(code = 404, message = "The workspace not found"), @ApiResponse(code = 500, message = "Internal server error occurred") }) public void deleteProject( @ApiParam("The workspace id") @PathParam("id") String id, @ApiParam("The name of the project to remove") @PathParam("path") String path) throws ServerException, BadRequestException, NotFoundException, ConflictException, ForbiddenException { final WorkspaceImpl workspace = workspaceManager.getWorkspace(id); final String normalizedPath = path.startsWith("/") ? path : '/' + path; if (workspace .getConfig() .getProjects() .removeIf(project -> project.getPath().equals(normalizedPath))) { doUpdate(id, workspace); } }
@Override public Optional<Resource> getUsedResource(String accountId) throws NotFoundException, ServerException { final Account account = accountManager.getById(accountId); final long currentlyUsedRuntimes = Pages.stream( (maxItems, skipCount) -> workspaceManagerProvider .get() .getByNamespace(account.getName(), false, maxItems, skipCount)) .filter(ws -> STOPPED != ws.getStatus()) .count(); if (currentlyUsedRuntimes > 0) { return Optional.of( new ResourceImpl( RuntimeResourceType.ID, currentlyUsedRuntimes, RuntimeResourceType.UNIT)); } else { return Optional.empty(); } } }
private void handleStartupError(Workspace workspace, Throwable t) { workspace .getAttributes() .put( ERROR_MESSAGE_ATTRIBUTE_NAME, t instanceof RuntimeException ? t.getCause().getMessage() : t.getMessage()); workspace.getAttributes().put(STOPPED_ATTRIBUTE_NAME, Long.toString(currentTimeMillis())); workspace.getAttributes().put(STOPPED_ABNORMALLY_ATTRIBUTE_NAME, Boolean.toString(true)); try { updateWorkspace(workspace.getId(), workspace); } catch (NotFoundException | ServerException | ValidationException | ConflictException e) { LOG.warn( String.format( "Cannot set error status of the workspace %s. Error is: %s", workspace.getId(), e.getMessage())); } }
workspaceManager.startWorkspace(config, namespace, isTemporary, new HashMap<>()); return asDtoWithLinksAndToken(workspace); } catch (ValidationException x) {