in source/packages/services/greengrass2-provisioning/src/deployments/deployments.service.ts [112:388]
public async createDeployment(taskId: string, request: NewDeployment): Promise<Deployment> {
logger.debug(
`deployments.service createDeployment: in: taskId:${taskId}, request:${JSON.stringify(
request
)}`
);
// fail fast if invalid request
ow(taskId, ow.string.nonEmpty);
ow(request?.coreName, 'core name', ow.string.nonEmpty);
// get the task to determine the template
const task = await this.deploymentTasksDao.get(taskId);
ow(task, 'deployment task', ow.object.nonEmpty);
ow(task.template.name, 'template name', ow.string.nonEmpty);
ow(task.template.version, 'template version', ow.number.greaterThan(0));
// set deployment as in progress
const deployment: Deployment = {
...request,
taskStatus: 'InProgress',
createdAt: new Date(),
updatedAt: new Date(),
};
// and save the desired template info
const futures: Promise<void>[] = [
this.deploymentTasksDao.saveTaskDetail(taskId, deployment),
this.coresService.associateTemplate(
deployment.coreName,
task.template.name,
task.template.version as number,
'desired'
),
];
await Promise.all(futures);
// determine if core is already registered with ggv2
try {
await this.ggv2.send(
new GetCoreDeviceCommand({ coreDeviceThingName: deployment.coreName })
);
} catch (e) {
logger.error(`deployments.service createDeployment: error: ${JSON.stringify(e)}`);
if (e.name === 'ResourceNotFoundException') {
this.markAsFailed(deployment, 'Core device not registered with Greengrass V2');
} else {
this.markAsFailed(
deployment,
`Unable to determine if core device is registered with Greengrass V2: ${e.name}`
);
}
}
// retrieve template/version
let template: TemplateItem;
if (this.isStillInProgress(deployment)) {
template = await this.templatesService.get(task.template.name, task.template.version);
if (template === undefined) {
this.markAsFailed(deployment, 'Template not found');
}
}
// determine if a deployment already exists for the template. if not, create it (and save to db)
if (this.isStillInProgress(deployment)) {
if (template.deployment?.id === undefined) {
// deployment does not exist therefore create it
// first create the thing group target
const thingGroupName = this.getTemplateVersionThingGroupTarget(template);
let thingGroupArn: string;
try {
const r = await this.iot.send(
new CreateThingGroupCommand({
thingGroupName,
tags: [
{
Key: DEPLOYMENT_TASK_ID_TAG_KEY,
Value: taskId,
},
],
})
);
logger.silly(
`deployments.service createDeployment: CreateThingGroupCommandOutput: ${JSON.stringify(
r
)}`
);
thingGroupArn = r.thingGroupArn;
} catch (e) {
logger.error(
`deployments.service createDeployment: error: ${JSON.stringify(e)}`
);
if (e.name === 'ResourceAlreadyExistsException') {
logger.warn(
`deployments.service createDeployment: thingGroup: ${thingGroupName} already exists`
);
const r = await this.iot.send(
new DescribeThingGroupCommand({ thingGroupName })
);
logger.silly(
`deployments.service createDeployment: DescribeThingGroupCommandOutput: ${JSON.stringify(
r
)}`
);
thingGroupArn = r.thingGroupArn;
} else {
this.markAsFailed(deployment, `Unable to create thing group: ${e.name}`);
}
}
// now we can create the deployment
if (this.isStillInProgress(deployment)) {
try {
const componentsMap: { [key: string]: ComponentDeploymentSpecification } =
{};
template.components?.forEach(
(c) =>
(componentsMap[c.key] = {
componentVersion: c.version,
configurationUpdate: c.configurationUpdate,
runWith: c.runWith,
})
);
const r = await this.ggv2.send(
new CreateDeploymentCommand({
targetArn: thingGroupArn,
deploymentName: `CDF GreengrassV2 Provisoning - Template: ${template.name}, Version: ${template.version}`,
components: componentsMap,
iotJobConfiguration: template.jobConfig,
deploymentPolicies: template.deploymentPolicies,
tags: {
DEPLOYMENT_TASK_ID_TAGGING_KEY: taskId,
},
})
);
logger.silly(
`deployments.service createDeployment: CreateDeploymentCommandOutput: ${JSON.stringify(
r
)}`
);
template.deployment = {
id: r.deploymentId,
thingGroupName,
jobId: r.iotJobId,
};
const tagResourceOutput = await this.iot.send(
new TagResourceCommand({
resourceArn: r.iotJobArn,
tags: [
{
Key: DEPLOYMENT_TASK_ID_TAG_KEY,
Value: taskId,
},
],
})
);
logger.silly(
`deployments.service createDeployment: TagResourceCommandOutput: ${JSON.stringify(
tagResourceOutput
)}`
);
} catch (e) {
logger.error(
`deployments.service createDeployment: error: ${JSON.stringify(e)}`
);
this.markAsFailed(deployment, `Failed to create deployment: ${e.name}`);
}
}
// and save the state
if (this.isStillInProgress(deployment)) {
try {
await this.templatesService.associateDeployment(template);
} catch (e) {
logger.error(
`deployments.service createDeployment: error: ${JSON.stringify(e)}`
);
this.markAsFailed(
deployment,
`Failed to associate deployment with template: ${e.name}`
);
}
}
}
}
if (this.isStillInProgress(deployment)) {
// remove from all existing deployment thing groups
try {
const listThingGroupsForThingOutput = await this.iot.send(
new ListThingGroupsForThingCommand({
thingName: deployment.coreName,
})
);
logger.silly(
`deployments.service createDeployment: ListThingGroupsForThingOutput: ${JSON.stringify(
listThingGroupsForThingOutput
)}`
);
const existingDeploymentThingGroups =
listThingGroupsForThingOutput?.thingGroups?.filter((tg) =>
tg.groupName.startsWith(this.DEPLOYMENT_THING_GROUP_PREFIX)
);
logger.silly(
`deployments.service createDeployment: deployment groups to remove from: ${JSON.stringify(
existingDeploymentThingGroups
)}`
);
if ((existingDeploymentThingGroups?.length ?? 0) > 0) {
for (const tg of existingDeploymentThingGroups) {
await this.iot.send(
new RemoveThingFromThingGroupCommand({
thingName: deployment.coreName,
thingGroupArn: tg.groupArn,
})
);
}
}
} catch (e) {
logger.error(e);
this.markAsFailed(
deployment,
`Failed removing from existing deployment thing groups: ${e.name}`
);
}
// add thing to new deployment thing group
try {
await this.iot.send(
new AddThingToThingGroupCommand({
thingName: deployment.coreName,
thingGroupName: template.deployment.thingGroupName,
})
);
} catch (e) {
logger.error(`deployments.service createDeployment: error: ${JSON.stringify(e)}`);
this.markAsFailed(
deployment,
`Failed to add core device to deployment thing group target: ${e.name}`
);
}
}
// save
if (deployment.taskStatus === 'InProgress') {
deployment.taskStatus = 'Success';
deployment.updatedAt = new Date();
await this.cdfEventPublisher.emitEvent<DeploymentTaskCreatedPayload>({
name: DeploymentTaskCreatedEvent,
payload: {
taskId: taskId,
coreName: deployment.coreName,
status: 'success',
},
});
}
if (deployment.taskStatus === 'Failure') {
await this.cdfEventPublisher.emitEvent<DeploymentTaskCreatedPayload>({
name: DeploymentTaskCreatedEvent,
payload: {
taskId: taskId,
coreName: deployment.coreName,
status: 'failed',
message: deployment.statusMessage,
},
});
}
await this.deploymentTasksDao.saveTaskDetail(taskId, deployment);
logger.debug(`deployments.service createDeployment: exit: ${JSON.stringify(deployment)}`);
return deployment;
}