Repository: microsoft/AzureTRE

Issue:
Create basic, no-op `pipeline: {}` block in new `dev-vm` resource
This task is to enable future work on the `pipeline: {}` without interfering with existing functionality.

- [x] Copy the `guacamole-azure-linuxvm` to `guacamole-dev-vm`
- [x] Add a basic 3-step `pipeline: {}` to the schema to update the `display_name` of the firewall shared service, run main, then update the firewall name again.
- [x] Make sure the `pipeline: {}` block is ignored/removed during schema validation.
- [x] Start a doc to describe what the `pipeline:{}` is for. This will be updated as features are added in subsequent tasks.





Discussion:

Resolving patch:
diff --git a/api_app/_version.py b/api_app/_version.py
--- a/api_app/_version.py
+++ b/api_app/_version.py
@@ -1 +1 @@
-__version__ = "0.2.12"
+__version__ = "0.2.13"
diff --git a/api_app/db/repositories/resource_templates.py b/api_app/db/repositories/resource_templates.py
--- a/api_app/db/repositories/resource_templates.py
+++ b/api_app/db/repositories/resource_templates.py
@@ -101,6 +101,9 @@ def create_template(self, template_input: ResourceTemplateInCreate, resource_typ
             "customActions": template_input.customActions
         }
 
+        if "pipeline" in template_input.json_schema:
+            template["pipeline"] = template_input.json_schema["pipeline"]
+
         if resource_type == ResourceType.UserResource:
             template["parentWorkspaceService"] = parent_service_name
             template = parse_obj_as(UserResourceTemplate, template)
diff --git a/api_app/models/domain/resource_template.py b/api_app/models/domain/resource_template.py
--- a/api_app/models/domain/resource_template.py
+++ b/api_app/models/domain/resource_template.py
@@ -43,6 +43,7 @@ class ResourceTemplate(AzureTREModel):
     properties: Dict[str, Property] = Field(title="Template properties")
     actions: List[CustomAction] = Field(default=[], title="Template actions")
     customActions: List[CustomAction] = Field(default=[], title="Template custom actions")
+    pipeline: Optional[dict] = Field(default=None, title="Template pipeline to define updates to other resources")
 
     # setting this to false means if extra, unexpected fields are supplied, the request is invalidated
     additionalProperties: bool = Field(default=False, title="Prevent unspecified properties being applied")


Test patch:
diff --git a/api_app/tests_ma/test_db/test_repositories/test_resource_templates_repository.py b/api_app/tests_ma/test_db/test_repositories/test_resource_templates_repository.py
--- a/api_app/tests_ma/test_db/test_repositories/test_resource_templates_repository.py
+++ b/api_app/tests_ma/test_db/test_repositories/test_resource_templates_repository.py
@@ -1,5 +1,6 @@
 import pytest
 from mock import patch
+from models.domain.user_resource_template import UserResourceTemplate
 
 from db.repositories.resource_templates import ResourceTemplateRepository
 from db.errors import EntityDoesNotExist
@@ -145,3 +146,34 @@ def test_create_item_created_with_the_expected_type(uuid_mock, save_item_mock, r
     )
     save_item_mock.assert_called_once_with(expected_resource_template)
     assert expected_resource_template == returned_template
+
+
+@patch('db.repositories.resource_templates.ResourceTemplateRepository.save_item')
+@patch('uuid.uuid4')
+def test_create_item_with_pipeline_succeeds(uuid_mock, save_item_mock, resource_template_repo, input_user_resource_template):
+    uuid_mock.return_value = "1234"
+    expected_type = ResourceType.UserResource
+    # add the pipeline block
+    pipeline = {
+        "upgrade": [],
+        "install": [],
+        "uninstall": []
+    }
+    input_user_resource_template.json_schema["pipeline"] = pipeline
+    returned_template = resource_template_repo.create_template(input_user_resource_template, expected_type)
+    expected_resource_template = UserResourceTemplate(
+        id="1234",
+        name=input_user_resource_template.name,
+        title=input_user_resource_template.json_schema["title"],
+        description=input_user_resource_template.json_schema["description"],
+        version=input_user_resource_template.version,
+        resourceType=expected_type,
+        properties=input_user_resource_template.json_schema["properties"],
+        customActions=input_user_resource_template.customActions,
+        required=input_user_resource_template.json_schema["required"],
+        current=input_user_resource_template.current,
+        pipeline=pipeline,
+        parentWorkspaceService=""
+    )
+    save_item_mock.assert_called_once_with(expected_resource_template)
+    assert expected_resource_template == returned_template