Skip to content

Commit 674079a

Browse files
is there a world?????
1 parent 2c21652 commit 674079a

8 files changed

Lines changed: 200 additions & 22 deletions

File tree

plugins/iac/nimble/src/main/java/org/apache/cloudstack/api/command/DeployIacTemplateCmd.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
import org.apache.cloudstack.api.ApiConstants;
2424
import org.apache.cloudstack.api.BaseAsyncCmd;
2525
import org.apache.cloudstack.api.Parameter;
26+
import org.apache.cloudstack.api.response.IacTemplateDeploymentResponse;
2627
import org.apache.cloudstack.api.response.IacTemplateResponse;
27-
import org.apache.cloudstack.api.response.SuccessResponse;
2828
import org.apache.cloudstack.persistence.iactemplates.IacTemplate;
2929
import org.apache.cloudstack.service.NimbleService;
3030
import org.apache.commons.collections.MapUtils;
@@ -33,7 +33,7 @@
3333
import java.util.HashMap;
3434
import java.util.Map;
3535

36-
@APICommand(name = "deployIacTemplate", description = "Deploys an already registered IaC template.", responseObject = SuccessResponse.class, entityType = {IacTemplate.class},
36+
@APICommand(name = "deployIacTemplate", description = "Deploys an already registered IaC template.", responseObject = IacTemplateDeploymentResponse.class, entityType = {IacTemplate.class},
3737
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
3838
public class DeployIacTemplateCmd extends BaseAsyncCmd {
3939
@Inject
@@ -63,8 +63,8 @@ public Map<String, String> getInputs() {
6363

6464
@Override
6565
public void execute() {
66-
nimbleService.deployIacTemplate(this);
67-
SuccessResponse response = new SuccessResponse(getCommandName());
66+
IacTemplateDeploymentResponse response = nimbleService.deployIacTemplate(this);
67+
response.setResponseName(getCommandName());
6868
setResponseObject(response);
6969
}
7070

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package org.apache.cloudstack.api.response;
18+
19+
import com.cloud.serializer.Param;
20+
import com.google.gson.annotations.SerializedName;
21+
import org.apache.cloudstack.api.ApiConstants;
22+
import org.apache.cloudstack.api.BaseResponse;
23+
24+
import java.util.List;
25+
import java.util.Map;
26+
27+
public class IacTemplateDeploymentResponse extends BaseResponse {
28+
@SerializedName(ApiConstants.SUCCESS)
29+
@Param(description = "Whether the IaC template deployment completed successfully.")
30+
private boolean success;
31+
32+
@SerializedName("nodes")
33+
@Param(description = "The provisioning result of each node template.")
34+
private List<NodeTemplateDeploymentResponse> nodes;
35+
36+
@SerializedName("deploymenterror")
37+
@Param(description = "IaC template deployment error.")
38+
private String deploymentError;
39+
40+
public void setSuccess(boolean success) {
41+
this.success = success;
42+
}
43+
44+
public void setNodes(List<NodeTemplateDeploymentResponse> nodes) {
45+
this.nodes = nodes;
46+
}
47+
48+
public void setDeploymentError(String deploymentError) {
49+
this.deploymentError = deploymentError;
50+
}
51+
52+
public static class NodeTemplateDeploymentResponse {
53+
@SerializedName(ApiConstants.NAME)
54+
@Param(description = "Name of the node template.")
55+
private String name;
56+
57+
@SerializedName(ApiConstants.TYPE)
58+
@Param(description = "Type of the IaC template node.")
59+
private String type;
60+
61+
@SerializedName(ApiConstants.STATE)
62+
@Param(description = "Provisioning state of the node template.")
63+
private String state;
64+
65+
@SerializedName(ApiConstants.PROPERTIES)
66+
@Param(description = "Evaluated properties of the node template.")
67+
private Map<String, String> properties;
68+
69+
@SerializedName("attributes")
70+
@Param(description = "Attributes of the node template populated after provisioning.")
71+
private Map<String, String> attributes;
72+
73+
@SerializedName("error")
74+
@Param(description = "Error message if the node template failed to be provisioned.")
75+
private String error;
76+
77+
public void setName(String name) {
78+
this.name = name;
79+
}
80+
81+
public void setType(String type) {
82+
this.type = type;
83+
}
84+
85+
public void setState(String state) {
86+
this.state = state;
87+
}
88+
89+
public void setProperties(Map<String, String> properties) {
90+
this.properties = properties;
91+
}
92+
93+
public void setAttributes(Map<String, String> attributes) {
94+
this.attributes = attributes;
95+
}
96+
97+
public void setError(String error) {
98+
this.error = error;
99+
}
100+
}
101+
}

plugins/iac/nimble/src/main/java/org/apache/cloudstack/api/response/NimbleResponseBuilder.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.apache.cloudstack.api.response;
1818

1919
import com.cloud.api.ApiDBUtils;
20+
import com.cloud.api.ApiGsonHelper;
2021
import com.cloud.domain.Domain;
2122
import com.cloud.exception.PermissionDeniedException;
2223
import com.cloud.projects.Project;
@@ -31,6 +32,8 @@
3132
import org.apache.cloudstack.persistence.iactemplatesprofile.IacResourceType;
3233
import org.apache.cloudstack.tosca.model.ToscaNodeTemplate;
3334
import org.apache.cloudstack.tosca.model.ToscaServiceTemplate;
35+
import org.apache.commons.collections.MapUtils;
36+
import org.apache.commons.lang3.StringUtils;
3437

3538
import javax.inject.Inject;
3639
import java.util.ArrayList;
@@ -187,4 +190,45 @@ public IacTemplateGraphResponse createIacTemplateGraphResponse(IacTemplate iacTe
187190
response.setObjectName("iactemplategraph");
188191
return response;
189192
}
193+
194+
public IacTemplateDeploymentResponse createIacTemplateDeploymentResponse(ToscaServiceTemplate serviceTemplate, boolean success) {
195+
List<IacTemplateDeploymentResponse.NodeTemplateDeploymentResponse> nodeResponses = serviceTemplate.getNodeTemplates().values().stream()
196+
.map(this::createNodeTemplateDeploymentResponse)
197+
.collect(Collectors.toList());
198+
199+
List<String> errorMessages = serviceTemplate.getNodeTemplates().values().stream()
200+
.filter(n -> n.getProvisioningError() != null)
201+
.map(n -> String.format("[%s]: %s", n.getName(), n.getProvisioningError()))
202+
.collect(Collectors.toList());
203+
204+
IacTemplateDeploymentResponse response = new IacTemplateDeploymentResponse();
205+
response.setSuccess(success);
206+
response.setDeploymentError(serviceTemplate.getDeploymentError());
207+
response.setNodes(nodeResponses);
208+
response.setObjectName("iactemplatedeployment");
209+
return response;
210+
}
211+
212+
private IacTemplateDeploymentResponse.NodeTemplateDeploymentResponse createNodeTemplateDeploymentResponse(ToscaNodeTemplate nodeTemplate) {
213+
IacTemplateDeploymentResponse.NodeTemplateDeploymentResponse response = new IacTemplateDeploymentResponse.NodeTemplateDeploymentResponse();
214+
response.setName(nodeTemplate.getName());
215+
response.setType(nodeTemplate.getType().getName());
216+
response.setState(nodeTemplate.getProvisioningState().name());
217+
if (StringUtils.isNotBlank(nodeTemplate.getProvisioningError())) {
218+
response.setError(nodeTemplate.getProvisioningError());
219+
}
220+
if (MapUtils.isNotEmpty(nodeTemplate.getAttributes())) {
221+
response.setAttributes(nodeTemplate.getAttributes().isEmpty() ? null :
222+
nodeTemplate.getAttributes().entrySet().stream()
223+
.collect(Collectors.toMap(
224+
Map.Entry::getKey,
225+
e -> ApiGsonHelper.getBuilder().create().toJson(e.getValue()))));
226+
}
227+
response.setProperties(nodeTemplate.getProperties().entrySet().stream()
228+
.filter(e -> e.getValue().getEvaluatedValue() != null)
229+
.collect(Collectors.toMap(
230+
Map.Entry::getKey,
231+
e -> ApiGsonHelper.getBuilder().create().toJson(e.getValue().getEvaluatedValue()))));
232+
return response;
233+
}
190234
}

plugins/iac/nimble/src/main/java/org/apache/cloudstack/service/NimbleManagerImpl.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.apache.cloudstack.api.command.RemoveIacTemplateCmd;
4343
import org.apache.cloudstack.api.command.UpdateIacTemplateCmd;
4444
import org.apache.cloudstack.api.response.IacResourceTypeResponse;
45+
import org.apache.cloudstack.api.response.IacTemplateDeploymentResponse;
4546
import org.apache.cloudstack.api.response.IacTemplateGraphResponse;
4647
import org.apache.cloudstack.api.response.IacTemplateResponse;
4748
import org.apache.cloudstack.api.response.ListResponse;
@@ -434,12 +435,12 @@ public void removeIacTemplate(RemoveIacTemplateCmd cmd) {
434435
}
435436

436437
@Override
437-
public void deployIacTemplate(DeployIacTemplateCmd cmd) {
438+
public IacTemplateDeploymentResponse deployIacTemplate(DeployIacTemplateCmd cmd) {
438439
Map<String, String> inputs = cmd.getInputs();
439440
IacTemplate iacTemplate = findIacTemplateById(cmd.getId());
440441
checkCallerAccessToIacTemplate(CallContext.current(), iacTemplate);
441-
ToscaServiceTemplate serviceTemplate = toscaOrchestrator.deployIacTemplate(iacTemplate.getIacTemplateContent(), inputs, cmd.getHttpMethod());
442-
logger.info("WILL IT WORK?????", serviceTemplate);
442+
Pair<ToscaServiceTemplate, Boolean> provisioningResult = toscaOrchestrator.deployIacTemplate(iacTemplate.getIacTemplateContent(), inputs, cmd.getHttpMethod());
443+
return responseBuilder.createIacTemplateDeploymentResponse(provisioningResult.first(), provisioningResult.second());
443444
}
444445

445446
@Override

plugins/iac/nimble/src/main/java/org/apache/cloudstack/service/NimbleService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.apache.cloudstack.api.command.PlanIacTemplateDeploymentCmd;
2525
import org.apache.cloudstack.api.command.RemoveIacTemplateCmd;
2626
import org.apache.cloudstack.api.response.IacResourceTypeResponse;
27+
import org.apache.cloudstack.api.response.IacTemplateDeploymentResponse;
2728
import org.apache.cloudstack.api.response.IacTemplateGraphResponse;
2829
import org.apache.cloudstack.api.response.IacTemplateResponse;
2930
import org.apache.cloudstack.api.response.ListResponse;
@@ -55,7 +56,7 @@ public interface NimbleService extends NimbleServiceHelper, PluggableService, Co
5556

5657
ListResponse<IacResourceTypeResponse> listIacResourceTypes(ListIacResourceTypesCmd cmd);
5758
ListResponse<IacTemplateResponse> listIacTemplates(ListIacTemplatesCmd cmd);
58-
void deployIacTemplate(DeployIacTemplateCmd cmd);
59+
IacTemplateDeploymentResponse deployIacTemplate(DeployIacTemplateCmd cmd);
5960
IacTemplateGraphResponse planIacTemplateDeployment(PlanIacTemplateDeploymentCmd cmd);
6061
IacTemplateResponse saveIacTemplate(BaseIacTemplateRegistrationCmd cmd);
6162
void removeIacTemplate(RemoveIacTemplateCmd cmd);

plugins/iac/nimble/src/main/java/org/apache/cloudstack/tosca/model/ToscaNodeTemplate.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public enum ProvisioningState {
3434
}
3535

3636
private volatile ProvisioningState provisioningState = ProvisioningState.PENDING;
37+
private volatile String provisioningError;
3738

3839
public ToscaNodeTemplate(String name, ToscaNodeType type, Map<String, ToscaProperty> properties) {
3940
this.name = name;
@@ -106,4 +107,12 @@ public ProvisioningState getProvisioningState() {
106107
public void setProvisioningState(ProvisioningState provisioningState) {
107108
this.provisioningState = provisioningState;
108109
}
110+
111+
public String getProvisioningError() {
112+
return provisioningError;
113+
}
114+
115+
public void setProvisioningError(String provisioningError) {
116+
this.provisioningError = provisioningError;
117+
}
109118
}

plugins/iac/nimble/src/main/java/org/apache/cloudstack/tosca/model/ToscaServiceTemplate.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public class ToscaServiceTemplate {
2525
private final Map<String, ToscaInputDefinition> inputs;
2626
private final Map<String, Set<ToscaNodeTemplate>> dependencyGraph;
2727
private final Map<String, Set<ToscaProperty>> unresolvedPropertiesByGetInput;
28+
private String deploymentError;
2829

2930
public ToscaServiceTemplate(Map<String, ToscaNodeTemplate> nodeTemplates, Map<String, Set<ToscaNodeTemplate>> dependencyGraph, Map<String, ToscaInputDefinition> inputs, Map<String, Set<ToscaProperty>> unresolvedPropertiesByGetInput) {
3031
this.nodeTemplates = nodeTemplates;
@@ -48,4 +49,12 @@ public Map<String, Set<ToscaNodeTemplate>> getDependencyGraph() {
4849
public Map<String, Set<ToscaProperty>> getUnresolvedPropertiesByGetInput() {
4950
return Collections.unmodifiableMap(unresolvedPropertiesByGetInput);
5051
}
52+
53+
public String getDeploymentError() {
54+
return deploymentError;
55+
}
56+
57+
public void setDeploymentError(String deploymentError) {
58+
this.deploymentError = deploymentError;
59+
}
5160
}

plugins/iac/nimble/src/main/java/org/apache/cloudstack/tosca/orchestrator/ToscaOrchestrator.java

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -104,17 +104,16 @@ public class ToscaOrchestrator {
104104

105105
private ExecutorService executorPool;
106106

107-
public ToscaServiceTemplate deployIacTemplate(String iacTemplateContent, Map<String, String> inputs, BaseCmd.HTTPMethod httpMethod) {
107+
public Pair<ToscaServiceTemplate, Boolean> deployIacTemplate(String iacTemplateContent, Map<String, String> inputs, BaseCmd.HTTPMethod httpMethod) {
108108
ToscaServiceTemplate serviceTemplate = parseServiceTemplate(iacTemplateContent);
109109
resolveServiceTemplateInputs(serviceTemplate, inputs);
110110

111111
Map<String, CompletableFuture<Void>> provisioningTasksFutures = new HashMap<>();
112112
Runnable cancelAllProvisioningTasks = getCancelAllProvisioningTasks(provisioningTasksFutures);
113-
List<Throwable> errors = Collections.synchronizedList(new ArrayList<>());
114113
getCurrentNimbleExecutorPoolStatus("before creating provisioning tasks");
115-
provisioningTasksFutures.putAll(createProvisioningTasksFutures(serviceTemplate, errors, cancelAllProvisioningTasks, httpMethod));
116-
awaitDeployCompletion(provisioningTasksFutures, errors, cancelAllProvisioningTasks);
117-
return serviceTemplate;
114+
provisioningTasksFutures.putAll(createProvisioningTasksFutures(serviceTemplate, cancelAllProvisioningTasks, httpMethod));
115+
boolean success = awaitDeployCompletion(serviceTemplate, provisioningTasksFutures, cancelAllProvisioningTasks);
116+
return new Pair<>(serviceTemplate, success);
118117
}
119118

120119
public ToscaServiceTemplate parseServiceTemplate(String iacTemplateContent) {
@@ -139,26 +138,40 @@ private Runnable getCancelAllProvisioningTasks(Map<String, CompletableFuture<Voi
139138
};
140139
}
141140

142-
private void awaitDeployCompletion(Map<String, CompletableFuture<Void>> provisioningTasksFutures, List<Throwable> errors, Runnable cancelAllProvisioningTasks) {
141+
private boolean awaitDeployCompletion(ToscaServiceTemplate serviceTemplate, Map<String, CompletableFuture<Void>> provisioningTasksFutures, Runnable cancelAllProvisioningTasks) {
143142
logger.debug("Awaiting for all the provisioning tasks of the service template to complete.");
144143
CompletableFuture<Void> serviceTemplateFeature = CompletableFuture.allOf(provisioningTasksFutures.values().toArray(new CompletableFuture[0]));
145144
int timeout = NimbleService.NimbleIaCTemplateExecutionTimeout.value();
146145
try {
147146
serviceTemplateFeature.get(timeout, TimeUnit.SECONDS);
148147
logger.info("All provisioning tasks of the service template completed successfully.");
149148
} catch (ExecutionException e) {
150-
Set<String> errorMessages = errors.stream().map(Throwable::getMessage).collect(Collectors.toSet());
151-
throw new CloudRuntimeException(String.format("The following errors occurred during the IaC template deployment: %s", String.join(" | ", errorMessages)), e);
149+
List<String> errorMessages = serviceTemplate.getNodeTemplates().values().stream()
150+
.filter(n -> n.getProvisioningError() != null)
151+
.map(n -> String.format("[%s]: %s", n.getName(), n.getProvisioningError()))
152+
.collect(Collectors.toList());
153+
String baseErrorMessage = "One or more node templates failed to be provisioned";
154+
logger.error("{}: {}", baseErrorMessage, String.join(" | ", errorMessages), e);
155+
serviceTemplate.setDeploymentError(baseErrorMessage + ".");
156+
return false;
152157
} catch (TimeoutException e) {
153158
cancelAllProvisioningTasks.run();
154-
throw new CloudRuntimeException(String.format("IaC template deployment timed out after [%d] seconds.", timeout), e);
159+
String errorMessage = String.format("IaC template deployment timed out after [%d] seconds.", timeout);
160+
logger.error(errorMessage, e);
161+
serviceTemplate.setDeploymentError(errorMessage);
162+
return false;
155163
} catch (InterruptedException e) {
156164
Thread.currentThread().interrupt();
157165
cancelAllProvisioningTasks.run();
158-
throw new CloudRuntimeException("IaC template deployment was interrupted.", e);
166+
String errorMessage = "IaC template deployment was interrupted.";
167+
logger.error(errorMessage, e);
168+
serviceTemplate.setDeploymentError(errorMessage);
169+
return false;
159170
} finally {
160171
getCurrentNimbleExecutorPoolStatus("after awaiting the deployment of the service template");
161172
}
173+
174+
return true;
162175
}
163176

164177
/**
@@ -200,7 +213,7 @@ private void handleGetInputFunctionCalls(String nodeTemplateName, ToscaProperty
200213
property.setEvaluatedValue(evaluatedValue);
201214
}
202215

203-
private Map<String, CompletableFuture<Void>> createProvisioningTasksFutures(ToscaServiceTemplate serviceTemplate, List<Throwable> errors, Runnable cancelAllProvisioningTasks, BaseCmd.HTTPMethod httpMethod) {
216+
private Map<String, CompletableFuture<Void>> createProvisioningTasksFutures(ToscaServiceTemplate serviceTemplate, Runnable cancelAllProvisioningTasks, BaseCmd.HTTPMethod httpMethod) {
204217
logger.debug("Building provisioning tasks for the service template based on its graph topological sort.");
205218
Map<String, CompletableFuture<Void>> futures = new HashMap<>();
206219
CallContext callContext = CallContext.current();
@@ -222,7 +235,7 @@ private Map<String, CompletableFuture<Void>> createProvisioningTasksFutures(Tosc
222235
}
223236

224237
taskFuture.whenComplete((result, ex) -> {
225-
handleTaskCompletion(nodeTemplate, ex, errors, cancelAllProvisioningTasks);
238+
handleTaskCompletion(nodeTemplate, ex, cancelAllProvisioningTasks);
226239
getCurrentNimbleExecutorPoolStatus(String.format("after node template [%s] provisioning", node));
227240
});
228241

@@ -233,7 +246,7 @@ private Map<String, CompletableFuture<Void>> createProvisioningTasksFutures(Tosc
233246
return futures;
234247
}
235248

236-
private void handleTaskCompletion(ToscaNodeTemplate nodeTemplate, Throwable ex, List<Throwable> errors, Runnable cancelAllProvisioningTasks) {
249+
private void handleTaskCompletion(ToscaNodeTemplate nodeTemplate, Throwable ex, Runnable cancelAllProvisioningTasks) {
237250
if (ex == null) {
238251
logger.trace("The provisioning of the node template completed successfully. Skipping error handling.");
239252
nodeTemplate.setProvisioningState(ToscaNodeTemplate.ProvisioningState.SUCCEEDED);
@@ -252,7 +265,7 @@ private void handleTaskCompletion(ToscaNodeTemplate nodeTemplate, Throwable ex,
252265

253266
logger.trace("An error occurred during the provisioning of a node template. Adding it to the list of errors.", ex);
254267
nodeTemplate.setProvisioningState(ToscaNodeTemplate.ProvisioningState.FAILED);
255-
errors.add(ex);
268+
nodeTemplate.setProvisioningError(ex.getMessage());
256269
cancelAllProvisioningTasks.run();
257270
}
258271

0 commit comments

Comments
 (0)