baidubce-cli
Version:
baidu cloud engine command line tools
376 lines (312 loc) • 11.9 kB
JavaScript
/**
* Copyright (c) 2014 Baidu.com, Inc. All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
var fs = require('fs');
var path = require('path');
var u = require('underscore');
var Q = require('q');
var moment = require('moment');
var bce = require('baidubce-sdk');
var humanize = require('humanize');
var kPattern = /^bs:\/\/([^\/]+)(\/.*)?/;
var cmdConfig = require('../config');
var cmdPut = require('./cmd_put');
var argv = require('yargs')
.usage('Usage: $0 bs')
.boolean('h', {alias: 'help', describe: 'Show this help message.'})
.options('c', {alias: 'config', describe: 'Manage the baidubce configuration', 'boolean': true})
.options('list-buckets', {describe: 'List all buckets.', 'boolean': true})
.example('baidubce bs --list-buckets', 'List all buckets.')
.options('create-buckets', {describe: 'Create buckets.', 'boolean': true})
.example('baidubce bs --create-buckets my-bucket-1 my-bucket-2', 'Create one or more buckets.')
.options('delete-buckets', {describe: 'Delete buckets.', 'boolean': true})
.example('baidubce bs --delete-buckets my-bucket-1 my-bucket-2', 'Delete buckets')
.options('set-bucket-acl', {describe: 'Set bucket acl.', 'boolean': true})
.example('baidubce bs --set-bucket-acl bucket-1 bucket-2 public-read-write', 'Set buckets public-read-write acl')
.example('baidubce bs --set-bucket-acl bucket-1 bucket-2 private', 'Set buckets private acl\n')
.options('list-objects', {describe: 'List all objects.', 'boolean': true})
.options('start', {describe: 'The 0-based offset.'})
.options('limit', {describe: 'The maximum count.'})
.example('baidubce bs --list-objects my-bucket-1', 'List all objects in the specific bucket')
.example('baidubce bs --list-objects my-bucket-1 --start=10 --limit=20', 'List all objects in the specific bucket with `start` and `limit`')
.options('put-object', {describe: 'Upload a regular file', requiresArg: true})
.example('baidubce bs --put-object hello.js bs://my-bucket/world.js', 'Upload a regular file')
// .options('copy-object', {describe: 'Copy object from one bucket to another', 'boolean': true})
// .example('baidubce bs --copy-object bs://source/sk.js bs://target/tk.js <options>',
// 'Copy object from one bucket to another')
// .options('put-superfile', {describe: 'Upload a super large file', requiresArg: true})
// .example('baidubce bs --put-superfile hello.js bs://my-bucket/world.js', 'Upload a super large file')
.options('delete-objects', {describe: 'Delete objects.', 'boolean': true})
.example('baidubce bs --delete-objects my-bucket-1 hello.js world.js', 'Delete objects')
.check(function (args) {
var x = ['p', 's'];
var c = 0;
u.each(x, function (key) {
if (u.has(args, key)) {
if (!fs.existsSync(args[key])) {
throw new Error('No such file or directory ' + args[key]);
}
c += 1;
}
});
if (c > 0 && args._.length <= 0) {
// We need the destination
// baidubce bs --put=hello.js bs://my-bucket/world.js
// ^^^^^^^^^^^^^^^^^^^^^^^^
// This is the destination.
throw new Error('Please specify the destination bucket and key');
}
var destination = args._[0];
if (c > 0 && !kPattern.test(destination)) {
throw new Error('Invalid destination pattern, please refer the above ' +
'examples for more information.');
}
});
exports.run = function () {
var args = argv.argv;
if (args.h === true || args.help === true) {
argv.showHelp();
return;
}
else if (process.argv.length <= 2) {
argv.showHelp();
return;
}
if (args.c) {
if (!args._.length) {
cmdConfig.dump();
}
else {
cmdConfig.set(args._);
}
return;
}
var actionMap = {
'create-buckets': createBuckets,
'list-buckets': listBuckets,
'set-bucket-acl': setBucketsAcl,
'list-objects': listObjects,
'delete-objects': deleteObjects,
'delete-buckets': deleteBuckets,
'copy-object': copyObject
};
for (var ak in actionMap) {
if (args[ak] === true) {
return actionMap[ak](args._, args);
}
}
if (args['put-object'] || args['put-superfile']) {
var destination = args._[0];
var match = kPattern.exec(destination);
if (!match) {
console.error('Invalid destination pattern, please refer the above ' +
'examples for more information.');
return;
}
var bucket_name = match[1];
var key = getKey(args);
var client = getClient(bucket_name);
if (args['put-object']) {
return cmdPut.putRegularFile(client, bucket_name, key, args['put-object']);
}
else if (args['put-superfile']) {
return cmdPut.putSuperLargeFile(client, bucket_name, key, args['put-superfile']);
}
}
};
function deleteBuckets(buckets) {
return Q.all(u.map(buckets, function (bucket) {
return getClient(bucket).deleteBucket(bucket);
})).then(function (response) {
console.log('DONE');
}).catch(function (error) {
console.error(error);
});
}
function safeParse(text) {
try {
return JSON.parse(text);
}
catch (ex) {
return null;
}
}
function copyObject(args) {
if (args.length !== 2) {
console.error('Arguments insufficient');
return;
}
var source = args[0];
var target = args[1];
var options = safeParse(args[2]);
var m1 = kPattern.exec(source);
var m2 = kPattern.exec(target);
var source_bucket_name = m1[1];
var source_key = m1[2];
var target_bucket_name = m2[1];
var target_key = m2[2];
var c1 = getClient(source_bucket_name);
var c2 = getClient(target_bucket_name);
if (c1.config.endpoint !== c2.config.endpoint) {
console.error(source_bucket_name + ' and ' + target_bucket_name +
' belongs to different endpoint.');
return;
}
return c1.copyObject(source_bucket_name, source_key,
target_bucket_name, target_key, options).then(function (response) {
console.log('DONE');
}).catch(function (error) {
console.error(error);
});
}
function deleteObjects(args) {
if (args.length < 2) {
console.error('Arguments insufficient');
return;
}
var bucket = args.shift();
var objects = args;
var client = getClient(bucket);
return Q.all(u.map(objects, function (object) {
return client.deleteObject(bucket, object);
})).then(function (response) {
console.log('DONE');
}).catch(function (error) {
console.error(error);
});
}
function listObjects(args, options) {
if (!args || !args.length) {
console.error('Arguments insufficient');
return;
}
var bucket_name = args[0];
return getClient(bucket_name).listObjects(bucket_name, u.pick(options, 'start', 'limit'))
.then(function (response) {
var objects = response.body.object_list;
if (!objects.length) {
console.log('Empty bucket.');
return;
}
var table = require('text-table');
var rows = [['No.', 'Name', 'Size', 'Last Modified']];
u.each(objects, function (object, idx) {
rows.push([idx + 1, object.object, humanize.filesize(object.size),
moment(new Date(1000 * object.mdatetime)).format('YYYY-MM-DD hh:mm:ss')]);
});
console.log(table(rows, {stringLength: stringLength}));
}).catch(function (error) {
console.error(error);
});
}
function setBucketsAcl(args) {
if (args.length < 2) {
console.error('Arguments insufficient');
return;
}
var acl = args.pop();
var buckets = args;
return Q.all(u.map(buckets, function (bucket) {
return getClient(bucket).setBucketCannedAcl(bucket, acl);
})).then(function (response) {
console.log('DONE');
}).catch(function (error) {
console.log(error);
});
}
function listBuckets() {
getClient().listBuckets()
.then(function (response) {
var table = require('text-table');
var rows = [['No.', 'Name', 'Region', 'Used', 'Total', 'Create Date']];
u.each(response.body, function (bucket, idx) {
rows.push([idx + 1, bucket.bucket_name,
bucket.region ? bucket.region : '-',
humanize.filesize(bucket.used_capacity),
humanize.filesize(bucket.total_capacity),
moment(new Date(1000 * bucket.cdatetime)).format('YYYY-MM-DD hh:mm:ss')]);
});
console.log(table(rows, {stringLength: stringLength}));
})
.catch(function (error) {
console.error(error);
});
}
function createBuckets(buckets) {
if (!buckets || !buckets.length) {
console.log('Please specify the buckets you want to create.');
return;
}
return Q.all(u.map(buckets, function (bucket) {
return getClient(bucket).createBucket(bucket);
})).then(function (response) {
console.log('DONE');
}).catch(function (error) {
console.error(error);
});
}
/**
* @param {string=} opt_bucket_name The bucket name.
*
* @return {bce.BcsClient}
*/
function getClient(opt_bucket_name) {
var bucket_name = opt_bucket_name || '';
var ak;
var sk;
var endpoint;
if (bucket_name) {
ak = cmdConfig.get('bs.' + bucket_name + '.ak');
sk = cmdConfig.get('bs.' + bucket_name + '.sk');
endpoint = cmdConfig.get('bs.' + bucket_name + '.endpoint');
}
ak = ak || cmdConfig.get('bs.ak');
sk = sk || cmdConfig.get('bs.sk');
endpoint = endpoint || cmdConfig.get('bs.endpoint');
if (!ak || !sk || !endpoint) {
throw new Error('Please set ak, sk and endpoint.');
}
return new bce.BcsClient({
credentials: {ak: ak, sk: sk},
endpoint: endpoint
});
}
function getKey(args) {
// 1. check the destination first
var destination = args._[0];
var match = kPattern.exec(destination);
var key = match[2];
if (key) {
key = key.replace(/^\/+/g, '');
key = key.replace(/\/+$/g, '/');
}
// 2. then check the -p or -s argument value the first step failed.
var basename = path.basename(args['put-object'] || args['put-superfile']);
if (!basename) {
throw new Error('Invalid argument, can not find the object name');
}
// 3. If the key end with slash, so it's directory, we need append the basename
if (/\/$/.test(key)) {
key += basename;
}
else if (!key) {
key = basename;
}
return key;
}
function stringLength(s) {
var a = String(s).length;
// Non-ASCII character will produce double width.
var b = String(s).replace(/[\x00-\xff]/g, '').length;
return a + b;
}
/* vim: set ts=4 sw=4 sts=4 tw=120: */