Skip to content

Commit

Permalink
Extend deployment dialog and on-demand deployment (#125)
Browse files Browse the repository at this point in the history
* Add QProv endpoint

* Add missing import

* Add lisener for QProv endpoint

* Fix method name

* Filter QProv endpoint input param and add during deployment

* Sort input params alphabetically

* Upload CSARs after completion

* Martini...

* Disable button before completing STs

* Handle input params

* Use QName for Winery request

* add qprovendpoint

* Fix error when closing input modal via cancel

* Abort if one of the ServiceTemplates can not be completed

* Log ServiceTask for policy retrieval

* Start adding functionality for policy retrieval

* Add policy content to completion request

* Delete policies after completion

* add sorting for ondemand vars

---------

Co-authored-by: LaviniaStiliadou <livia_16@live.de>
Co-authored-by: mbeisel <beiselmn@gmail.com>
  • Loading branch information
3 people authored Nov 21, 2023
1 parent 3758f64 commit 8ac82a1
Show file tree
Hide file tree
Showing 11 changed files with 327 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ export function completeIncompleteDeploymentModel(
blacklistedNodetypes,
policies
) {
console.log("Completing deployment model at: ", deploymentModelUrl);
console.log("Blacklist: ", blacklistedNodetypes);
let url = deploymentModelUrl.split("/?csar")[0];
url = url.split("/");
url.shift();
Expand All @@ -119,9 +121,14 @@ export function completeIncompleteDeploymentModel(
blacklistedNodetypes: blacklistedNodetypes,
policies: policies,
});
return synchronousPostRequest(
config.wineryEndpoint + "/" + url + "/topologytemplate/completemodel",
"application/json",
body
).getResponseHeader("location");
try {
return synchronousPostRequest(
config.wineryEndpoint + "/" + url + "/topologytemplate/completemodel",
"application/json",
body
).getResponseHeader("location");
} catch (e) {
console.error("Error while completing deployment model: ", e);
return undefined;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,13 @@ export async function uploadCSARToContainer(
body: JSON.stringify(body),
headers: { "Content-Type": "application/json" },
});
console.log(
"CSAR sent to OpenTOSCA Container. Waiting for build plan..."
);

// check successful upload and retrieve corresponding url
getCSARResult = await getBuildPlanForCSAR(opentoscaEndpoint, csarName);
console.log("Retrieved result: ", getCSARResult);
}

