RackHD/on-tasks

View on GitHub
lib/jobs/analyze-os-repo-job.js

Summary

Maintainability
C
1 day
Test Coverage
// Copyright 2015, EMC, Inc.

'use strict';

var di = require('di');

module.exports = analyzeOsRepoJobFactory;
di.annotate(analyzeOsRepoJobFactory, new di.Provide('Job.Os.Analyze.Repo'));
    di.annotate(analyzeOsRepoJobFactory,
    new di.Inject(
        'Job.Base',
        'Logger',
        'Assert',
        'Util',
        '_',
        'Promise',
        'JobUtils.OsRepoTool'
    )
);

function analyzeOsRepoJobFactory(
    BaseJob,
    Logger,
    assert,
    util,
    _,
    Promise,
    repoTool
) {
    var logger = Logger.initialize(analyzeOsRepoJobFactory);

    /**
     * This job will analyze the external OS repository, fetch some options from some repository
     * files and pass these options to shared context:
     * context.repoOptions  = { xxx: xxx }
     *
     * @param {Object} options
     * @param {Object} context
     * @param {String} taskId
     * @constructor
     */
    function AnalyzeOsRepoJob(options, context, taskId) {
        var self = this;
        AnalyzeOsRepoJob.super_.call(self, logger, options, context, taskId);

        self.nodeId = self.context.target;

        assert.string(self.options.osName);
        assert.string(self.options.repo);

        // Both http://xxx/repo and http://xxx/repo/ should be valid and point to same repository,
        // but our code prefer the previous one
        if (self.options.repo) {
            self.options.repo =  self.options.repo.trim();
            if (_.last(self.options.repo) === '/') {
                self.options.repo =  self.options.repo.substring(0, self.options.repo.length-1);
            }
        }
    }

    util.inherits(AnalyzeOsRepoJob, BaseJob);

    /**
     * @memberOf AnalyzeOsRepoJob
     */
    AnalyzeOsRepoJob.prototype._run = function() {
        var self = this;

        return Promise.resolve().then(function() {
            var handleFunc = self._findHandle(self.options.osName);
            return handleFunc.call(self, self.options.repo);
        }).then(function(result) {
            self.context.repoOptions = _.merge(
                result,
                {
                   //I add some smart conversion for 'repo' in constructor, so I want it be exposed
                   //to shared context to avoid duplicate conversion
                    repo: self.options.repo
                }
            );
            self._done();
        }).catch(function(error) {
            self._done(error);
            logger.error('fail to analyze the os repository', {
                error: error,
                osName: self.osName,
                repo: self.repo,
                nodeId: self.nodeId,
                context: self.context
            });
        });
    };

    /**
     * A function that can be used to provide a function with no operations or side effects
     * when lookup fails.
     *
     * @memberof AnalyzeOsRepoJob
     * @return {Function} A noop/empty function.
     * @private
     */
    AnalyzeOsRepoJob.prototype._noop = function() {};

    /**
     * find the handle function for current OS
     *
     * @memberof AnalyzeOsRepoJob
     * @param {String} osName - the name of target OS
     * @return {Function} the handle function for the input OS, if no function is defined, it will
     * return an empty function.
     */
    AnalyzeOsRepoJob.prototype._findHandle = function(osName) {
        var funcName = '_' + osName + 'Handle';
        var handleFunc = this[funcName];

        //Haven't defined a hanlding function for specified OS, it means it doesn't need to
        //analyze the OS repository, return an empty for fluent promise chain
        if (!handleFunc) {
            return AnalyzeOsRepoJob.prototype._noop;
        }

        if (!_.isFunction(handleFunc)) {
            throw(new Error('The handling for ' + osName + ' is not callable.'));
        }

        return handleFunc;
    };

    /**
     * Fetch the ESXi installation options from exteranl repository
     * @memberof AnalyzeOsRepoJob
     * @param {String} repo - the external repository address.
     * @return {Promise}
     */
    AnalyzeOsRepoJob.prototype._ESXiHandle = function (repo) {
        //first try the lower case because the installation has some problem when the repository
        //is in upper case, but anyway we will try the upper case as a retry, in future we (maybe
        //Vmware?) may have solution to fix the upper case problem.
        return repoTool.downloadViaHttp(repo + '/boot.cfg').catch(function() {
            return repoTool.downloadViaHttp(repo + '/BOOT.CFG');
        }).then(function(data) {
            var result = repoTool.parseEsxBootCfgFile(data);
            //The following values are required for ESXi installation, add pre-checking for these
            //value to provide error message earlier than template rendering, the time advance
            //can be as much as 5-10min for some systems.
            var requiredKeys = ['tbootFile', 'mbootFile', 'moduleFiles'];
            _.forEach(requiredKeys, function(key) {
                if (!result || !result.hasOwnProperty(key) || !result[key] ||
                    !_.isString(result[key])) {
                        throw new Error('The value \'' + key + '\' from ESXi repository is either '+
                            'missing or its format is not correct');
                }
            });
            return result;
        });
    };

    return AnalyzeOsRepoJob;
}