commands/run.js (246 lines of code) (raw):
'use strict';
const { parse } = require('../lib/parser');
const { spawn, exec } = require('child_process');
const { loadConfig, loadProfile, loadCurrentProfile } = require('../lib/profile');
const { default: Credential, Config } = require('@alicloud/credentials');
function onExit(cp) {
return new Promise((resolve) => {
cp.on('exit', (code) => resolve(code));
});
}
function onFinish(cp) {
return new Promise((resolve) => {
const stdout = [];
const stderr = [];
cp.stdout.on('data', (chunk) => {
stdout.push(chunk);
});
cp.stderr.on('data', (chunk) => {
stderr.push(chunk);
});
cp.on('exit', (code) => {
resolve({
code: code,
stdout: Buffer.concat(stdout),
stderr: Buffer.concat(stderr)
});
});
});
}
class CredentialModel {
constructor(id, secret, token) {
this.id = id;
this.secret = secret;
this.token = token;
}
getAccessKeyId() {
return this.id;
}
getAccessKeySecret() {
return this.secret;
}
getSecurityToken() {
return this.token;
}
toEnv() {
const result = {
'ALIBABACLOUD_ACCESS_KEY_ID': this.id,
'ALICLOUD_ACCESS_KEY_ID': this.id,
'ALIBABACLOUD_ACCESS_KEY_SECRET': this.secret,
'ALICLOUD_ACCESS_KEY_SECRET': this.secret
};
if (this.token) {
result['ALIBABACLOUD_SECURITY_TOKEN'] = this.token;
result['ALICLOUD_SECURITY_TOKEN'] = this.token;
result['SECURITY_TOKEN'] = this.token;
}
return result;
}
}
async function getCredential(profile, parent) {
switch (profile.mode) {
case 'AK': {
const id = profile['access_key_id'];
const secret = profile['access_key_secret'];
return new CredentialModel(id, secret);
}
case 'RamRoleArn': {
const config = new Config({
type: 'ram_role_arn',
accessKeyId: profile['access_key_id'], // AccessKeyId of your account
accessKeySecret: profile['access_key_secret'], // AccessKeySecret of your account
roleArn: profile['ram_role_arn'], // Format: acs:ram::USER_ID:role/ROLE_NAME
roleSessionName: profile['ram_session_name'] || 'ACC_SESSION', // Role Session Name
// policy: 'policy', // Not required, limit the permissions of STS Token
roleSessionExpiration: 3600, // Not required, limit the Valid time of STS Token
stsRegion: profile['sts_region'] || 'cn-hangzhou'
});
const cred = new Credential(config);
const id = await cred.getAccessKeyId();
const secret = await cred.getAccessKeySecret();
const securityToken = await cred.getSecurityToken();
return new CredentialModel(id, secret, securityToken);
}
case 'EcsRamRole': {
const config = new Config({
type: 'ecs_ram_role',
roleName: profile['ram_role_name']
});
const cred = new Credential(config);
const id = await cred.getAccessKeyId();
const secret = await cred.getAccessKeySecret();
const securityToken = await cred.getSecurityToken();
return new CredentialModel(id, secret, securityToken);
}
case 'CredentialsURI': {
const config = new Config({
type: 'credentials_uri',
credentialsURI: profile['credentials_uri']
});
const cred = new Credential(config);
const id = await cred.getAccessKeyId();
const secret = await cred.getAccessKeySecret();
const securityToken = await cred.getSecurityToken();
return new CredentialModel(id, secret, securityToken);
}
case 'ChainableRamRoleArn': {
const sourceProfileName = profile['source_profile'];
const sourceProfile = loadProfile(parent, sourceProfileName);
const credential = await getCredential(sourceProfile);
const config = new Config({
type: 'ram_role_arn',
accessKeyId: credential.getAccessKeyId(), // AccessKeyId of your account
accessKeySecret: credential.getAccessKeySecret(), // AccessKeySecret of your account
securityToken: credential.getSecurityToken(),
roleArn: profile['ram_role_arn'], // Format: acs:ram::USER_ID:role/ROLE_NAME
roleSessionName: profile['ram_session_name'] || 'ACC_SESSION', // Role Session Name
// policy: 'policy', // Not required, limit the permissions of STS Token
roleSessionExpiration: 3600, // Not required, limit the Valid time of STS Token
stsRegion: profile['sts_region'] || 'cn-hangzhou'
});
const cred = new Credential(config);
const id = await cred.getAccessKeyId();
const secret = await cred.getAccessKeySecret();
const securityToken = await cred.getSecurityToken();
return new CredentialModel(id, secret, securityToken);
}
case 'External': {
const command = profile['process_command'];
const cp = exec(command, {
cwd: process.cwd(),
encoding: null
});
const result = await onFinish(cp);
const stdout = result.stdout.toString();
const response = JSON.parse(stdout);
if (response.mode === 'AK') {
return new CredentialModel(response['access_key_id'], response['access_key_secret']);
} else if (response.mode === 'STS') {
return new CredentialModel(response['access_key_id'], response['access_key_secret'], response['security_token']);
} else if (response.mode === 'StsToken') {
return new CredentialModel(response['access_key_id'], response['access_key_secret'], response['sts_token']);
}
break;
}
default:
return null;
}
}
module.exports = class {
constructor(app) {
this.app = app;
this.name = 'run';
this.description = 'run command with profile';
this.useArgs = '0-n';
this.supportAfterCommand = true;
this.options = {
profile: {
short: 'p',
required: false,
description: `the profile name. default is current profile`
}
};
}
findOption(short) {
for (const [key, option] of Object.entries(this.options)) {
if (option.short === short) {
return key;
}
}
return null;
}
validOptions(full, short) {
const all = new Map();
for (const [key, value] of full) {
if (this.options[key]) {
all.set(key, value);
}
}
for (const [key, value] of short) {
const find = this.findOption(key);
if (find) {
all.set(key, value);
}
}
return all;
}
async run(argv) {
const { full, short, rest } = parse(argv);
const parsed = this.validOptions(full, short);
const config = await loadConfig();
if (!config) {
console.error(`No any configurations.`);
process.exit(1);
}
let profile;
if (parsed.has('profile')) {
const profileName = parsed.get('profile');
if (!profileName) {
console.error(`Please input a valid profile name.`);
process.exit(1);
}
profile = loadProfile(config, profileName);
if (!profile) {
console.error(`Can not find profile '${profileName}'.`);
process.exit(1);
}
} else {
// load default profile
profile = loadCurrentProfile(config);
}
const credential = await getCredential(profile, config);
if (!credential) {
console.error(`the mode '${profile.mode}' is not supported currently.`);
process.exit(1);
}
if (rest && rest.length > 0) {
const [cmd, ...args] = rest;
const child = spawn(cmd, args, {
env: {
...process.env,
// ak & sk & security token
...credential.toEnv()
},
cwd: process.cwd(),
shell: true, // add it for Windows
stdio: 'inherit'
});
const code = await onExit(child);
process.exit(code);
} else {
console.log(`export ALIBABACLOUD_ACCESS_KEY_ID=${credential.getAccessKeyId()}`);
console.log(`export ALIBABA_CLOUD_ACCESS_KEY_ID=${credential.getAccessKeyId()}`);
console.log(`export ALICLOUD_ACCESS_KEY_ID=${credential.getAccessKeyId()}`);
console.log(`export ALIBABACLOUD_ACCESS_KEY_SECRET=${credential.getAccessKeySecret()}`);
console.log(`export ALIBABA_CLOUD_ACCESS_KEY_SECRET=${credential.getAccessKeySecret()}`);
console.log(`export ALICLOUD_ACCESS_KEY_SECRET=${credential.getAccessKeySecret()}`);
if (credential.getSecurityToken()) {
console.log(`export ALIBABACLOUD_SECURITY_TOKEN=${credential.getSecurityToken()}`);
console.log(`export ALIBABA_CLOUD_SECURITY_TOKEN=${credential.getSecurityToken()}`);
console.log(`export ALICLOUD_SECURITY_TOKEN=${credential.getSecurityToken()}`);
console.log(`export SECURITY_TOKEN=${credential.getSecurityToken()}`);
} else {
console.log(`unset ALIBABACLOUD_SECURITY_TOKEN`);
console.log(`unset ALIBABA_CLOUD_SECURITY_TOKEN`);
console.log(`unset ALICLOUD_SECURITY_TOKEN`);
console.log(`unset SECURITY_TOKEN`);
}
}
}
};