Name
sn_itom_pattern.UpdateContainers
Description
This script grabs a batch of newly created containers and associates them with software packages and application CIs based on the results of container image scanning
Script
var UpdateContainers = Class.create();
UpdateContainers.prototype = {
initialize: function() {
this.LAST_CONTAINER_PROCESSED_TIME = 'last_container_processed_time';
this.logger = new Logger('UpdateContainers');
},
/*
process a batch of newly created containers. Per each container, add the installed packages and application CIs
*/
run: function() {
if (!gs.getProperty('sn_itom_pattern.container_image_scan', 'true') == 'true')
return;
var lastContainerProcessedTime = this._getLastContainerProcessedTime();
var batchSize = gs.getProperty('sn_itom_pattern.container_batch_size',50);
var lastContainerTimestamp;
var containers = [];
var containerGr = new GlideRecord('cmdb_ci_oslv_container');
containerGr.addQuery('sys_created_on','>=', lastContainerProcessedTime);
containerGr.orderBy('sys_created_on');
containerGr.setLimit(batchSize);
containerGr.query();
while (containerGr.next()) {
lastContainerTimestamp = containerGr.sys_created_on + '';
containers.push(containerGr.getUniqueValue());
this.logger.debug('processing container ' + containerGr.name + ' ' + containerGr.getUniqueValue());
}
// Now get additional containers created on the same second. We do this since we want to avoid a situation where the next
// iteration will process the same containers
this._getContainersCreatedAt(lastContainerTimestamp, containers);
// If the current time is identical to the recent timestamp, then abort. Ideally, we could wait here one second, but gs.sleep
// is not available for scoped apps. Again, this is in order to avoid a situation where the next iteration will processs the same
// containers
var now = new GlideDateTime();
if (now.compareTo(new GlideDateTime(lastContainerTimestamp)) == 0) {
this.logger.debug('Current time is identical to the timestamp of the last container we found. Will read from the same marker in the next iteration');
return;
}
// If no containers fetched, return here
if (containers.length == 0)
return;
// Add packages and application CIs to containers
this._processContainers(containers);
// Add one second to lastContainerTimestamp, so the next iteration will not get containers already processed
var lastCreated = new GlideDateTime(lastContainerTimestamp);
lastCreated.addSeconds(1);
// Record the timestamp for the next iteration
this._saveLastProcessedTime(lastCreated);
},
// Get containers created at given time (time is give as string). The result is pushed to an array called containers
_getContainersCreatedAt: function(lastContainerTimestamp, containers) {
containerGr = new GlideRecord('cmdb_ci_oslv_container');
containerGr.addQuery('sys_created_on', lastContainerTimestamp);
containerGr.addQuery('sys_id','NOT IN', containers);
containerGr.query();
while (containerGr.next()) {
containers.push(containerGr.getUniqueValue());
this.logger.debug('processing container ' + containerGr.name + ' ' + containerGr.getUniqueValue());
}
},
// Process the containers in the input array. Per each container, add the installed packages and application CIs
_processContainers: function(containers) {
var imageToContainers = {};
// Find the images associated with the containers. Prepare a map between image ID and array of containers
var relGr = new GlideRecord('cmdb_rel_ci');
relGr.addQuery('child', containers);
relGr.addQuery('type','1bb40e370a0a0b323d85a1ce84f8feae'); // Instantiates::Instantiated by
relGr.query();
while (relGr.next()) {
if (imageToContainers[relGr.parent + '']) {
imageToContainers[relGr.parent + ''].push(relGr.child + '');
} else {
imageToContainers[relGr.parent + ''] = [relGr.child + ''];
}
}
// Find which images are scanned. Per each scanned image do the processing
var imageScanGr = new GlideRecord('sn_itom_pattern_container_image_scan_status');
imageScanGr.addQuery('image', Object.keys(imageToContainers));
imageScanGr.addQuery('scan_status','scanned');
imageScanGr.query();
while (imageScanGr.next()) {
this.processImage(imageScanGr.image + '', imageToContainers[imageScanGr.image]);
}
},
// Process all containers that use a given image. Per each container, add the installed packages and application CIs
processImage: function(imageId, containers) {
this.logger.debug('Processing image ' + imageId + ' used by ' + containers.length + ' containers');
// Prepare a map of software packages for this image based on sn_itom_pattern_container_image_os_packages
var packages = this._preparePackagesPerImage(imageId);
var imageGr = new GlideRecord('cmdb_ci_oslv_image');
if (!imageGr.get(imageId))
return;
var appHandler = new ContainerizedAppHandler();
// Apply the packages to each of the containers
var packagesPayload;
if (Object.keys(packages).length > 0)
packagesPayload = { cmdb_ci_spkg : { data: packages}};
var containerGr = new GlideRecord('cmdb_ci_oslv_container');
containerGr.addQuery('sys_id', containers);
containerGr.query();
while (containerGr.next()) {
// Create installed packages
if (packagesPayload)
new global.DiscoveryReconciler(containerGr, packagesPayload).process();
// Create application Cis
appHandler.createApp(containerGr.getUniqueValue(), imageGr);
}
},
// Prepare a data structure describing the installed packages in a given image
_preparePackagesPerImage: function(imageId) {
var imagePackagesGr = new GlideRecord('sn_itom_pattern_container_image_os_packages');
imagePackagesGr.addQuery('image', imageId);
imagePackagesGr.query();
var packages = [];
while (imagePackagesGr.next()) {
packages.push({ name: imagePackagesGr.package_name + '' , version: imagePackagesGr.package_version + '', vendor: imagePackagesGr.package_maintainer + ''});
}
return packages;
},
// Record a timestamp to be used as input in the next iteration
_saveLastProcessedTime: function(timestamp) {
var grHash = new GlideRecord('sa_hash');
grHash.addQuery('name', this.LAST_CONTAINER_PROCESSED_TIME);
grHash.query();
if (grHash.next()) {
grHash.setValue('hash',timestamp);
grHash.update();
} else {
grHash = new GlideRecord('sa_hash');
grHash.setValue('name', this.LAST_CONTAINER_PROCESSED_TIME);
grHash.setValue('hash',timestamp);
grHash.insert();
}
},
// Get the last saved timestamp recorded by the previous iteration
_getLastContainerProcessedTime: function() {
var grHash = new GlideRecord('sa_hash');
grHash.addQuery('name', this.LAST_CONTAINER_PROCESSED_TIME);
grHash.query();
if (grHash.next())
return new GlideDateTime(grHash.hash + '');
else
return new GlideDateTime('1970-01-01 12:00:00');
},
type: 'UpdateContainers'
};
Sys ID
ef2be385a1e71910f8775b4b0fc68db9