зеркало из https://github.com/microsoft/AzureTRE.git
Support resource templateVersion update (#2908)
* resource update mechanism Co-authored-by: Tamir Kamara <26870601+tamirkamara@users.noreply.github.com>
This commit is contained in:
Родитель
099d5f9a42
Коммит
21caf4c607
|
@ -10,3 +10,4 @@ Describe the current behavior you are modifying. Please also remember to update
|
|||
- Note any pending work (with links to the issues that will address them)
|
||||
- Update documentation
|
||||
- Update CHANGELOG.md if needed
|
||||
- Increment template version if needed, for guidelines see [Authoring templates - versioning](https://microsoft.github.io/AzureTRE/tre-workspace-authors/authoring-workspace-templates/#versioning)
|
||||
|
|
|
@ -10,6 +10,7 @@ FEATURES:
|
|||
|
||||
ENHANCEMENTS:
|
||||
* Remove Porter's Docker mixin as it's not in use ([#2889](https://github.com/microsoft/AzureTRE/pull/2889))
|
||||
* Support template version update ([#2908](https://github.com/microsoft/AzureTRE/pull/2908))
|
||||
|
||||
BUG FIXES:
|
||||
* Private endpoints for AppInsights are now provisioning successfully and consistently ([#2841](https://github.com/microsoft/AzureTRE/pull/2841))
|
||||
|
|
|
@ -1 +1 @@
|
|||
__version__ = "0.5.23"
|
||||
__version__ = "0.6.0"
|
||||
|
|
|
@ -4,7 +4,7 @@ from fastapi import APIRouter, Depends, HTTPException, Header, status, Response
|
|||
from jsonschema.exceptions import ValidationError
|
||||
|
||||
from db.repositories.operations import OperationRepository
|
||||
from db.errors import DuplicateEntity, UserNotAuthorizedToUseTemplate
|
||||
from db.errors import DuplicateEntity, MajorVersionUpdateDenied, UserNotAuthorizedToUseTemplate, TargetTemplateVersionDoesNotExist, VersionDowngradeDenied
|
||||
from api.dependencies.database import get_repository
|
||||
from api.dependencies.shared_services import get_shared_service_by_id_from_path, get_operation_by_id_from_path
|
||||
from db.repositories.resource_templates import ResourceTemplateRepository
|
||||
|
@ -75,9 +75,9 @@ async def create_shared_service(response: Response, shared_service_input: Shared
|
|||
response_model=OperationInResponse,
|
||||
name=strings.API_UPDATE_SHARED_SERVICE,
|
||||
dependencies=[Depends(get_current_admin_user), Depends(get_shared_service_by_id_from_path)])
|
||||
async def patch_shared_service(shared_service_patch: ResourcePatch, response: Response, user=Depends(get_current_admin_user), shared_service_repo=Depends(get_repository(SharedServiceRepository)), shared_service=Depends(get_shared_service_by_id_from_path), resource_template_repo=Depends(get_repository(ResourceTemplateRepository)), operations_repo=Depends(get_repository(OperationRepository)), etag: str = Header(...)) -> SharedServiceInResponse:
|
||||
async def patch_shared_service(shared_service_patch: ResourcePatch, response: Response, user=Depends(get_current_admin_user), shared_service_repo=Depends(get_repository(SharedServiceRepository)), shared_service=Depends(get_shared_service_by_id_from_path), resource_template_repo=Depends(get_repository(ResourceTemplateRepository)), operations_repo=Depends(get_repository(OperationRepository)), etag: str = Header(...), force_version_update: bool = False) -> SharedServiceInResponse:
|
||||
try:
|
||||
patched_shared_service, resource_template = shared_service_repo.patch_shared_service(shared_service, shared_service_patch, etag, resource_template_repo, user)
|
||||
patched_shared_service, resource_template = shared_service_repo.patch_shared_service(shared_service, shared_service_patch, etag, resource_template_repo, user, force_version_update)
|
||||
operation = await send_resource_request_message(
|
||||
resource=patched_shared_service,
|
||||
operations_repo=operations_repo,
|
||||
|
@ -93,6 +93,8 @@ async def patch_shared_service(shared_service_patch: ResourcePatch, response: Re
|
|||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=strings.ETAG_CONFLICT)
|
||||
except ValidationError as v:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=v.message)
|
||||
except (MajorVersionUpdateDenied, TargetTemplateVersionDoesNotExist, VersionDowngradeDenied) as e:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
||||
|
||||
|
||||
@shared_services_router.delete("/shared-services/{shared_service_id}", response_model=OperationInResponse, name=strings.API_DELETE_SHARED_SERVICE, dependencies=[Depends(get_current_admin_user)])
|
||||
|
|
|
@ -7,7 +7,7 @@ from jsonschema.exceptions import ValidationError
|
|||
from api.dependencies.database import get_repository
|
||||
from api.dependencies.workspaces import get_operation_by_id_from_path, get_workspace_by_id_from_path, get_deployed_workspace_by_id_from_path, get_deployed_workspace_service_by_id_from_path, get_workspace_service_by_id_from_path, get_user_resource_by_id_from_path
|
||||
|
||||
from db.errors import UserNotAuthorizedToUseTemplate
|
||||
from db.errors import MajorVersionUpdateDenied, TargetTemplateVersionDoesNotExist, UserNotAuthorizedToUseTemplate, VersionDowngradeDenied
|
||||
from db.repositories.operations import OperationRepository
|
||||
from db.repositories.resource_templates import ResourceTemplateRepository
|
||||
from db.repositories.user_resources import UserResourceRepository
|
||||
|
@ -116,9 +116,9 @@ async def create_workspace(workspace_create: WorkspaceInCreate, response: Respon
|
|||
|
||||
|
||||
@workspaces_core_router.patch("/workspaces/{workspace_id}", status_code=status.HTTP_202_ACCEPTED, response_model=OperationInResponse, name=strings.API_UPDATE_WORKSPACE, dependencies=[Depends(get_current_admin_user)])
|
||||
async def patch_workspace(workspace_patch: ResourcePatch, response: Response, user=Depends(get_current_admin_user), workspace=Depends(get_workspace_by_id_from_path), workspace_repo=Depends(get_repository(WorkspaceRepository)), resource_template_repo=Depends(get_repository(ResourceTemplateRepository)), operations_repo=Depends(get_repository(OperationRepository)), etag: str = Header(...)) -> OperationInResponse:
|
||||
async def patch_workspace(workspace_patch: ResourcePatch, response: Response, user=Depends(get_current_admin_user), workspace=Depends(get_workspace_by_id_from_path), workspace_repo: WorkspaceRepository = Depends(get_repository(WorkspaceRepository)), resource_template_repo=Depends(get_repository(ResourceTemplateRepository)), operations_repo=Depends(get_repository(OperationRepository)), etag: str = Header(...), force_version_update: bool = False) -> OperationInResponse:
|
||||
try:
|
||||
patched_workspace, resource_template = workspace_repo.patch_workspace(workspace, workspace_patch, etag, resource_template_repo, user)
|
||||
patched_workspace, resource_template = workspace_repo.patch_workspace(workspace, workspace_patch, etag, resource_template_repo, user, force_version_update)
|
||||
operation = await send_resource_request_message(
|
||||
resource=patched_workspace,
|
||||
operations_repo=operations_repo,
|
||||
|
@ -133,6 +133,8 @@ async def patch_workspace(workspace_patch: ResourcePatch, response: Response, us
|
|||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=strings.ETAG_CONFLICT)
|
||||
except ValidationError as v:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=v.message)
|
||||
except (MajorVersionUpdateDenied, TargetTemplateVersionDoesNotExist, VersionDowngradeDenied) as e:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
||||
|
||||
|
||||
@workspaces_core_router.delete("/workspaces/{workspace_id}", response_model=OperationInResponse, name=strings.API_DELETE_WORKSPACE, dependencies=[Depends(get_current_admin_user)])
|
||||
|
@ -242,9 +244,9 @@ async def create_workspace_service(response: Response, workspace_service_input:
|
|||
|
||||
|
||||
@workspace_services_workspace_router.patch("/workspaces/{workspace_id}/workspace-services/{service_id}", status_code=status.HTTP_202_ACCEPTED, response_model=OperationInResponse, name=strings.API_UPDATE_WORKSPACE_SERVICE, dependencies=[Depends(get_current_workspace_owner_or_researcher_user), Depends(get_workspace_by_id_from_path)])
|
||||
async def patch_workspace_service(workspace_service_patch: ResourcePatch, response: Response, user=Depends(get_current_workspace_owner_user), workspace_service_repo=Depends(get_repository(WorkspaceServiceRepository)), workspace_service=Depends(get_workspace_service_by_id_from_path), resource_template_repo=Depends(get_repository(ResourceTemplateRepository)), operations_repo=Depends(get_repository(OperationRepository)), etag: str = Header(...)) -> OperationInResponse:
|
||||
async def patch_workspace_service(workspace_service_patch: ResourcePatch, response: Response, user=Depends(get_current_workspace_owner_user), workspace_service_repo=Depends(get_repository(WorkspaceServiceRepository)), workspace_service=Depends(get_workspace_service_by_id_from_path), resource_template_repo=Depends(get_repository(ResourceTemplateRepository)), operations_repo=Depends(get_repository(OperationRepository)), etag: str = Header(...), force_version_update: bool = False) -> OperationInResponse:
|
||||
try:
|
||||
patched_workspace_service, resource_template = workspace_service_repo.patch_workspace_service(workspace_service, workspace_service_patch, etag, resource_template_repo, user)
|
||||
patched_workspace_service, resource_template = workspace_service_repo.patch_workspace_service(workspace_service, workspace_service_patch, etag, resource_template_repo, user, force_version_update)
|
||||
operation = await send_resource_request_message(
|
||||
resource=patched_workspace_service,
|
||||
operations_repo=operations_repo,
|
||||
|
@ -259,6 +261,8 @@ async def patch_workspace_service(workspace_service_patch: ResourcePatch, respon
|
|||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=strings.ETAG_CONFLICT)
|
||||
except ValidationError as v:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=v.message)
|
||||
except (MajorVersionUpdateDenied, TargetTemplateVersionDoesNotExist, VersionDowngradeDenied) as e:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
||||
|
||||
|
||||
@workspace_services_workspace_router.delete("/workspaces/{workspace_id}/workspace-services/{service_id}", response_model=OperationInResponse, name=strings.API_DELETE_WORKSPACE_SERVICE, dependencies=[Depends(get_current_workspace_owner_user)])
|
||||
|
@ -417,11 +421,12 @@ async def patch_user_resource(
|
|||
user_resource_repo=Depends(get_repository(UserResourceRepository)),
|
||||
resource_template_repo=Depends(get_repository(ResourceTemplateRepository)),
|
||||
operations_repo=Depends(get_repository(OperationRepository)),
|
||||
etag: str = Header(...)) -> OperationInResponse:
|
||||
etag: str = Header(...),
|
||||
force_version_update: bool = False) -> OperationInResponse:
|
||||
validate_user_has_valid_role_for_user_resource(user, user_resource)
|
||||
|
||||
try:
|
||||
patched_user_resource, resource_template = user_resource_repo.patch_user_resource(user_resource, user_resource_patch, etag, resource_template_repo, workspace_service.templateName, user)
|
||||
patched_user_resource, resource_template = user_resource_repo.patch_user_resource(user_resource, user_resource_patch, etag, resource_template_repo, workspace_service.templateName, user, force_version_update)
|
||||
operation = await send_resource_request_message(
|
||||
resource=patched_user_resource,
|
||||
operations_repo=operations_repo,
|
||||
|
@ -437,6 +442,8 @@ async def patch_user_resource(
|
|||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=strings.ETAG_CONFLICT)
|
||||
except ValidationError as v:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=v.message)
|
||||
except (MajorVersionUpdateDenied, TargetTemplateVersionDoesNotExist, VersionDowngradeDenied) as e:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
||||
|
||||
|
||||
# user resource actions
|
||||
|
|
|
@ -24,3 +24,15 @@ class InvalidInput(Exception):
|
|||
|
||||
class UserNotAuthorizedToUseTemplate(Exception):
|
||||
"""Raised when user attempts to use a template they aren't authorized to use"""
|
||||
|
||||
|
||||
class MajorVersionUpdateDenied(Exception):
|
||||
"""Raised when user attempts to update a resource with a major version."""
|
||||
|
||||
|
||||
class TargetTemplateVersionDoesNotExist(Exception):
|
||||
"""Raised when user attempts to upgrade a resource to a version which was not registered."""
|
||||
|
||||
|
||||
class VersionDowngradeDenied(Exception):
|
||||
"""Raised when user attempts to downgrade a resource to a lower version."""
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import copy
|
||||
import semantic_version
|
||||
from datetime import datetime
|
||||
from typing import Tuple, List
|
||||
|
||||
from azure.cosmos import CosmosClient
|
||||
from azure.cosmos.exceptions import CosmosResourceNotFoundError
|
||||
from core import config
|
||||
from db.errors import EntityDoesNotExist, UserNotAuthorizedToUseTemplate
|
||||
from db.errors import VersionDowngradeDenied, EntityDoesNotExist, MajorVersionUpdateDenied, TargetTemplateVersionDoesNotExist, UserNotAuthorizedToUseTemplate
|
||||
from db.repositories.base import BaseRepository
|
||||
from db.repositories.resource_templates import ResourceTemplateRepository
|
||||
from jsonschema import validate
|
||||
|
@ -96,7 +97,7 @@ class ResourceRepository(BaseRepository):
|
|||
|
||||
return parse_obj_as(ResourceTemplate, template)
|
||||
|
||||
def patch_resource(self, resource: Resource, resource_patch: ResourcePatch, resource_template: ResourceTemplate, etag: str, resource_template_repo: ResourceTemplateRepository, user: User) -> Tuple[Resource, ResourceTemplate]:
|
||||
def patch_resource(self, resource: Resource, resource_patch: ResourcePatch, resource_template: ResourceTemplate, etag: str, resource_template_repo: ResourceTemplateRepository, user: User, force_version_update: bool = False) -> Tuple[Resource, ResourceTemplate]:
|
||||
# create a deep copy of the resource to use for history, create the history item + add to history list
|
||||
resource_copy = copy.deepcopy(resource)
|
||||
history_item = ResourceHistoryItem(
|
||||
|
@ -104,7 +105,8 @@ class ResourceRepository(BaseRepository):
|
|||
properties=resource_copy.properties,
|
||||
resourceVersion=resource_copy.resourceVersion,
|
||||
updatedWhen=resource_copy.updatedWhen,
|
||||
user=resource_copy.user
|
||||
user=resource_copy.user,
|
||||
templateVersion=resource_copy.templateVersion
|
||||
)
|
||||
resource.history.append(history_item)
|
||||
|
||||
|
@ -116,6 +118,10 @@ class ResourceRepository(BaseRepository):
|
|||
if resource_patch.isEnabled is not None:
|
||||
resource.isEnabled = resource_patch.isEnabled
|
||||
|
||||
if resource_patch.templateVersion is not None:
|
||||
self.validate_template_version_patch(resource, resource_patch, resource_template_repo, resource_template, force_version_update)
|
||||
resource.templateVersion = resource_patch.templateVersion
|
||||
|
||||
if resource_patch.properties is not None and len(resource_patch.properties) > 0:
|
||||
self.validate_patch(resource_patch, resource_template_repo, resource_template)
|
||||
|
||||
|
@ -125,6 +131,27 @@ class ResourceRepository(BaseRepository):
|
|||
self.update_item_with_etag(resource, etag)
|
||||
return resource, resource_template
|
||||
|
||||
def validate_template_version_patch(self, resource: Resource, resource_patch: ResourcePatch, resource_template_repo: ResourceTemplateRepository, resource_template: ResourceTemplate, force_version_update: bool = False):
|
||||
parent_resource_id = None
|
||||
if resource.resourceType == ResourceType.UserResource:
|
||||
parent_resource_id = resource.parentWorkspaceServiceId
|
||||
|
||||
# validate Major upgrade
|
||||
desired_version = semantic_version.Version(resource_patch.templateVersion)
|
||||
current_version = semantic_version.Version(resource.templateVersion)
|
||||
|
||||
if not force_version_update:
|
||||
if desired_version.major > current_version.major:
|
||||
raise MajorVersionUpdateDenied(f'Attempt to upgrade from {current_version} to {desired_version} denied. major version upgrade is not allowed.')
|
||||
elif desired_version < current_version:
|
||||
raise VersionDowngradeDenied(f'Attempt to downgrade from {current_version} to {desired_version} denied. version downgrade is not allowed.')
|
||||
|
||||
# validate if target template with desired version is registered
|
||||
try:
|
||||
resource_template_repo.get_template_by_name_and_version(resource.templateName, resource_patch.templateVersion, resource_template.resourceType, parent_resource_id)
|
||||
except EntityDoesNotExist:
|
||||
raise TargetTemplateVersionDoesNotExist(f"Template '{resource_template.name}' not found for resource type '{resource_template.resourceType}' with target template version '{resource_patch.templateVersion}'")
|
||||
|
||||
def validate_patch(self, resource_patch: ResourcePatch, resource_template_repo: ResourceTemplateRepository, resource_template: ResourceTemplate):
|
||||
# get the enriched (combined) template
|
||||
enriched_template = resource_template_repo.enrich_template(resource_template, is_update=True)
|
||||
|
|
|
@ -83,7 +83,7 @@ class SharedServiceRepository(ResourceRepository):
|
|||
|
||||
return shared_service, template
|
||||
|
||||
def patch_shared_service(self, shared_service: SharedService, shared_service_patch: ResourcePatch, etag: str, resource_template_repo: ResourceTemplateRepository, user: User) -> Tuple[SharedService, ResourceTemplate]:
|
||||
def patch_shared_service(self, shared_service: SharedService, shared_service_patch: ResourcePatch, etag: str, resource_template_repo: ResourceTemplateRepository, user: User, force_version_update: bool) -> Tuple[SharedService, ResourceTemplate]:
|
||||
# get shared service template
|
||||
shared_service_template = resource_template_repo.get_template_by_name_and_version(shared_service.templateName, shared_service.templateVersion, ResourceType.SharedService)
|
||||
return self.patch_resource(shared_service, shared_service_patch, shared_service_template, etag, resource_template_repo, user)
|
||||
return self.patch_resource(shared_service, shared_service_patch, shared_service_template, etag, resource_template_repo, user, force_version_update)
|
||||
|
|
|
@ -67,7 +67,7 @@ class UserResourceRepository(ResourceRepository):
|
|||
def get_user_resource_spec_params(self):
|
||||
return self.get_resource_base_spec_params()
|
||||
|
||||
def patch_user_resource(self, user_resource: UserResource, user_resource_patch: ResourcePatch, etag: str, resource_template_repo: ResourceTemplateRepository, parent_template_name: str, user: User) -> Tuple[UserResource, ResourceTemplate]:
|
||||
def patch_user_resource(self, user_resource: UserResource, user_resource_patch: ResourcePatch, etag: str, resource_template_repo: ResourceTemplateRepository, parent_template_name: str, user: User, force_version_update: bool) -> Tuple[UserResource, ResourceTemplate]:
|
||||
# get user resource template
|
||||
user_resource_template = resource_template_repo.get_template_by_name_and_version(user_resource.templateName, user_resource.templateVersion, ResourceType.UserResource, parent_service_name=parent_template_name)
|
||||
return self.patch_resource(user_resource, user_resource_patch, user_resource_template, etag, resource_template_repo, user)
|
||||
return self.patch_resource(user_resource, user_resource_patch, user_resource_template, etag, resource_template_repo, user, force_version_update)
|
||||
|
|
|
@ -74,7 +74,7 @@ class WorkspaceServiceRepository(ResourceRepository):
|
|||
|
||||
return workspace_service, template
|
||||
|
||||
def patch_workspace_service(self, workspace_service: WorkspaceService, workspace_service_patch: ResourcePatch, etag: str, resource_template_repo: ResourceTemplateRepository, user: User) -> Tuple[WorkspaceService, ResourceTemplate]:
|
||||
def patch_workspace_service(self, workspace_service: WorkspaceService, workspace_service_patch: ResourcePatch, etag: str, resource_template_repo: ResourceTemplateRepository, user: User, force_version_update: bool) -> Tuple[WorkspaceService, ResourceTemplate]:
|
||||
# get workspace service template
|
||||
workspace_service_template = resource_template_repo.get_template_by_name_and_version(workspace_service.templateName, workspace_service.templateVersion, ResourceType.WorkspaceService)
|
||||
return self.patch_resource(workspace_service, workspace_service_patch, workspace_service_template, etag, resource_template_repo, user)
|
||||
return self.patch_resource(workspace_service, workspace_service_patch, workspace_service_template, etag, resource_template_repo, user, force_version_update)
|
||||
|
|
|
@ -137,10 +137,10 @@ class WorkspaceRepository(ResourceRepository):
|
|||
new_address_space = generate_new_cidr(networks, cidr_netmask)
|
||||
return new_address_space
|
||||
|
||||
def patch_workspace(self, workspace: Workspace, workspace_patch: ResourcePatch, etag: str, resource_template_repo: ResourceTemplateRepository, user: User) -> Tuple[Workspace, ResourceTemplate]:
|
||||
def patch_workspace(self, workspace: Workspace, workspace_patch: ResourcePatch, etag: str, resource_template_repo: ResourceTemplateRepository, user: User, force_version_update: bool) -> Tuple[Workspace, ResourceTemplate]:
|
||||
# get the workspace template
|
||||
workspace_template = resource_template_repo.get_template_by_name_and_version(workspace.templateName, workspace.templateVersion, ResourceType.Workspace)
|
||||
return self.patch_resource(workspace, workspace_patch, workspace_template, etag, resource_template_repo, user)
|
||||
return self.patch_resource(workspace, workspace_patch, workspace_template, etag, resource_template_repo, user, force_version_update)
|
||||
|
||||
def get_workspace_spec_params(self, full_workspace_id: str):
|
||||
params = self.get_resource_base_spec_params()
|
||||
|
|
|
@ -25,6 +25,7 @@ class ResourceHistoryItem(AzureTREModel):
|
|||
resourceVersion: int
|
||||
updatedWhen: float
|
||||
user: dict = {}
|
||||
templateVersion: Optional[str]
|
||||
|
||||
|
||||
class Resource(AzureTREModel):
|
||||
|
|
|
@ -5,11 +5,13 @@ from pydantic import BaseModel
|
|||
class ResourcePatch(BaseModel):
|
||||
isEnabled: Optional[bool]
|
||||
properties: Optional[dict]
|
||||
templateVersion: Optional[str]
|
||||
|
||||
class Config:
|
||||
schema_extra = {
|
||||
"example": {
|
||||
"isEnabled": False,
|
||||
"templateVersion": "1.0.1",
|
||||
"properties": {
|
||||
"display_name": "the display name",
|
||||
"description": "a description",
|
||||
|
|
|
@ -163,7 +163,7 @@ class TestSharedServiceRoutesThatRequireAdminRights:
|
|||
|
||||
modified_shared_service = sample_shared_service()
|
||||
modified_shared_service.isEnabled = False
|
||||
modified_shared_service.history = [ResourceHistoryItem(properties=copy.deepcopy(modified_shared_service.properties), isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user())]
|
||||
modified_shared_service.history = [ResourceHistoryItem(properties=copy.deepcopy(modified_shared_service.properties), isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user(), templateVersion=modified_shared_service.templateVersion)]
|
||||
modified_shared_service.resourceVersion = 1
|
||||
modified_shared_service.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
modified_shared_service.user = create_admin_user()
|
||||
|
@ -172,3 +172,93 @@ class TestSharedServiceRoutesThatRequireAdminRights:
|
|||
update_item_mock.assert_called_once_with(modified_shared_service, etag)
|
||||
|
||||
assert response.status_code == status.HTTP_202_ACCEPTED
|
||||
|
||||
# [PATCH] /shared-services/{shared_service_id}
|
||||
@patch("api.routes.shared_services.SharedServiceRepository.get_timestamp", return_value=FAKE_UPDATE_TIMESTAMP)
|
||||
@patch("api.dependencies.shared_services.SharedServiceRepository.get_shared_service_by_id", return_value=sample_shared_service(SHARED_SERVICE_ID))
|
||||
@patch("api.routes.shared_services.ResourceTemplateRepository.get_template_by_name_and_version", return_value=sample_shared_service())
|
||||
@patch("api.routes.shared_services.SharedServiceRepository.update_item_with_etag", return_value=sample_shared_service())
|
||||
@patch("api.routes.shared_services.send_resource_request_message", return_value=sample_resource_operation(resource_id=SHARED_SERVICE_ID, operation_id=OPERATION_ID))
|
||||
async def test_patch_shared_service_with_upgrade_minor_version_patches_shared_service(self, _, update_item_mock, __, ___, ____, app, client):
|
||||
etag = "some-etag-value"
|
||||
shared_service_patch = {"templateVersion": "0.2.0"}
|
||||
|
||||
modified_shared_service = sample_shared_service()
|
||||
modified_shared_service.isEnabled = True
|
||||
modified_shared_service.history = [ResourceHistoryItem(properties=copy.deepcopy(modified_shared_service.properties), isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user(), templateVersion=modified_shared_service.templateVersion)]
|
||||
modified_shared_service.resourceVersion = 1
|
||||
modified_shared_service.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
modified_shared_service.user = create_admin_user()
|
||||
modified_shared_service.templateVersion = "0.2.0"
|
||||
|
||||
response = await client.patch(app.url_path_for(strings.API_UPDATE_SHARED_SERVICE, shared_service_id=SHARED_SERVICE_ID), json=shared_service_patch, headers={"etag": etag})
|
||||
update_item_mock.assert_called_once_with(modified_shared_service, etag)
|
||||
|
||||
assert response.status_code == status.HTTP_202_ACCEPTED
|
||||
|
||||
# [PATCH] /shared-services/{shared_service_id}
|
||||
@patch("api.routes.shared_services.SharedServiceRepository.get_timestamp", return_value=FAKE_UPDATE_TIMESTAMP)
|
||||
@patch("api.dependencies.shared_services.SharedServiceRepository.get_shared_service_by_id", return_value=sample_shared_service(SHARED_SERVICE_ID))
|
||||
@patch("api.routes.shared_services.ResourceTemplateRepository.get_template_by_name_and_version", return_value=sample_shared_service())
|
||||
@patch("api.routes.shared_services.SharedServiceRepository.update_item_with_etag", return_value=sample_shared_service())
|
||||
@patch("api.routes.shared_services.send_resource_request_message", return_value=sample_resource_operation(resource_id=SHARED_SERVICE_ID, operation_id=OPERATION_ID))
|
||||
async def test_patch_shared_service_with_upgrade_major_version_and_force_update_patches_shared_service(self, _, update_item_mock, __, ___, ____, app, client):
|
||||
etag = "some-etag-value"
|
||||
shared_service_patch = {"templateVersion": "2.0.0"}
|
||||
|
||||
modified_shared_service = sample_shared_service()
|
||||
modified_shared_service.isEnabled = True
|
||||
modified_shared_service.history = [ResourceHistoryItem(properties=copy.deepcopy(modified_shared_service.properties), isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user(), templateVersion=modified_shared_service.templateVersion)]
|
||||
modified_shared_service.resourceVersion = 1
|
||||
modified_shared_service.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
modified_shared_service.user = create_admin_user()
|
||||
modified_shared_service.templateVersion = "2.0.0"
|
||||
|
||||
response = await client.patch(app.url_path_for(strings.API_UPDATE_SHARED_SERVICE, shared_service_id=SHARED_SERVICE_ID) + "?force_version_update=True", json=shared_service_patch, headers={"etag": etag})
|
||||
update_item_mock.assert_called_once_with(modified_shared_service, etag)
|
||||
|
||||
assert response.status_code == status.HTTP_202_ACCEPTED
|
||||
|
||||
# [PATCH] /shared-services/{shared_service_id}
|
||||
@patch("api.routes.shared_services.SharedServiceRepository.get_timestamp", return_value=FAKE_UPDATE_TIMESTAMP)
|
||||
@patch("api.dependencies.shared_services.SharedServiceRepository.get_shared_service_by_id", return_value=sample_shared_service(SHARED_SERVICE_ID))
|
||||
@patch("api.routes.shared_services.ResourceTemplateRepository.get_template_by_name_and_version", return_value=None)
|
||||
@patch("api.routes.shared_services.SharedServiceRepository.update_item_with_etag", return_value=sample_shared_service())
|
||||
@patch("api.routes.shared_services.send_resource_request_message", return_value=sample_resource_operation(resource_id=SHARED_SERVICE_ID, operation_id=OPERATION_ID))
|
||||
async def test_patch_shared_service_with_upgrade_major_version_returns_bad_request(self, _, update_item_mock, __, ___, ____, app, client):
|
||||
etag = "some-etag-value"
|
||||
shared_service_patch = {"templateVersion": "2.0.0"}
|
||||
|
||||
modified_shared_service = sample_shared_service()
|
||||
modified_shared_service.isEnabled = True
|
||||
modified_shared_service.history = [ResourceHistoryItem(properties=copy.deepcopy(modified_shared_service.properties), isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user(), templateVersion=modified_shared_service.templateVersion)]
|
||||
modified_shared_service.resourceVersion = 1
|
||||
modified_shared_service.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
modified_shared_service.user = create_admin_user()
|
||||
|
||||
response = await client.patch(app.url_path_for(strings.API_UPDATE_SHARED_SERVICE, shared_service_id=SHARED_SERVICE_ID), json=shared_service_patch, headers={"etag": etag})
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
assert response.text == 'Attempt to upgrade from 0.1.0 to 2.0.0 denied. major version upgrade is not allowed.'
|
||||
|
||||
# [PATCH] /shared-services/{shared_service_id}
|
||||
@patch("api.routes.shared_services.SharedServiceRepository.get_timestamp", return_value=FAKE_UPDATE_TIMESTAMP)
|
||||
@patch("api.dependencies.shared_services.SharedServiceRepository.get_shared_service_by_id", return_value=sample_shared_service(SHARED_SERVICE_ID))
|
||||
@patch("api.routes.shared_services.ResourceTemplateRepository.get_template_by_name_and_version", return_value=None)
|
||||
@patch("api.routes.shared_services.SharedServiceRepository.update_item_with_etag", return_value=sample_shared_service())
|
||||
@patch("api.routes.shared_services.send_resource_request_message", return_value=sample_resource_operation(resource_id=SHARED_SERVICE_ID, operation_id=OPERATION_ID))
|
||||
async def test_patch_shared_service_with_downgrade_version_returns_bad_request(self, _, update_item_mock, __, ___, ____, app, client):
|
||||
etag = "some-etag-value"
|
||||
shared_service_patch = {"templateVersion": "0.0.1"}
|
||||
|
||||
modified_shared_service = sample_shared_service()
|
||||
modified_shared_service.isEnabled = True
|
||||
modified_shared_service.history = [ResourceHistoryItem(properties=copy.deepcopy(modified_shared_service.properties), isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user(), templateVersion=modified_shared_service.templateVersion)]
|
||||
modified_shared_service.resourceVersion = 1
|
||||
modified_shared_service.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
modified_shared_service.user = create_admin_user()
|
||||
|
||||
response = await client.patch(app.url_path_for(strings.API_UPDATE_SHARED_SERVICE, shared_service_id=SHARED_SERVICE_ID), json=shared_service_patch, headers={"etag": etag})
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
assert response.text == 'Attempt to downgrade from 0.1.0 to 0.0.1 denied. version downgrade is not allowed.'
|
||||
|
|
|
@ -452,7 +452,7 @@ class TestWorkspaceRoutesThatRequireAdminRights:
|
|||
|
||||
modified_workspace = sample_workspace()
|
||||
modified_workspace.isEnabled = False
|
||||
modified_workspace.history = [ResourceHistoryItem(properties={'client_id': '12345', 'scope_id': 'test_scope_id'}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user())]
|
||||
modified_workspace.history = [ResourceHistoryItem(properties={'client_id': '12345', 'scope_id': 'test_scope_id'}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user(), templateVersion=modified_workspace.templateVersion)]
|
||||
modified_workspace.resourceVersion = 1
|
||||
modified_workspace.user = create_admin_user()
|
||||
modified_workspace.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
|
@ -462,6 +462,96 @@ class TestWorkspaceRoutesThatRequireAdminRights:
|
|||
update_item_mock.assert_called_once_with(modified_workspace, etag)
|
||||
assert response.status_code == status.HTTP_202_ACCEPTED
|
||||
|
||||
# [PATCH] /workspaces/{workspace_id}
|
||||
@ patch("api.routes.workspaces.send_resource_request_message", return_value=sample_resource_operation(resource_id=WORKSPACE_ID, operation_id=OPERATION_ID))
|
||||
@ patch("api.dependencies.workspaces.WorkspaceRepository.get_workspace_by_id", return_value=sample_workspace())
|
||||
@ patch("api.routes.workspaces.WorkspaceRepository.update_item_with_etag", return_value=sample_workspace())
|
||||
@ patch("api.routes.workspaces.ResourceTemplateRepository.get_template_by_name_and_version", return_value=None)
|
||||
@ patch("api.routes.workspaces.WorkspaceRepository.get_timestamp", return_value=FAKE_UPDATE_TIMESTAMP)
|
||||
async def test_patch_workspaces_with_upgrade_major_version_returns_bad_request(self, _, __, update_item_mock, ___, ____, app, client):
|
||||
workspace_patch = {"templateVersion": "2.0.0"}
|
||||
etag = "some-etag-value"
|
||||
|
||||
modified_workspace = sample_workspace()
|
||||
modified_workspace.isEnabled = True
|
||||
modified_workspace.history = [ResourceHistoryItem(properties={'client_id': '12345', 'scope_id': 'test_scope_id'}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user(), templateVersion=modified_workspace.templateVersion)]
|
||||
modified_workspace.resourceVersion = 1
|
||||
modified_workspace.user = create_admin_user()
|
||||
modified_workspace.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
|
||||
response = await client.patch(app.url_path_for(strings.API_UPDATE_WORKSPACE, workspace_id=WORKSPACE_ID), json=workspace_patch, headers={"etag": etag})
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
assert response.text == 'Attempt to upgrade from 0.1.0 to 2.0.0 denied. major version upgrade is not allowed.'
|
||||
|
||||
# [PATCH] /workspaces/{workspace_id}
|
||||
@ patch("api.routes.workspaces.send_resource_request_message", return_value=sample_resource_operation(resource_id=WORKSPACE_ID, operation_id=OPERATION_ID))
|
||||
@ patch("api.dependencies.workspaces.WorkspaceRepository.get_workspace_by_id", return_value=sample_workspace())
|
||||
@ patch("api.routes.workspaces.WorkspaceRepository.update_item_with_etag", return_value=sample_workspace())
|
||||
@ patch("api.routes.workspaces.ResourceTemplateRepository.get_template_by_name_and_version", return_value=sample_workspace())
|
||||
@ patch("api.routes.workspaces.WorkspaceRepository.get_timestamp", return_value=FAKE_UPDATE_TIMESTAMP)
|
||||
async def test_patch_workspaces_with_upgrade_major_version_and_force_update_returns_patched_workspace(self, _, __, update_item_mock, ___, ____, app, client):
|
||||
workspace_patch = {"templateVersion": "2.0.0"}
|
||||
etag = "some-etag-value"
|
||||
|
||||
modified_workspace = sample_workspace()
|
||||
modified_workspace.isEnabled = True
|
||||
modified_workspace.history = [ResourceHistoryItem(properties={'client_id': '12345', 'scope_id': 'test_scope_id'}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user(), templateVersion=modified_workspace.templateVersion)]
|
||||
modified_workspace.resourceVersion = 1
|
||||
modified_workspace.user = create_admin_user()
|
||||
modified_workspace.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
modified_workspace.templateVersion = "2.0.0"
|
||||
|
||||
response = await client.patch(app.url_path_for(strings.API_UPDATE_WORKSPACE, workspace_id=WORKSPACE_ID) + "?force_version_update=True", json=workspace_patch, headers={"etag": etag})
|
||||
|
||||
update_item_mock.assert_called_once_with(modified_workspace, etag)
|
||||
assert response.status_code == status.HTTP_202_ACCEPTED
|
||||
|
||||
# [PATCH] /workspaces/{workspace_id}
|
||||
@ patch("api.routes.workspaces.send_resource_request_message", return_value=sample_resource_operation(resource_id=WORKSPACE_ID, operation_id=OPERATION_ID))
|
||||
@ patch("api.dependencies.workspaces.WorkspaceRepository.get_workspace_by_id", return_value=sample_workspace())
|
||||
@ patch("api.routes.workspaces.WorkspaceRepository.update_item_with_etag", return_value=sample_workspace())
|
||||
@ patch("api.routes.workspaces.ResourceTemplateRepository.get_template_by_name_and_version", return_value=None)
|
||||
@ patch("api.routes.workspaces.WorkspaceRepository.get_timestamp", return_value=FAKE_UPDATE_TIMESTAMP)
|
||||
async def test_patch_workspaces_with_downgrade_version_returns_bad_request(self, _, __, update_item_mock, ___, ____, app, client):
|
||||
workspace_patch = {"templateVersion": "0.0.1"}
|
||||
etag = "some-etag-value"
|
||||
|
||||
modified_workspace = sample_workspace()
|
||||
modified_workspace.isEnabled = True
|
||||
modified_workspace.history = [ResourceHistoryItem(properties={'client_id': '12345', 'scope_id': 'test_scope_id'}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user(), templateVersion=modified_workspace.templateVersion)]
|
||||
modified_workspace.resourceVersion = 1
|
||||
modified_workspace.user = create_admin_user()
|
||||
modified_workspace.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
|
||||
response = await client.patch(app.url_path_for(strings.API_UPDATE_WORKSPACE, workspace_id=WORKSPACE_ID), json=workspace_patch, headers={"etag": etag})
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
assert response.text == 'Attempt to downgrade from 0.1.0 to 0.0.1 denied. version downgrade is not allowed.'
|
||||
|
||||
# [PATCH] /workspaces/{workspace_id}
|
||||
@ patch("api.routes.workspaces.send_resource_request_message", return_value=sample_resource_operation(resource_id=WORKSPACE_ID, operation_id=OPERATION_ID))
|
||||
@ patch("api.dependencies.workspaces.WorkspaceRepository.get_workspace_by_id", return_value=sample_workspace())
|
||||
@ patch("api.routes.workspaces.WorkspaceRepository.update_item_with_etag", return_value=sample_workspace())
|
||||
@ patch("api.routes.workspaces.ResourceTemplateRepository.get_template_by_name_and_version", return_value=sample_workspace())
|
||||
@ patch("api.routes.workspaces.WorkspaceRepository.get_timestamp", return_value=FAKE_UPDATE_TIMESTAMP)
|
||||
async def test_patch_workspaces_with_upgrade_minor_version_patches_workspace(self, _, __, update_item_mock, ___, ____, app, client):
|
||||
workspace_patch = {"templateVersion": "0.2.0"}
|
||||
etag = "some-etag-value"
|
||||
|
||||
modified_workspace = sample_workspace()
|
||||
modified_workspace.isEnabled = True
|
||||
modified_workspace.history = [ResourceHistoryItem(properties={'client_id': '12345', 'scope_id': 'test_scope_id'}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user(), templateVersion=modified_workspace.templateVersion)]
|
||||
modified_workspace.resourceVersion = 1
|
||||
modified_workspace.user = create_admin_user()
|
||||
modified_workspace.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
modified_workspace.templateVersion = "0.2.0"
|
||||
|
||||
response = await client.patch(app.url_path_for(strings.API_UPDATE_WORKSPACE, workspace_id=WORKSPACE_ID), json=workspace_patch, headers={"etag": etag})
|
||||
|
||||
update_item_mock.assert_called_once_with(modified_workspace, etag)
|
||||
assert response.status_code == status.HTTP_202_ACCEPTED
|
||||
|
||||
# [PATCH] /workspaces/{workspace_id}
|
||||
@ patch("api.dependencies.workspaces.WorkspaceRepository.get_workspace_by_id", return_value=sample_workspace())
|
||||
@ patch("api.routes.workspaces.WorkspaceRepository.update_item_with_etag", side_effect=CosmosAccessConditionFailedError)
|
||||
|
@ -472,7 +562,7 @@ class TestWorkspaceRoutesThatRequireAdminRights:
|
|||
etag = "some-etag-value"
|
||||
modified_workspace = sample_workspace()
|
||||
modified_workspace.isEnabled = False
|
||||
modified_workspace.history = [ResourceHistoryItem(properties={'client_id': '12345', 'scope_id': 'test_scope_id'}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user())]
|
||||
modified_workspace.history = [ResourceHistoryItem(properties={'client_id': '12345', 'scope_id': 'test_scope_id'}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user(), templateVersion=modified_workspace.templateVersion)]
|
||||
modified_workspace.resourceVersion = 1
|
||||
modified_workspace.user = create_admin_user()
|
||||
modified_workspace.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
|
@ -704,13 +794,13 @@ class TestWorkspaceServiceRoutesThatRequireOwnerRights:
|
|||
@ patch("api.dependencies.workspaces.UserResourceRepository.get_user_resource_by_id", return_value=sample_user_resource_object())
|
||||
@ patch("api.routes.workspaces.UserResourceRepository.update_item_with_etag", return_value=sample_user_resource_object())
|
||||
@ patch("api.routes.workspaces.UserResourceRepository.get_timestamp", return_value=FAKE_UPDATE_TIMESTAMP)
|
||||
async def test_patch_user_resources_patches_user_resource(self, _, update_item_mock, __, ___, ____, _____, ______, _______, app, client):
|
||||
async def test_patch_user_resource_patches_user_resource(self, _, update_item_mock, __, ___, ____, _____, ______, _______, app, client):
|
||||
user_resource_service_patch = {"isEnabled": False}
|
||||
etag = "some-etag-value"
|
||||
|
||||
modified_user_resource = sample_user_resource_object()
|
||||
modified_user_resource.isEnabled = False
|
||||
modified_user_resource.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_researcher_user())]
|
||||
modified_user_resource.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_researcher_user(), templateVersion=modified_user_resource.templateVersion)]
|
||||
modified_user_resource.resourceVersion = 1
|
||||
modified_user_resource.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
modified_user_resource.user = create_workspace_owner_user()
|
||||
|
@ -720,6 +810,108 @@ class TestWorkspaceServiceRoutesThatRequireOwnerRights:
|
|||
update_item_mock.assert_called_once_with(modified_user_resource, etag)
|
||||
assert response.status_code == status.HTTP_202_ACCEPTED
|
||||
|
||||
# [PATCH] /workspaces/{workspace_id}/workspace-services/{service_id}/user-resources/{resource_id}
|
||||
@ patch("api.routes.workspaces.send_resource_request_message", return_value=sample_resource_operation(resource_id=USER_RESOURCE_ID, operation_id=OPERATION_ID))
|
||||
@ patch("api.routes.workspaces.ResourceTemplateRepository.get_template_by_name_and_version", return_value=sample_workspace_service())
|
||||
@ patch("api.routes.workspaces.validate_user_has_valid_role_for_user_resource")
|
||||
@ patch("api.dependencies.workspaces.WorkspaceServiceRepository.get_workspace_service_by_id", return_value=sample_workspace_service())
|
||||
@ patch("api.dependencies.workspaces.WorkspaceRepository.get_workspace_by_id", return_value=sample_workspace())
|
||||
@ patch("api.dependencies.workspaces.UserResourceRepository.get_user_resource_by_id", return_value=sample_user_resource_object())
|
||||
@ patch("api.routes.workspaces.UserResourceRepository.update_item_with_etag", return_value=sample_user_resource_object())
|
||||
@ patch("api.routes.workspaces.UserResourceRepository.get_timestamp", return_value=FAKE_UPDATE_TIMESTAMP)
|
||||
async def test_patch_user_resource_with_upgrade_major_version_returns_bad_request(self, _, update_item_mock, __, ___, ____, _____, ______, _______, app, client):
|
||||
user_resource_service_patch = {"templateVersion": "2.0.0"}
|
||||
etag = "some-etag-value"
|
||||
|
||||
modified_user_resource = sample_user_resource_object()
|
||||
modified_user_resource.isEnabled = True
|
||||
modified_user_resource.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_researcher_user(), templateVersion=modified_user_resource.templateVersion)]
|
||||
modified_user_resource.resourceVersion = 1
|
||||
modified_user_resource.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
modified_user_resource.user = create_workspace_owner_user()
|
||||
|
||||
response = await client.patch(app.url_path_for(strings.API_UPDATE_USER_RESOURCE, workspace_id=WORKSPACE_ID, service_id=SERVICE_ID, resource_id=USER_RESOURCE_ID), json=user_resource_service_patch, headers={"etag": etag})
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
assert response.text == 'Attempt to upgrade from 0.1.0 to 2.0.0 denied. major version upgrade is not allowed.'
|
||||
|
||||
# [PATCH] /workspaces/{workspace_id}/workspace-services/{service_id}/user-resources/{resource_id}
|
||||
@ patch("api.routes.workspaces.send_resource_request_message", return_value=sample_resource_operation(resource_id=USER_RESOURCE_ID, operation_id=OPERATION_ID))
|
||||
@ patch("api.routes.workspaces.ResourceTemplateRepository.get_template_by_name_and_version", return_value=sample_workspace_service())
|
||||
@ patch("api.routes.workspaces.validate_user_has_valid_role_for_user_resource")
|
||||
@ patch("api.dependencies.workspaces.WorkspaceServiceRepository.get_workspace_service_by_id", return_value=sample_workspace_service())
|
||||
@ patch("api.dependencies.workspaces.WorkspaceRepository.get_workspace_by_id", return_value=sample_workspace())
|
||||
@ patch("api.dependencies.workspaces.UserResourceRepository.get_user_resource_by_id", return_value=sample_user_resource_object())
|
||||
@ patch("api.routes.workspaces.UserResourceRepository.update_item_with_etag", return_value=sample_user_resource_object())
|
||||
@ patch("api.routes.workspaces.UserResourceRepository.get_timestamp", return_value=FAKE_UPDATE_TIMESTAMP)
|
||||
async def test_patch_user_resource_with_upgrade_major_version_and_force_update_returns_patched_user_resource(self, _, update_item_mock, __, ___, ____, _____, ______, _______, app, client):
|
||||
user_resource_service_patch = {"templateVersion": "2.0.0"}
|
||||
etag = "some-etag-value"
|
||||
|
||||
modified_user_resource = sample_user_resource_object()
|
||||
modified_user_resource.isEnabled = True
|
||||
modified_user_resource.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_researcher_user(), templateVersion=modified_user_resource.templateVersion)]
|
||||
modified_user_resource.resourceVersion = 1
|
||||
modified_user_resource.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
modified_user_resource.user = create_workspace_owner_user()
|
||||
modified_user_resource.templateVersion = "2.0.0"
|
||||
|
||||
response = await client.patch(app.url_path_for(strings.API_UPDATE_USER_RESOURCE, workspace_id=WORKSPACE_ID, service_id=SERVICE_ID, resource_id=USER_RESOURCE_ID) + "?force_version_update=True", json=user_resource_service_patch, headers={"etag": etag})
|
||||
|
||||
update_item_mock.assert_called_once_with(modified_user_resource, etag)
|
||||
assert response.status_code == status.HTTP_202_ACCEPTED
|
||||
|
||||
# [PATCH] /workspaces/{workspace_id}/workspace-services/{service_id}/user-resources/{resource_id}
|
||||
@ patch("api.routes.workspaces.send_resource_request_message", return_value=sample_resource_operation(resource_id=USER_RESOURCE_ID, operation_id=OPERATION_ID))
|
||||
@ patch("api.routes.workspaces.ResourceTemplateRepository.get_template_by_name_and_version", return_value=sample_workspace_service())
|
||||
@ patch("api.routes.workspaces.validate_user_has_valid_role_for_user_resource")
|
||||
@ patch("api.dependencies.workspaces.WorkspaceServiceRepository.get_workspace_service_by_id", return_value=sample_workspace_service())
|
||||
@ patch("api.dependencies.workspaces.WorkspaceRepository.get_workspace_by_id", return_value=sample_workspace())
|
||||
@ patch("api.dependencies.workspaces.UserResourceRepository.get_user_resource_by_id", return_value=sample_user_resource_object())
|
||||
@ patch("api.routes.workspaces.UserResourceRepository.update_item_with_etag", return_value=sample_user_resource_object())
|
||||
@ patch("api.routes.workspaces.UserResourceRepository.get_timestamp", return_value=FAKE_UPDATE_TIMESTAMP)
|
||||
async def test_patch_user_resource_with_downgrade_version_returns_bad_request(self, _, update_item_mock, __, ___, ____, _____, ______, _______, app, client):
|
||||
user_resource_service_patch = {"templateVersion": "0.0.1"}
|
||||
etag = "some-etag-value"
|
||||
|
||||
modified_user_resource = sample_user_resource_object()
|
||||
modified_user_resource.isEnabled = True
|
||||
modified_user_resource.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_researcher_user(), templateVersion=modified_user_resource.templateVersion)]
|
||||
modified_user_resource.resourceVersion = 1
|
||||
modified_user_resource.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
modified_user_resource.user = create_workspace_owner_user()
|
||||
|
||||
response = await client.patch(app.url_path_for(strings.API_UPDATE_USER_RESOURCE, workspace_id=WORKSPACE_ID, service_id=SERVICE_ID, resource_id=USER_RESOURCE_ID), json=user_resource_service_patch, headers={"etag": etag})
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
assert response.text == 'Attempt to downgrade from 0.1.0 to 0.0.1 denied. version downgrade is not allowed.'
|
||||
|
||||
# [PATCH] /workspaces/{workspace_id}/workspace-services/{service_id}/user-resources/{resource_id}
|
||||
@ patch("api.routes.workspaces.send_resource_request_message", return_value=sample_resource_operation(resource_id=USER_RESOURCE_ID, operation_id=OPERATION_ID))
|
||||
@ patch("api.routes.workspaces.ResourceTemplateRepository.get_template_by_name_and_version", return_value=sample_workspace_service())
|
||||
@ patch("api.routes.workspaces.validate_user_has_valid_role_for_user_resource")
|
||||
@ patch("api.dependencies.workspaces.WorkspaceServiceRepository.get_workspace_service_by_id", return_value=sample_workspace_service())
|
||||
@ patch("api.dependencies.workspaces.WorkspaceRepository.get_workspace_by_id", return_value=sample_workspace())
|
||||
@ patch("api.dependencies.workspaces.UserResourceRepository.get_user_resource_by_id", return_value=sample_user_resource_object())
|
||||
@ patch("api.routes.workspaces.UserResourceRepository.update_item_with_etag", return_value=sample_user_resource_object())
|
||||
@ patch("api.routes.workspaces.UserResourceRepository.get_timestamp", return_value=FAKE_UPDATE_TIMESTAMP)
|
||||
async def test_patch_user_resource_with_upgrade_minor_version_patches_user_resource(self, _, update_item_mock, __, ___, ____, _____, ______, _______, app, client):
|
||||
user_resource_service_patch = {"templateVersion": "0.2.0"}
|
||||
etag = "some-etag-value"
|
||||
|
||||
modified_user_resource = sample_user_resource_object()
|
||||
modified_user_resource.isEnabled = True
|
||||
modified_user_resource.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_researcher_user(), templateVersion=modified_user_resource.templateVersion)]
|
||||
modified_user_resource.resourceVersion = 1
|
||||
modified_user_resource.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
modified_user_resource.user = create_workspace_owner_user()
|
||||
modified_user_resource.templateVersion = "0.2.0"
|
||||
|
||||
response = await client.patch(app.url_path_for(strings.API_UPDATE_USER_RESOURCE, workspace_id=WORKSPACE_ID, service_id=SERVICE_ID, resource_id=USER_RESOURCE_ID), json=user_resource_service_patch, headers={"etag": etag})
|
||||
|
||||
update_item_mock.assert_called_once_with(modified_user_resource, etag)
|
||||
assert response.status_code == status.HTTP_202_ACCEPTED
|
||||
|
||||
# [PATCH] /workspaces/{workspace_id}/workspace-services/{service_id}/user-resources/{resource_id}
|
||||
@ patch("api.routes.workspaces.send_resource_request_message", return_value=sample_resource_operation(resource_id=USER_RESOURCE_ID, operation_id=OPERATION_ID))
|
||||
@ patch("api.routes.workspaces.UserResourceRepository.update_item_with_etag", return_value=sample_user_resource_object())
|
||||
|
@ -734,7 +926,7 @@ class TestWorkspaceServiceRoutesThatRequireOwnerRights:
|
|||
|
||||
modified_resource = sample_user_resource_object()
|
||||
modified_resource.isEnabled = False
|
||||
modified_resource.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_researcher_user())]
|
||||
modified_resource.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_researcher_user(), templateVersion=modified_resource.templateVersion)]
|
||||
modified_resource.resourceVersion = 1
|
||||
modified_resource.properties["vm_size"] = "large"
|
||||
modified_resource.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
|
@ -812,7 +1004,7 @@ class TestWorkspaceServiceRoutesThatRequireOwnerRights:
|
|||
|
||||
modified_workspace_service = sample_workspace_service()
|
||||
modified_workspace_service.isEnabled = False
|
||||
modified_workspace_service.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_owner_user())]
|
||||
modified_workspace_service.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_owner_user(), templateVersion=modified_workspace_service.templateVersion)]
|
||||
modified_workspace_service.resourceVersion = 1
|
||||
modified_workspace_service.user = create_workspace_owner_user()
|
||||
modified_workspace_service.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
|
@ -822,6 +1014,111 @@ class TestWorkspaceServiceRoutesThatRequireOwnerRights:
|
|||
|
||||
assert response.status_code == status.HTTP_202_ACCEPTED
|
||||
|
||||
# [PATCH] /workspaces/{workspace_id}/services/{service_id}
|
||||
@ patch("api.routes.workspaces.send_resource_request_message", return_value=sample_resource_operation(resource_id=WORKSPACE_ID, operation_id=OPERATION_ID))
|
||||
@ patch("api.routes.workspaces.ResourceTemplateRepository.get_template_by_name_and_version", return_value=sample_workspace_service())
|
||||
@ patch("api.dependencies.workspaces.WorkspaceServiceRepository.get_workspace_service_by_id", return_value=sample_workspace_service())
|
||||
@ patch("api.dependencies.workspaces.WorkspaceRepository.get_workspace_by_id")
|
||||
@ patch("api.routes.workspaces.WorkspaceServiceRepository.update_item_with_etag", return_value=sample_workspace_service())
|
||||
@ patch("api.routes.workspaces.WorkspaceServiceRepository.get_timestamp", return_value=FAKE_UPDATE_TIMESTAMP)
|
||||
async def test_patch_workspace_service_with_upgrade_major_version_returns_bad_request(self, _, update_item_mock, get_workspace_mock, __, ___, ____, app, client):
|
||||
auth_info_user_in_workspace_owner_role = {'sp_id': 'ab123', 'roles': {'WorkspaceOwner': 'ab124', 'WorkspaceResearcher': 'ab125'}}
|
||||
|
||||
get_workspace_mock.return_value = sample_deployed_workspace(WORKSPACE_ID, auth_info_user_in_workspace_owner_role)
|
||||
etag = "some-etag-value"
|
||||
workspace_service_patch = {"templateVersion": "2.0.0"}
|
||||
|
||||
modified_workspace_service = sample_workspace_service()
|
||||
modified_workspace_service.isEnabled = True
|
||||
modified_workspace_service.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_owner_user(), templateVersion=modified_workspace_service.templateVersion)]
|
||||
modified_workspace_service.resourceVersion = 1
|
||||
modified_workspace_service.user = create_workspace_owner_user()
|
||||
modified_workspace_service.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
|
||||
response = await client.patch(app.url_path_for(strings.API_UPDATE_WORKSPACE_SERVICE, workspace_id=WORKSPACE_ID, service_id=SERVICE_ID), json=workspace_service_patch, headers={"etag": etag})
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
assert response.text == 'Attempt to upgrade from 0.1.0 to 2.0.0 denied. major version upgrade is not allowed.'
|
||||
|
||||
@ patch("api.routes.workspaces.send_resource_request_message", return_value=sample_resource_operation(resource_id=WORKSPACE_ID, operation_id=OPERATION_ID))
|
||||
@ patch("api.routes.workspaces.ResourceTemplateRepository.get_template_by_name_and_version", return_value=sample_workspace_service())
|
||||
@ patch("api.dependencies.workspaces.WorkspaceServiceRepository.get_workspace_service_by_id", return_value=sample_workspace_service())
|
||||
@ patch("api.dependencies.workspaces.WorkspaceRepository.get_workspace_by_id")
|
||||
@ patch("api.routes.workspaces.WorkspaceServiceRepository.update_item_with_etag", return_value=sample_workspace_service())
|
||||
@ patch("api.routes.workspaces.WorkspaceServiceRepository.get_timestamp", return_value=FAKE_UPDATE_TIMESTAMP)
|
||||
async def test_patch_workspace_service_with_upgrade_major_version_and_force_update_returns_patched_workspace_service(self, _, update_item_mock, get_workspace_mock, __, ___, ____, app, client):
|
||||
auth_info_user_in_workspace_owner_role = {'sp_id': 'ab123', 'roles': {'WorkspaceOwner': 'ab124', 'WorkspaceResearcher': 'ab125'}}
|
||||
|
||||
get_workspace_mock.return_value = sample_deployed_workspace(WORKSPACE_ID, auth_info_user_in_workspace_owner_role)
|
||||
etag = "some-etag-value"
|
||||
workspace_service_patch = {"templateVersion": "2.0.0"}
|
||||
|
||||
modified_workspace_service = sample_workspace_service()
|
||||
modified_workspace_service.isEnabled = True
|
||||
modified_workspace_service.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_owner_user(), templateVersion=modified_workspace_service.templateVersion)]
|
||||
modified_workspace_service.resourceVersion = 1
|
||||
modified_workspace_service.user = create_workspace_owner_user()
|
||||
modified_workspace_service.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
modified_workspace_service.templateVersion = "2.0.0"
|
||||
|
||||
response = await client.patch(app.url_path_for(strings.API_UPDATE_WORKSPACE_SERVICE, workspace_id=WORKSPACE_ID, service_id=SERVICE_ID) + "?force_version_update=True", json=workspace_service_patch, headers={"etag": etag})
|
||||
|
||||
update_item_mock.assert_called_once_with(modified_workspace_service, etag)
|
||||
assert response.status_code == status.HTTP_202_ACCEPTED
|
||||
|
||||
# [PATCH] /workspaces/{workspace_id}/services/{service_id}
|
||||
@ patch("api.routes.workspaces.send_resource_request_message", return_value=sample_resource_operation(resource_id=WORKSPACE_ID, operation_id=OPERATION_ID))
|
||||
@ patch("api.routes.workspaces.ResourceTemplateRepository.get_template_by_name_and_version", return_value=sample_workspace_service())
|
||||
@ patch("api.dependencies.workspaces.WorkspaceServiceRepository.get_workspace_service_by_id", return_value=sample_workspace_service())
|
||||
@ patch("api.dependencies.workspaces.WorkspaceRepository.get_workspace_by_id")
|
||||
@ patch("api.routes.workspaces.WorkspaceServiceRepository.update_item_with_etag", return_value=sample_workspace_service())
|
||||
@ patch("api.routes.workspaces.WorkspaceServiceRepository.get_timestamp", return_value=FAKE_UPDATE_TIMESTAMP)
|
||||
async def test_patch_workspace_service_with_downgrade_version_returns_bad_request(self, _, update_item_mock, get_workspace_mock, __, ___, ____, app, client):
|
||||
auth_info_user_in_workspace_owner_role = {'sp_id': 'ab123', 'roles': {'WorkspaceOwner': 'ab124', 'WorkspaceResearcher': 'ab125'}}
|
||||
|
||||
get_workspace_mock.return_value = sample_deployed_workspace(WORKSPACE_ID, auth_info_user_in_workspace_owner_role)
|
||||
etag = "some-etag-value"
|
||||
workspace_service_patch = {"templateVersion": "0.0.1"}
|
||||
|
||||
modified_workspace_service = sample_workspace_service()
|
||||
modified_workspace_service.isEnabled = True
|
||||
modified_workspace_service.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_owner_user(), templateVersion=modified_workspace_service.templateVersion)]
|
||||
modified_workspace_service.resourceVersion = 1
|
||||
modified_workspace_service.user = create_workspace_owner_user()
|
||||
modified_workspace_service.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
|
||||
response = await client.patch(app.url_path_for(strings.API_UPDATE_WORKSPACE_SERVICE, workspace_id=WORKSPACE_ID, service_id=SERVICE_ID), json=workspace_service_patch, headers={"etag": etag})
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
assert response.text == 'Attempt to downgrade from 0.1.0 to 0.0.1 denied. version downgrade is not allowed.'
|
||||
|
||||
# [PATCH] /workspaces/{workspace_id}/services/{service_id}
|
||||
@ patch("api.routes.workspaces.send_resource_request_message", return_value=sample_resource_operation(resource_id=WORKSPACE_ID, operation_id=OPERATION_ID))
|
||||
@ patch("api.routes.workspaces.ResourceTemplateRepository.get_template_by_name_and_version", return_value=sample_workspace_service())
|
||||
@ patch("api.dependencies.workspaces.WorkspaceServiceRepository.get_workspace_service_by_id", return_value=sample_workspace_service())
|
||||
@ patch("api.dependencies.workspaces.WorkspaceRepository.get_workspace_by_id")
|
||||
@ patch("api.routes.workspaces.WorkspaceServiceRepository.update_item_with_etag", return_value=sample_workspace_service())
|
||||
@ patch("api.routes.workspaces.WorkspaceServiceRepository.get_timestamp", return_value=FAKE_UPDATE_TIMESTAMP)
|
||||
async def test_patch_workspace_service_with_upgrade_minor_version_patches_workspace(self, _, update_item_mock, get_workspace_mock, __, ___, ____, app, client):
|
||||
auth_info_user_in_workspace_owner_role = {'sp_id': 'ab123', 'roles': {'WorkspaceOwner': 'ab124', 'WorkspaceResearcher': 'ab125'}}
|
||||
|
||||
get_workspace_mock.return_value = sample_deployed_workspace(WORKSPACE_ID, auth_info_user_in_workspace_owner_role)
|
||||
etag = "some-etag-value"
|
||||
workspace_service_patch = {"templateVersion": "0.2.0"}
|
||||
|
||||
modified_workspace_service = sample_workspace_service()
|
||||
modified_workspace_service.isEnabled = True
|
||||
modified_workspace_service.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_owner_user(), templateVersion=modified_workspace_service.templateVersion)]
|
||||
modified_workspace_service.resourceVersion = 1
|
||||
modified_workspace_service.user = create_workspace_owner_user()
|
||||
modified_workspace_service.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
modified_workspace_service.templateVersion = "0.2.0"
|
||||
|
||||
response = await client.patch(app.url_path_for(strings.API_UPDATE_WORKSPACE_SERVICE, workspace_id=WORKSPACE_ID, service_id=SERVICE_ID), json=workspace_service_patch, headers={"etag": etag})
|
||||
update_item_mock.assert_called_once_with(modified_workspace_service, etag)
|
||||
|
||||
assert response.status_code == status.HTTP_202_ACCEPTED
|
||||
|
||||
|
||||
class TestWorkspaceServiceRoutesThatRequireOwnerOrResearcherRights:
|
||||
@pytest.fixture(autouse=True, scope='class')
|
||||
|
@ -1066,7 +1363,7 @@ class TestWorkspaceServiceRoutesThatRequireOwnerOrResearcherRights:
|
|||
|
||||
modified_user_resource = sample_user_resource_object()
|
||||
modified_user_resource.isEnabled = False
|
||||
modified_user_resource.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_researcher_user())]
|
||||
modified_user_resource.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_researcher_user(), templateVersion=modified_user_resource.templateVersion)]
|
||||
modified_user_resource.resourceVersion = 1
|
||||
modified_user_resource.updatedWhen = FAKE_UPDATE_TIMESTAMP
|
||||
modified_user_resource.user = create_workspace_researcher_user()
|
||||
|
|
|
@ -330,7 +330,8 @@ def test_patch_resource_preserves_property_history(_, __, resource_repo):
|
|||
resourceVersion=0,
|
||||
updatedWhen=FAKE_CREATE_TIMESTAMP,
|
||||
properties={'display_name': 'initial display name', 'description': 'initial description', 'computed_prop': 'computed_val'},
|
||||
user=user)]
|
||||
user=user,
|
||||
templateVersion=resource.templateVersion)]
|
||||
expected_resource.properties['display_name'] = 'updated name'
|
||||
expected_resource.resourceVersion = 1
|
||||
expected_resource.user = user
|
||||
|
@ -348,7 +349,8 @@ def test_patch_resource_preserves_property_history(_, __, resource_repo):
|
|||
resourceVersion=1,
|
||||
updatedWhen=FAKE_UPDATE_TIMESTAMP,
|
||||
properties={'display_name': 'updated name', 'description': 'initial description', 'computed_prop': 'computed_val'},
|
||||
user=user
|
||||
user=user,
|
||||
templateVersion=resource.templateVersion
|
||||
)
|
||||
)
|
||||
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 48 KiB |
|
@ -0,0 +1,55 @@
|
|||
<!-- markdownlint-disable-file MD046 -->
|
||||
# Upgrading Resources Version
|
||||
|
||||
Azure TRE workspaces, workspace services, workspace shared services, and user resources are [Porter](https://porter.sh/) bundles. Porter bundles are based on [Cloud Native Application Bundles (CNAB)](https://cnab.io/).
|
||||
|
||||
When a new bundle version becomes available, users can upgrade their resources to a newer version after building, publishing and registering the bundle template.
|
||||
|
||||
Upgrades (and downgrades) are based on [CNAB bundle upgrade action](https://getporter.org/bundle/manifest/#bundle-actions).
|
||||
|
||||
Bundle template versions follow [semantic versioning rules](../tre-workspace-authors/authoring-workspace-templates.md#versioning).
|
||||
|
||||
!!! Note
|
||||
Only minor and patch version upgrades are automatically allowed within the Azure TRE upgrade mechanism. Major versions upgrades and any version downgrades are blocked as they are assumed to contain breaking changes or changes that require additional consideration.
|
||||
|
||||
For users who wish to upgrade a major version, we highly recommend to read the changelog, review what has changed and take some appropriate action before upgrading using [force version update](#force-version-update).
|
||||
|
||||
## How to upgrade a resource using Swagger UI
|
||||
|
||||
Resources can be upgrade using Swagger UI, in the following example we show how to upgrade a workspace version from 1.0.0 to 1.0.1, other resources upgrades are similar.
|
||||
|
||||
1. First make sure the desired template version is registered, [follow these steps if not](../tre-admins/registering-templates.md).
|
||||
|
||||
1. Navigate to the Swagger UI at `/api/docs`.
|
||||
|
||||
1. Log into the Swagger UI using `Authorize`.
|
||||
|
||||
1. Click `Try it out` on the `GET` `/api/workspace/{workspace_id}` operation.
|
||||
|
||||
1. Provide your `workspace_id` in the parameters section and click `Execute`.
|
||||
|
||||
1. Copy the `_etag` property from the response body.
|
||||
|
||||
1. Click `Try it out` on the `PATCH` `/api/workspace/{workspace_id}` operation.
|
||||
|
||||
1. Provide your `workspace_id` and `_etag` parameters which you've just copied.
|
||||
|
||||
1. Provide the following payload with the desired version in the `Request body` parameter and click `Execute`.
|
||||
|
||||
```json
|
||||
{
|
||||
"templateVersion": "1.0.1",
|
||||
}
|
||||
```
|
||||
1. Review server response, it should include a new `operation` document with `upgrade` as an `action` and `updating` as `status` for upgrading the workspace and a message states that the Job is starting.
|
||||
|
||||
1. Once the upgrade is complete another operation will be created and can be viewed by executing `GET` `/api/workspace/{workspace_id}/operations`, review it and make sure its `status` is `updated`.
|
||||
|
||||
### Force version update
|
||||
If you wish to upgrade a major version, or downgrade to any version, you can override the blocking in the upgrade mechanism by passing `force_version_update=true` query parameter to the resource `Patch` action.
|
||||
|
||||
For example force version patching a workspace:
|
||||
|
||||
![Force version update](../assets/swagger_force_version_update.png)
|
||||
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
# Authoring workspaces templates
|
||||
# Authoring templates
|
||||
|
||||
Azure TRE workspaces, workspace services, and user resources are [Porter](https://porter.sh/) bundles. Porter bundles are based on [Cloud Native Application Bundles (CNAB)](https://cnab.io/).
|
||||
Azure TRE workspaces, workspace services, shared services, and user resources are [Porter](https://porter.sh/) bundles. Porter bundles are based on [Cloud Native Application Bundles (CNAB)](https://cnab.io/).
|
||||
|
||||
Workspace authors are free to choose the technology stack for provisioning resources (e.g., ARM templates, Terraform etc.), but the Azure TRE framework sets certain requirements for the bundle manifests, which specify the credentials, input and output parameters, deployment actions among other things.
|
||||
Authors are free to choose the technology stack for provisioning resources (e.g., ARM templates, Terraform etc.), but the Azure TRE framework sets certain requirements for the bundle manifests, which specify the credentials, input and output parameters, deployment actions among other things.
|
||||
|
||||
This document describes the requirements, and the process to author a template.
|
||||
|
||||
|
@ -120,7 +120,16 @@ Templates authors need to make sure that underling Azure resources are tagged wi
|
|||
|
||||
Workspace versions are the bundle versions specified in [the metadata](https://porter.sh/author-bundles/#bundle-metadata). The bundle versions should match the image tags in the container registry (see [Publishing workspace bundle](#publishing-workspace-bundle)).
|
||||
|
||||
TRE does not provide means to update an existing workspace to a newer version. Instead, the user has to first uninstall the old version and then install the new one. The CNAB **upgrade** or a Porter custom ("`update`") action may be used in the future version of TRE to do this automatically.
|
||||
Bundle versions should follow [Semantic Versioning](https://semver.org/), given a version number **MAJOR.MINOR.PATCH**, increment the:
|
||||
|
||||
1. **MAJOR** version when you make a breaking change, potential data loss, changes that don't easily/automatically upgrade, or significant changes which require someone to review what has changed and take some appropriate action, or functionality of the component has significantly changed and users might need training.
|
||||
|
||||
2. **MINOR** version when you add minor functionality which can be automatically upgraded.
|
||||
|
||||
3. **PATCH** version when you make backward-compatible bug or typo fixes.
|
||||
|
||||
|
||||
For resource version upgrades see [Upgrading Resources Version](../tre-admins/upgrading-resources.md).
|
||||
|
||||
## Publishing workspace bundle
|
||||
|
||||
|
|
|
@ -125,7 +125,8 @@ nav:
|
|||
- Install Resources via API:
|
||||
- Install Base Workspace: tre-admins/setup-instructions/installing-base-workspace.md
|
||||
- Install Workspace Service and User Resource: tre-admins/setup-instructions/installing-workspace-service-and-user-resource.md
|
||||
- Upgrading AzureTRE version: tre-admins/upgrading-tre.md
|
||||
- Upgrading AzureTRE Version: tre-admins/upgrading-tre.md
|
||||
- Upgrading Resources Version: tre-admins/upgrading-resources.md
|
||||
- Configuring Airlock Reviews: tre-admins/configure-airlock-review.md
|
||||
|
||||
- Development: # Docs related to the developing code for the AzureTRE
|
||||
|
|
Загрузка…
Ссылка в новой задаче