src/cmds/agent.js
import { CliTrackerController } from 'azk/cli/cli_tracker_controller.js';
import { Helpers } from 'azk/cli/helpers';
import { _, path, config, lazy_require, log, fsAsync } from 'azk';
import { defer, async, promiseResolve, asyncUnsubscribe } from 'azk/utils/promises';
import { subscribe } from 'azk/utils/postal';
var lazy = lazy_require({
Client : [ 'azk/agent/client' ],
net : 'net',
VMController: 'azk/cmds/vm',
});
export default class Agent extends CliTrackerController {
get docker() {
return require('azk/docker').default;
}
index(opts) {
return this
.callAgent(opts)
.then((result) => {
if (!_.isNumber(result)) {
result = result.agent ? 0 : 1;
}
return result;
});
}
callAgent(opts) {
var params = {
action: _.head(this.route.actions) || opts.action
};
// Create a progress output
var view = Helpers.vmStartProgress(this.ui);
var _subscription = subscribe('#.status', (data) => {
view(data);
});
return asyncUnsubscribe(this, _subscription, function* () {
if (params.action === 'start') {
// And no running
var status = yield lazy.Client.status(opts.action, false);
if (!status.agent) {
// Check and load configures
var configure_file = this.normalized_params.options['configure-file'];
if (!_.isEmpty(configure_file)) {
configure_file = path.resolve(this.cwd, configure_file);
params.configs = require(configure_file);
} else {
this.ui.warning('status.agent.wait');
params.configs = yield Helpers.configure(this.ui);
}
// Run in daemon mode
if (!opts['no-daemon']) {
var args = _.clone(this.args);
return this._runDaemon(args, params.configs);
}
// Save pid and connect signals
var stopping = false;
this._captureSignal(() => {
if (!stopping) {
stopping = true;
_subscription.unsubscribe();
view({
type : "status",
status: "stopped"
});
status.pid.unlink();
this.ui.exit(1);
}
});
status.pid.update(process.pid);
// Remove and adding vm (to refresh vm configs)
if (!opts['no-reload-vm']) { yield this._removeVM(); }
// Generate a new tracker agent session id
this.ui.tracker.generateNewAgentSessionId();
this._trackStart();
}
}
// Changing directory for security
process.chdir(config('paths:azk_root'));
// Call action in agent
var promise = lazy.Client[params.action](params);
return promise.then((result) => {
if (params.action != "status") {
return result;
}
return (result.agent) ? 0 : 1;
});
});
}
_removeVM() {
if (config('agent:requires_vm')) {
if (!config('agent:dev:force_disable_vm')) {
var cmd_vm = new lazy.VMController({ ui: this.ui });
return cmd_vm.index({ action: 'remove'}).catch(() => {});
} else {
this.ui.ok("Force skip use vm");
}
}
return promiseResolve(true);
}
_runDaemon(args, configs) {
return async(this, function* () {
// Configure file
var file = config("paths:agent_config");
log.debug("[agent] save config file", file);
yield fsAsync.writeFile(file, JSON.stringify(configs));
args = args.concat(["--configure", file]);
// Color options
var envs = _.clone(process.env);
if (this.ui.useColours()) {
envs.AZK_FORCE_COLOR = true;
}
var cmd = `azk agent-daemon --no-daemon "${args.join('" "')}"`;
return this._runDaemonCommand(cmd, envs);
});
}
_runDaemonCommand(cmd, env) {
return defer((resolve) => {
var opts = {
detached: false,
stdio: [ 'ignore', process.stdout, process.stderr ],
env: env,
};
var child = this.ui.execSh(cmd, opts, (err) => {
resolve(err ? err.code : 0);
});
this._captureSignal((signal) => {
child.kill(signal);
});
});
}
_captureSignal(handler) {
process.on('SIGTERM', () => handler('SIGTERM'));
process.on('SIGINT' , () => handler('SIGINT'));
process.on('SIGQUIT', () => handler('SIGQUIT'));
}
_trackStart() {
// use VM?
var _subscription = subscribe("agent.agent.started.event", (/* data, envelope */) => {
// auto-unsubscribe
_subscription.unsubscribe();
var vm_data = {};
if (config("agent:requires_vm")) {
vm_data = {
cpus: config("agent:vm:cpus"),
memory: config("agent:vm:memory")
};
}
// Track agent start
this.docker.version().then((result) => {
this.addDataToTracker({
vm: vm_data,
docker: {
version: result
}
});
return this.sendTrackerData();
}, (error) => {
log.info(error);
});
});
}
}