Loads UCS

@param {Object}        bigIp - bigIp instances
@param {Buffer|Stream} ucsData - Either a Buffer or a ReadableStream containing UCS data
@param {String}        cloudProvider - Cloud provider (aws, azure, etc)

@returns {Promise} Promise that will be resolved when the UCS is loaded or rejected
if an error occurs.
function loadUcs(provider, bigIp, ucsData, cloudProvider) {
        const timeStamp = Date.now();
        const originalPath = `/config/ucsOriginal_${timeStamp}.ucs`;
        const updatedPath = `/config/ucsUpdated_${timeStamp}.ucs`;
        const updateScript = `${__dirname}/updateAutoScaleUcs`;

        const deferred = q.defer();
        let originalFile;

        const preLoad = function () {
            // eslint-disable-next-line max-len
            const sedCommand = "sed -i '/sys dynad key {/ { N ; /\\n[[:space:]]\\+key[[:space:]]*\\$M\\$[^\\n]*/ { N;   /\\n[[:space:]]*}/ { d } } }' /config/bigip_base.conf";
            const loadSysConfigCommand = 'load /sys config';

            logger.silly('removing dynad key from base config');
            return util.runShellCommand(sedCommand)
                .then(() => {
                    logger.silly('loading sys config');
                    return util.runTmshCommand(loadSysConfigCommand);
                })
                .then(() => {
                    logger.silly('waiting for BIG-IP to be ready');
                    return bigIp.ready();
                })
                .catch((err) => {
                    logger.warn('preload of ucs failed:', err);
                    throw err;
                });
        };

        const doLoad = function () {
            const args = [
                '--original-ucs',
                originalPath,
                '--updated-ucs',
                updatedPath,
                '--cloud-provider',
                cloudProvider,
                '--extract-directory',
                '/config/cloud/ucsRestore'
            ];
            const loadUcsOptions = {
                initLocalKeys: true
            };

            preLoad()
                .then(() => {
                    childProcess.execFile(updateScript, args, (childProcessErr) => {
                        if (childProcessErr) {
                            const message = `${updateScript} failed: ${childProcessErr}`;
                            logger.warn(message);
                            deferred.reject(new Error(message));
                            return;
                        }

                        if (!fs.existsSync(updatedPath)) {
                            logger.warn(`${updatedPath} does not exist after running ${updateScript}`);
                            deferred.reject(new Error('updated ucs not found'));
                            return;
                        }

                        // If we're not sharing the password, put our current user back after
                        // load
                        if (!provider.hasFeature(CloudProvider.FEATURE_SHARED_PASSWORD)) {
                            loadUcsOptions.restoreUser = true;
                        }

                        bigIp.loadUcs(
                            updatedPath,
                            { 'no-license': true, 'reset-trust': true },
                            loadUcsOptions
                        )
                            .then(() => {
                                // reset-trust on load does not always seem to work
                                // use a belt-and-suspenders approach and reset now as well
                                return bigIp.cluster.resetTrust();
                            })
                            .then(() => {
                                // Attempt to delete the file, but ignore errors
                                try {
                                    fs.unlinkSync(originalPath);
                                    fs.unlinkSync(updatedPath);
                                } finally {
                                    deferred.resolve();
                                }
                            })
                            .catch((err) => {
                                logger.info('error loading ucs', err);
                                deferred.reject(err);
                            });
                    });
                })
                .catch((err) => {
                    throw err;
                });
        };

        // If ucsData has a pipe method (is a stream), use it
        if (ucsData.pipe) {
            logger.silly('ucsData is a Stream');
            originalFile = fs.createWriteStream(originalPath);

            ucsData.pipe(originalFile);

            originalFile.on('finish', () => {
                logger.silly('finished piping ucsData');
                originalFile.close(() => {
                    doLoad();
                });
            });

            originalFile.on('error', (err) => {
                logger.warn('Error piping ucsData', err);
                deferred.reject(err);
            });

            ucsData.on('error', (err) => {
                logger.warn('Error reading ucs data', err);
                deferred.reject(err);
            });
        } else {
            // Otherwise, assume it's a Buffer
            logger.silly('ucsData is a Buffer');
            fs.writeFile(originalPath, ucsData, (err) => {
                logger.silly('finished writing ucsData');
                if (err) {
                    logger.warn('Error writing ucsData', err);
                    deferred.reject(err);
                    return;
                }
                doLoad();
            });
        }

        return deferred.promise;
    }