in source/services/deployments/lib/addDeployment.js [17:392]
module.exports = function (event, context) {
let _device;
let _deviceType;
let _deviceBlueprint;
let _newSpec = {};
let _newGreengrassGroupVersion = {};
let _groupVersion;
let _deployment;
let _savedDeployment;
let _newShadow = {};
let _certificateArn;
let _substitutions = {
THING_NAME: null,
CORE: null,
CORE_ARN: null,
CORE_CERTIFICATE_ARN: null,
AWS_REGION: null,
AWS_ACCOUNT: null,
DATA_BUCKET: null,
DATA_BUCKET_S3_URL: null,
IOT_ENDPOINT: null
};
// First lets get the device.
return documentClient.get({
TableName: process.env.TABLE_DEVICES,
Key: {
thingId: event.thingId
}
}).promise().then(device => {
_device = device.Item;
if (_device === undefined) {
throw 'Device does not exist in DB';
}
return Promise.all([
documentClient.get({
TableName: process.env.TABLE_DEVICE_TYPES,
Key: {
id: _device.deviceTypeId
}
}).promise().then(result => _deviceType = result.Item),
documentClient.get({
TableName: process.env.TABLE_DEVICE_BLUEPRINTS,
Key: {
id: _device.deviceBlueprintId
}
}).promise().then(result => _deviceBlueprint = result.Item)
]);
}).then(results => {
if (_deviceType === undefined || _deviceBlueprint === undefined) {
throw 'Device Type or Device Blueprint do not exist in DB';
}
console.log('Device:', _device);
console.log('Device Type:', _deviceType);
console.log('Device Blueprint:', _deviceBlueprint);
return iot.listThingPrincipals({
thingName: _device.thingName
}).promise();
}).then(principals => {
principals = principals.principals;
if (principals.length === 0) {
throw 'Device does not yet have a certificate. Need to create one first.';
}
_certificateArn = principals[0];
_substitutions.AWS_ACCOUNT = process.env.AWS_ACCOUNT;
_substitutions.AWS_REGION = process.env.AWS_REGION;
_substitutions.THING_NAME = _device.thingName;
_substitutions.CORE = _device.thingName;
_substitutions.CORE_ARN = _device.thingArn;
_substitutions.CORE_CERTIFICATE_ARN = _certificateArn;
_substitutions.DATA_BUCKET = process.env.DATA_BUCKET;
_substitutions.DATA_BUCKET_S3_URL = `https://${process.env.DATA_BUCKET}.s3.amazonaws.com`;
_substitutions.IOT_ENDPOINT = process.env.IOT_ENDPOINT;
// Order is important
// Merge Device into DeviceType into DeviceBlueprint
_newSpec = {};
_newShadow = {};
if (_device.spec) {
console.log(`Device Spec: ${JSON.stringify(_device.spec, null, 4)}`);
_newSpec = mergeSputnikSpecs(_newSpec, _device.spec);
_newShadow = mergeSputnikShadows(_newShadow, _device.spec.Shadow);
console.log(`WIP Spec: ${JSON.stringify(_newSpec, null, 4)}`);
}
if (_deviceType.spec) {
console.log(`Device Type Spec: ${JSON.stringify(_deviceType.spec, null, 4)}`);
_newSpec = mergeSputnikSpecs(_newSpec, _deviceType.spec);
_newShadow = mergeSputnikShadows(_newShadow, _deviceType.spec.Shadow);
console.log(`WIP Spec: ${JSON.stringify(_newSpec, null, 4)}`);
}
if (_deviceBlueprint.spec) {
console.log(`Device Blueprint Spec: ${JSON.stringify(_deviceBlueprint.spec, null, 4)}`);
_newSpec = mergeSputnikSpecs(_newSpec, _deviceBlueprint.spec);
_newShadow = mergeSputnikShadows(_newShadow, _deviceBlueprint.spec.Shadow);
console.log(`WIP Spec: ${JSON.stringify(_newSpec, null, 4)}`);
}
// console.log('Deal with actions');
// _newSpec.afterActions = [...(_device.spec.afterActions || []), ...(_deviceType.spec.afterActions || []), ...(_deviceBlueprint.spec.afterActions || [])];
console.log('Going to substitute in the spec');
// Construct the spec:
let strSpec = JSON.stringify(_newSpec);
let strShadow = JSON.stringify(_newShadow);
for (var key in _substitutions) {
// skip loop if the property is from prototype
if (!_substitutions.hasOwnProperty(key)) continue;
var value = _substitutions[key];
for (var prop in value) {
// skip loop if the property is from prototype
if (!value.hasOwnProperty(prop)) continue;
// your code
let regExp = new RegExp('[' + key + ']', 'gi');
strSpec = strSpec.split('[' + key + ']').join(value);
strShadow = strShadow.split('[' + key + ']').join(value);
}
}
// TODO: Replace this with the !GetAtt system ?
_deviceBlueprint.deviceTypeMappings.forEach(mapping => {
if (mapping.value[_deviceType.id]) {
let regExp = new RegExp('[' + mapping.substitute + ']', 'gi');
strSpec = strSpec.split('[' + mapping.substitute + ']').join(mapping.value[_deviceType.id]);
}
});
_newSpec = JSON.parse(strSpec);
_newShadow = JSON.parse(strShadow);
_newSpec.Shadow = _newShadow;
// // TODO: this eval thing could be a security risk. Need to potentially rethink this.
// _newSpec.afterActions.forEach(a => {
// console.log('Evaluating:', a);
// eval(a);
// _newSpec = afterAction(_newSpec);
// });
console.log(`Spec out: ${JSON.stringify(_newSpec, null, 4)}`);
if (_deviceType.type === 'GREENGRASS' && _deviceBlueprint.type === 'GREENGRASS') {
console.log('Device is a Greengrass device:', _device.greengrassGroupId);
return gg.getGroup({
GroupId: _device.greengrassGroupId
}).promise().then(group => {
if (!group.LatestVersion) {
console.log('Group does not have a definition version yet. We will need to create it later down.');
return null;
} else {
return gg.getGroupVersion({
GroupId: _device.greengrassGroupId,
GroupVersionId: group.LatestVersion
}).promise();
}
}).then(groupDefinitionVersion => {
_newGreengrassGroupVersion = {};
_newGreengrassGroupVersion.GroupId = _device.greengrassGroupId;
return Promise.all([
createGreengrassXDefinitionVersion('Core', _newSpec, groupDefinitionVersion).then(c => {
if (c) {
_newGreengrassGroupVersion.CoreDefinitionVersionArn = c.Arn;
}
return c;
}),
createGreengrassXDefinitionVersion('Function', _newSpec, groupDefinitionVersion).then(f => {
if (f) {
_newGreengrassGroupVersion.FunctionDefinitionVersionArn = f.Arn;
}
return f;
}),
createGreengrassXDefinitionVersion('Logger', _newSpec, groupDefinitionVersion).then(l => {
if (l) {
_newGreengrassGroupVersion.LoggerDefinitionVersionArn = l.Arn;
}
return l;
}),
createGreengrassXDefinitionVersion('Resource', _newSpec, groupDefinitionVersion).then(r => {
if (r) {
_newGreengrassGroupVersion.ResourceDefinitionVersionArn = r.Arn;
}
return r;
}),
createGreengrassXDefinitionVersion('Subscription', _newSpec, groupDefinitionVersion).then(s => {
if (s) {
_newGreengrassGroupVersion.SubscriptionDefinitionVersionArn = s.Arn;
}
return s;
}),
createGreengrassXDefinitionVersion('Device', _newSpec, groupDefinitionVersion).then(d => {
if (d) {
_newGreengrassGroupVersion.DeviceDefinitionVersionArn = d.Arn;
}
return d;
}),
createGreengrassXDefinitionVersion('Connector', _newSpec, groupDefinitionVersion).then(d => {
if (d) {
_newGreengrassGroupVersion.ConnectorDefinitionVersionArn = d.Arn;
}
return d;
})
]);
}).then(results => {
console.log('results', JSON.stringify(results));
console.log('newGreengrassGroupVersion', JSON.stringify(_newGreengrassGroupVersion, null, 2));
return gg.createGroupVersion(_newGreengrassGroupVersion).promise();
}).then(groupVersion => {
_groupVersion = groupVersion;
console.log(`Created group version: ${JSON.stringify(_groupVersion, null, 2)}`);
console.log('Attach IAM role to group just in case');
return gg.associateRoleToGroup({
RoleArn: process.env.IAM_ROLE_ARN_FOR_GREENGRASS_GROUPS,
GroupId: _device.greengrassGroupId
}).promise();
}).then(result => {
console.log('Attach IOT Greengrass policy to group just in case');
// TODO: this should ideally be in the specs!
return iot
.attachPrincipalPolicy({
policyName: process.env.IOT_POLICY_GREENGRASS_CORE,
principal: _certificateArn
})
.promise();
}).then(result => {
console.log(`Deploy group:`);
return gg.createDeployment({
GroupId: _groupVersion.Id,
DeploymentId: uuid.v4(),
DeploymentType: 'NewDeployment',
GroupVersionId: _groupVersion.Version
}).promise();
}).then(deployment => {
_deployment = deployment;
console.log(`Deployed: ${_deployment.DeploymentId}`);
_savedDeployment = {
thingId: _device.thingId,
deploymentId: _deployment.DeploymentId,
spec: _newSpec,
type: 'GREENGRASS',
greengrassGroup: {
Id: _groupVersion.Id,
VersionId: _groupVersion.Version
},
createdAt: moment()
.utc()
.format(),
updatedAt: moment()
.utc()
.format()
};
return _savedDeployment;
});
} else {
console.log('Device is NOT a greengrass device, or at least not detected as one. OR the deviceBlueprint/deviceType combination is not for a Greengrass device');
_savedDeployment = {
thingId: _device.thingId,
deploymentId: uuid.v4(),
spec: _newSpec,
type: _deviceType.type,
greengrassGroup: {},
createdAt: moment()
.utc()
.format(),
updatedAt: moment()
.utc()
.format()
};
return _savedDeployment;
}
}).then(params => {
const newDeployment = {
TableName: process.env.TABLE_DEPLOYMENTS,
Item: _savedDeployment
};
return documentClient.put(newDeployment).promise();
}).then(deployment => {
const updateParams = {
TableName: process.env.TABLE_DEVICES,
Key: {
thingId: _device.thingId
},
UpdateExpression: 'set #ua = :ua, #l = :l',
ExpressionAttributeNames: {
'#ua': 'updatedAt',
'#l': 'lastDeploymentId'
},
ExpressionAttributeValues: {
':ua': moment()
.utc()
.format(),
':l': _savedDeployment.deploymentId
}
};
return documentClient.update(updateParams).promise();
}).then(device => {
if (JSON.stringify(_newShadow) !== '{}') {
return iot.describeEndpoint().promise().then(endpoint => {
const iotdata = new AWS.IotData({
endpoint: endpoint.endpointAddress
});
return iotdata.updateThingShadow({
thingName: _device.thingName,
payload: JSON.stringify({
state: {}
})
}).promise().then(result => {
console.log('Updating with nothing, to create in case it doesnt exist');
return iotdata.deleteThingShadow({
thingName: _device.thingName
}).promise().then(result => {
return iotdata.updateThingShadow({
thingName: _device.thingName,
payload: JSON.stringify({
state: _newShadow
})
}).promise();
});
});
}).then(result => {
console.log('Updated shadow per spec:', _newShadow);
return _savedDeployment;
});
} else {
return _savedDeployment;
}
}).then(() => {
console.log(`AND WE ARE DONE !`);
return _savedDeployment;
});
};