if (!getCSARResult.success) {
Expand Down Expand Up @@ -91,6 +95,8 @@ export async function uploadCSARToContainer(
* @return the status whether the given CSAR is uploaded and the corresponding build plan link if available
*/
async function getBuildPlanForCSAR(opentoscaEndpoint, csarName) {
console.log("Retrieving build plan for CSAR with name: ", csarName);

// get all currently deployed CSARs
const response = await fetch(opentoscaEndpoint);
const responseJson = await response.json();
Expand All @@ -100,6 +106,7 @@ async function getBuildPlanForCSAR(opentoscaEndpoint, csarName) {
// no CSARs available
return { success: false };
}
console.log("Retrieved all currently deployed CSARs: ", deployedCSARs);

for (let i = 0; i < deployedCSARs.length; i++) {
const deployedCSAR = deployedCSARs[i];
Expand Down Expand Up @@ -155,12 +162,20 @@ async function getBuildPlanUrl(csarUrl) {
*
* @param csar the details about the CSAR to create an instance from the contained ServiceTemplate
* @param camundaEngineEndpoint the endpoint of the Camunda engine to bind services using the pulling pattern
* @param qprovEndpoint the endpoint of QProv to store provenance data
* @param inputParams a set of predfined input parameters to use for the instance creation
* @return the result of the instance creation (success, endpoint, topic on which the service listens, ...)
*/
export async function createServiceInstance(csar, camundaEngineEndpoint) {
export async function createServiceInstance(
csar,
camundaEngineEndpoint,
qprovEndpoint,
inputParams
) {
const result = { success: false };

const inputParameters = csar.inputParameters;
console.log("Required input parameters: ", inputParameters);
if (csar.type === "pull") {
// get special parameters that are required to bind services using external tasks / the pulling pattern
const camundaTopicParam = inputParameters.find(
Expand All @@ -186,6 +201,27 @@ export async function createServiceInstance(csar, camundaEngineEndpoint) {
result.topicName = topicName;
}

// add QProv endpoint for provenance data storage
const qprovEndpointParam = inputParameters.find(
(param) => param.name === "QProvEndpoint"
);
if (qprovEndpointParam !== undefined) {
console.info(
"ServiceTemplate requires QProv Endpoint as input: ",
qprovEndpoint
);
qprovEndpointParam.value = qprovEndpoint;
}

// handle input parameters for completed ServiceTemplates
inputParameters.forEach((param) => {
if (!param.value) {
console.log("Parameter is not yet defined: ", param.name);
param.value = inputParams[param.name];
}
});
console.log("Input parameters after adding provided data: ", inputParameters);

// trigger instance creation
const instanceCreationResponse = await fetch(
csar.buildPlanUrl + "/instances",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
},
{
"name": "CloudDeploymentModelPolicy",
"superClass": ["bpmn:Event"],
"superClass": ["bpmn:BoundaryEvent"],
"properties": [
{
"name": "cloudType",
Expand All @@ -40,7 +40,7 @@
},
{
"name": "OnDemandPolicy",
"superClass": ["bpmn:Event"],
"superClass": ["bpmn:BoundaryEvent"],
"properties": [
{
"name": "onDemand",
Expand All @@ -51,7 +51,7 @@
},
{
"name": "DedicatedHostingPolicy",
"superClass": ["bpmn:Event"],
"superClass": ["bpmn:BoundaryEvent"],
"properties": [
{
"name": "dedicatedHosting",
Expand All @@ -62,7 +62,7 @@
},
{
"name": "LocationPolicy",
"superClass": ["bpmn:Event"],
"superClass": ["bpmn:BoundaryEvent"],
"properties": [
{
"name": "location",
Expand All @@ -73,7 +73,7 @@
},
{
"name": "Policy",
"superClass": ["bpmn:Event"],
"superClass": ["bpmn:BoundaryEvent"],
"properties": []
}
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,18 @@ import { bindUsingPull, bindUsingPush } from "../../../deployment/BindingUtils";
import {
completeIncompleteDeploymentModel,
getServiceTasksToDeploy,
isCompleteDeploymentModel,
} from "../../../deployment/DeploymentUtils";
import { getModeler } from "../../../../../editor/ModelerHandler";
import NotificationHandler from "../../../../../editor/ui/notifications/NotificationHandler";
import { getRootProcess } from "../../../../../editor/util/ModellingUtilities";
import ExtensibleButton from "../../../../../editor/ui/ExtensibleButton";
import { loadDiagram } from "../../../../../editor/util/IoUtilities";
import { startOnDemandReplacementProcess } from "../../../replacement/OnDemandTransformator";
import { deletePolicies, getPolicies } from "../../../utilities/Utilities";
import {
CLOUD_DEPLOYMENT_MODEL_POLICY,
LOCATION_POLICY,
} from "../../../Constants";

const defaultState = {
windowOpenOnDemandDeploymentOverview: false,
Expand Down Expand Up @@ -202,55 +206,172 @@ export default class DeploymentPlugin extends PureComponent {
async handleDeploymentInputClosed(result) {
// handle click on 'Next' button
if (result && result.hasOwnProperty("next") && result.next === true) {
console.log(
"Blacklisting NodeTypes based on requirements: ",
result.nodeTypeRequirements
);

// Blacklist Nodetypes which don't have their requirements fulfilled for Incomplete Deployment Models
const nodeTypeRequirements = result.nodeTypeRequirements;
let blacklistedNodetypes = [];
Object.entries(nodeTypeRequirements).forEach((entry) => {
const [key, value] = entry;
Object.values(value.requiredAttributes).forEach((value) => {
if (value === "" && !blacklistedNodetypes.includes(key)) {
blacklistedNodetypes.push(key);
Object.entries(nodeTypeRequirements).forEach(([key, value]) => {
console.log(value);
Object.values(value.requiredAttributes).forEach((innerValue) => {
if (
innerValue === "" &&
!blacklistedNodetypes.includes(value.qName)
) {
blacklistedNodetypes.push(value.qName);
}
});
});
console.log("Blacklisted NodeTypes: ", blacklistedNodetypes);

// collect input parameters of all NodeTypes that might be used during completion
let nodeTypesToUse = Object.entries(nodeTypeRequirements)
.filter(([key, value]) => !blacklistedNodetypes.includes(value.qName))
.map(([key, value]) => value);
console.log("NodeTypes to use for completion: ", nodeTypesToUse);
let inputParams = {};
Object.values(nodeTypesToUse).forEach((nodeType) => {
console.log("Retrieving input parameters for NodeType: ", nodeType.id);
console.log("Input parameters: ", nodeType.requiredAttributes);
Object.entries(nodeType.requiredAttributes).forEach(([key, value]) => {
inputParams[key] = value;
});
});
console.log("Corresponding input parameters: ", inputParams);

// make progress bar visible and hide buttons
result.refs.progressBarDivRef.current.hidden = false;
result.refs.footerRef.current.hidden = true;
let progressBar = result.refs.progressBarRef.current;
this.handleProgress(progressBar, 10);

let csarList = result.csarList;
let incompleteCSARList = [];
console.log("List of CSARs before completion: ", csarList);
for (let csar of csarList) {
let url = csar;
const isComplete = isCompleteDeploymentModel(csar.url);
if (!isComplete) {
incompleteCSARList.push(csar);
}
}
if (csar.incomplete) {
console.log("Found incomplete CSAR: ", csar.csarName);

// retrieve policies for the ServiceTask the CSAR belongs to
let policyShapes = getPolicies(this.modeler, csar.serviceTaskIds[0]);
let policies = {};
policyShapes.forEach((policy) => {
console.log("Found policy: ", policy);
switch (policy.type) {
case CLOUD_DEPLOYMENT_MODEL_POLICY:
console.log(
"Adding cloud model policy: ",
policy.businessObject.cloudType
);
policies[policy.type] = policy.businessObject.cloudType;
break;
case LOCATION_POLICY:
console.log(
"Adding location policy: ",
policy.businessObject.location
);
policies[policy.type] = policy.businessObject.location;
break;
default:
console.error(
"Policy of type %s not supported for completion!"
);
}
});
console.log("Invoking completion with policies: ", policies);

for (let incompleteCSAR of incompleteCSARList) {
const locationOfCompletedCSAR = completeIncompleteDeploymentModel(
incompleteCSAR.url,
blacklistedNodetypes,
{}
);
const nameOfCompletedCSAR = locationOfCompletedCSAR
.split("/")
.filter((x) => x.length > 1)
.pop();
for (let i = 0; i < csarList.length; i++) {
if (csarList[i].name === incompleteCSAR.name) {
csarList[i].url = locationOfCompletedCSAR;
csarList[i].name = nameOfCompletedCSAR;
// complete CSAR and refresh meta data
const locationOfCompletedCSAR = completeIncompleteDeploymentModel(
csar.url,
blacklistedNodetypes,
policies
);
if (!locationOfCompletedCSAR) {
// notify user about failed completion
NotificationHandler.getInstance().displayNotification({
type: "error",
title: "Unable to complete ServiceTemplate",
content:
"ServiceTemplate with Id '" +
csar.csarName +
"' could not be completed!",
duration: 20000,
});

// abort process
this.setState({
windowOpenDeploymentOverview: false,
windowOpenDeploymentInput: false,
windowOpenDeploymentBinding: false,
});
return;
}
}
}
const nameOfCompletedCSAR = locationOfCompletedCSAR
.split("/")
.filter((x) => x.length > 1)
.pop();
csar.url = locationOfCompletedCSAR + "?csar";
csar.csarName = nameOfCompletedCSAR + ".csar";
csar.incomplete = false;
console.log("Completed CSAR. New name: ", csar.csarName);
console.log("New location: ", csar.url);

// update the deployment model connected to the ServiceTask
let serviceTask = this.modeler
.get("elementRegistry")
.get(csar.serviceTaskIds[0]);
serviceTask.businessObject.deploymentModelUrl =
"{{ wineryEndpoint }}/servicetemplates/" +
csar.url.split("/servicetemplates/")[1];

// delete the policies as they are now incorporated into the new deployment model
deletePolicies(this.modeler, csar.serviceTaskIds[0]);

// upload completed CSAR to the OpenTOSCA Container
console.log(
"Uploading CSAR to the OpenTOSCA Container at: ",
this.modeler.config.opentoscaEndpoint
);
let uploadResult = await uploadCSARToContainer(
this.modeler.config.opentoscaEndpoint,
csar.csarName,
csar.url,
this.modeler.config.wineryEndpoint
);
if (uploadResult.success === false) {
// notify user about failed CSAR upload
NotificationHandler.getInstance().displayNotification({
type: "error",
title: "Unable to upload CSAR to the OpenTOSCA Container",
content:
"CSAR defined for ServiceTasks with Id '" +
csar.serviceTaskIds +
"' could not be uploaded to the connected OpenTOSCA Container!",
duration: 20000,
});

// TODO buildplan url is missing still -> new complete deployment model needs to be uploaded and buildplan ref added
// abort process
this.setState({
windowOpenDeploymentOverview: false,
windowOpenDeploymentInput: false,
windowOpenDeploymentBinding: false,
});
return;
}

// make progress bar visible and hide buttons
result.refs.progressBarDivRef.current.hidden = false;
result.refs.footerRef.current.hidden = true;
let progressBar = result.refs.progressBarRef.current;
this.handleProgress(progressBar, 10);
// calculate progress step size for the number of CSARs to create an service instance for
// set URL of the CSAR in the OpenTOSCA Container which is required to create instances
console.log("Upload successfully!");
csar.buildPlanUrl = uploadResult.url;
csar.inputParameters = uploadResult.inputParameters;
console.log("Build plan URL: ", csar.buildPlanUrl);
console.log("Input Parameters: ", csar.inputParameters);
}
}
console.log("Retrieved CSAR list after completion: ", csarList);

// calculate progress step size for the number of CSARs to create a service instance for
let progressStep = Math.round(90 / csarList.length);

// create service instances for all CSARs
Expand All @@ -260,7 +381,9 @@ export default class DeploymentPlugin extends PureComponent {

let instanceCreationResponse = await createServiceInstance(
csar,
this.modeler.config.camundaEndpoint
this.modeler.config.camundaEndpoint,
this.modeler.config.qprovEndpoint,
inputParams
);
console.log("Creating service instance for CSAR: ", csar);
csar.properties = instanceCreationResponse.properties;
Expand Down
Loading

0 comments on commit 8ac82a1

Please sign in to comment.