diff --git a/openapi-spec.yml b/openapi-spec.yml index 584ad74..5ef34ec 100644 --- a/openapi-spec.yml +++ b/openapi-spec.yml @@ -1,6 +1,6 @@ openapi: "3.0.0" info: - version: 0.5.0 + version: 0.5.1 title: xOpera API license: name: Apache-2.0 @@ -102,6 +102,26 @@ paths: $ref: "#/components/schemas/DeploymentOutput" "404": description: No outputs exist for this deployment. + /update: + post: + summary: Do an update + operationId: update + requestBody: + description: Update inputs + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/UpdateRequest" + responses: + "202": + description: Update successfully initiated. + content: + application/json: + schema: + $ref: "#/components/schemas/Invocation" + "500": + description: There was an error initiating the update. /validate: post: deprecated: true @@ -424,6 +444,7 @@ components: - deploy - undeploy - notify + - update OperationSuccess: description: A general success message. type: object @@ -472,3 +493,16 @@ components: type: string csar: type: string + UpdateRequest: + description: Update request. + type: object + required: + - inputs + - newServiceTemplateContents + properties: + inputs: + type: object + newServiceTemplateContents: + description: The contents of the new service template. + type: string + format: bytes diff --git a/src/opera/api/controllers/background_invocation.py b/src/opera/api/controllers/background_invocation.py index 31d680e..25015f1 100644 --- a/src/opera/api/controllers/background_invocation.py +++ b/src/opera/api/controllers/background_invocation.py @@ -9,8 +9,12 @@ from typing import List, Optional from opera.commands.deploy import deploy_service_template as opera_deploy +from opera.commands.diff import diff_instances as opera_diff_instances from opera.commands.notify import notify as opera_notify from opera.commands.undeploy import undeploy as opera_undeploy +from opera.commands.update import update as opera_update +from opera.compare.instance_comparer import InstanceComparer +from opera.compare.template_comparer import TemplateComparer from opera.storage import Storage from opera.api.log import get_logger @@ -53,6 +57,8 @@ def _run_internal(work_queue: multiprocessing.Queue): elif inv.operation == OperationType.NOTIFY: # we abuse service_template and inputs a bit, but they match InvocationWorkerProcess._notify(inv.service_template, inv.inputs) + elif inv.operation == OperationType.DEPLOY: + InvocationWorkerProcess._update(inv.service_template, inv.inputs, num_workers=1) else: raise RuntimeError("Unknown operation type:" + str(inv.operation)) @@ -95,6 +101,24 @@ def _notify(event_name: str, notification_contents: Optional[str]): opera_notify(opera_storage, verbose_mode=True, trigger_name_or_event=event_name, notification_file_contents=notification_contents) + @staticmethod + def _update(service_template: str, inputs: typing.Optional[dict], num_workers: int): + original_storage = Storage.create() + + new_storage = Storage.create(instance_path=".opera-api-update") + new_storage.write_json(inputs, "inputs") + new_storage.write(service_template, "root_file") + + instance_diff = opera_diff_instances( + original_storage, ".", new_storage, ".", + TemplateComparer(), InstanceComparer(), True + ) + + opera_update( + original_storage, ".", new_storage, ".", + InstanceComparer(), instance_diff, True, num_workers, overwrite=True + ) + @staticmethod def read_file(filename): with open(filename, "r") as f: diff --git a/src/opera/api/controllers/default.py b/src/opera/api/controllers/default.py index d42a59b..220b5e6 100644 --- a/src/opera/api/controllers/default.py +++ b/src/opera/api/controllers/default.py @@ -1,5 +1,6 @@ import tempfile import traceback +from datetime import datetime from pathlib import PurePath from opera.commands.diff import diff_instances as opera_diff_instances @@ -18,7 +19,7 @@ from opera.api.controllers.background_invocation import InvocationService from opera.api.log import get_logger from opera.api.openapi.models import ValidationResult, OperationType, CsarInitializationInput, PackagingInput, \ - UnpackagingInput, PackagingResult, Info, CsarValidationInput, DiffRequest, Diff + UnpackagingInput, PackagingResult, Info, CsarValidationInput, DiffRequest, Diff, UpdateRequest from opera.api.openapi.models.deployment_input import DeploymentInput logger = get_logger(__name__) @@ -33,7 +34,7 @@ def deploy(body: DeploymentInput = None): deployment_input = DeploymentInput.from_dict(body) result = invocation_service.invoke(OperationType.DEPLOY, deployment_input.service_template, deployment_input.inputs, deployment_input.clean_state) - return result, 200 + return result, 202 def diff(body: dict = None): @@ -208,3 +209,17 @@ def unpackage(unpackaging_input: UnpackagingInput): return {"success": True, "message": ""}, 200 except Exception as e: return {"success": False, "message": "General error: {}".format(str(e))}, 500 + + +def update(body: dict = None): # noqa: E501 + logger.debug("Entry: update") + update_request = UpdateRequest.from_dict(body) + + posixnow = int(datetime.utcnow().timestamp()) + with open("st-operaapi-update-{}.yml".format(posixnow)) as new_st_file: + new_st_file.write(update_request.new_service_template_contents) + new_st_filename = new_st_file.name + + result = invocation_service.invoke(OperationType.UPDATE, new_st_filename, update_request.inputs, False) + + return result, 202