merge with upstream
Tento commit je obsažen v:
		
						revize
						caa6da7821
					
				
							
								
								
									
										28
									
								
								CHANGES.md
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								CHANGES.md
									
									
									
									
									
								
							| @ -6,6 +6,34 @@ Known issues: | ||||
| 
 | ||||
| ## not yet released | ||||
| 
 | ||||
| (nothing) | ||||
| 
 | ||||
| ## 7.0.0 | ||||
| 
 | ||||
| - [Backward incompatible.] `triton image get NAME|SHORTID` will now *exclude* | ||||
|   inactive images by default. Before this change inactive images (e.g. those | ||||
|   with a state of "creating" or "unactivated" or "disabled") would be | ||||
|   included. Use the new `-a,--all` option to include inactive images. This | ||||
|   matches the behavior of `triton image list [-a,--all] ...`. | ||||
| 
 | ||||
| - [joyent/node-triton#258] `triton instance create IMAGE ...` will now exclude | ||||
|   inactive images when looking for an image with the given name. | ||||
| 
 | ||||
| ## 6.3.0 | ||||
| 
 | ||||
| - [joyent/node-triton#259] Added basic support for use of SSH bastion hosts | ||||
|   to access zones on private fabrics.  If the `tritoncli.ssh.proxy` tag is set | ||||
|   on an instance, `triton ssh` will look up the name or UUID of the proxy | ||||
|   instance and use `ssh -o ProxyJump` to tunnel the connection to the target. | ||||
|   If the `tritoncli.ssh.ip` tag is set on an instance, `triton ssh` will use | ||||
|   that IP address instead of the `primaryIp` when making its connection. | ||||
| 
 | ||||
| ## 6.2.0 | ||||
| 
 | ||||
| - [joyent/node-triton#255, joyent/node-triton#257] Improved the interface | ||||
|   and documentation of `triton network create` and `triton vlan create`.  In | ||||
|   particular, it is now possible to specify static routes and DNS resolvers. | ||||
| 
 | ||||
| ## 6.1.2 | ||||
| 
 | ||||
| - [joyent/node-triton#249] Error when creating or deleting profiles when | ||||
|  | ||||
							
								
								
									
										19
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								README.md
									
									
									
									
									
								
							| @ -2,18 +2,21 @@ | ||||
| 
 | ||||
| # node-spearhead | ||||
| 
 | ||||
| This repository holds the node-spearhead CLI tool to work with the Spearhead Cloud. | ||||
| This repository holds the node-spearhead CLI tool to work with the Spearhead  | ||||
| Cloud. It is a fork of [node-triton](https://github.com/joyent/node-triton). | ||||
| 
 | ||||
| ## Installation and configuration | ||||
| 
 | ||||
| ### Get a Spearhead Cloud account | ||||
| 
 | ||||
| Create an account on the Spearhead Cloud and upload your SSH key.[!TBD: docs]You can create an account  [here](https://spearhead.cloud/). | ||||
| Create an account on the Spearhead Cloud and upload your SSH key. You can create an account  | ||||
| [here](https://spearhead.cloud/). | ||||
| 
 | ||||
| 
 | ||||
| ### Data-centers | ||||
| 
 | ||||
| The list of available Spearhead Cloud data-centers is available [here](https://spearhead.cloud/datacenters). | ||||
| The list of available Spearhead Cloud data-centers is available  | ||||
| [here](https://spearhead.cloud/datacenters). | ||||
| 
 | ||||
| 
 | ||||
| ### Installation | ||||
| @ -22,8 +25,14 @@ Install [node.js](http://nodejs.org/), then: | ||||
| 
 | ||||
|     npm install -g spearhead | ||||
| 
 | ||||
| Now you ca use `spearhead` to interact with our Public Cloud. More details about installation and configuration are available [here](https://docs.spearhead.cloud). | ||||
| Verify that it is installed and on your PATH: | ||||
|     $ spearhead --version | ||||
|     Spearhead CLI 6.1.4 | ||||
|     https://code.spearhead.cloud/Spearhead/node-spearhead | ||||
|      | ||||
| Now you ca use `spearhead` to interact with our Public Cloud. More details  | ||||
| about installation and configuration are available  | ||||
| [here](https://docs.spearhead.cloud). | ||||
| 
 | ||||
| ## License | ||||
| 
 | ||||
| MPL 2.0 | ||||
|  | ||||
| @ -504,7 +504,7 @@ CloudApi.prototype.UPDATE_NETWORK_IP_FIELDS = { | ||||
| // --- Fabric VLANs
 | ||||
| 
 | ||||
| /** | ||||
|  * Creates a network on a fabric (specifically: a fabric VLAN). | ||||
|  * Creates a network on a fabric VLAN. | ||||
|  * | ||||
|  * @param {Object} options object containing: | ||||
|  *      - {Integer} vlan_id (required) VLAN's id, between 0-4095. | ||||
| @ -513,7 +513,8 @@ CloudApi.prototype.UPDATE_NETWORK_IP_FIELDS = { | ||||
|  *      - {String} provision_start_ip (required) First assignable IP addr. | ||||
|  *      - {String} provision_end_ip (required) Last assignable IP addr. | ||||
|  *      - {String} gateway (optional) Gateway IP address. | ||||
|  *      - {String} resolvers (optional) Static routes for hosts on network. | ||||
|  *      - {Array} resolvers (optional) DNS resolvers for hosts on network. | ||||
|  *      - {Object} routes (optional) Static routes for hosts on network. | ||||
|  *      - {String} description (optional) | ||||
|  *      - {Boolean} internet_nat (optional) Whether to provision an Internet | ||||
|  *          NAT on the gateway address (default: true). | ||||
| @ -528,8 +529,8 @@ function createFabricNetwork(opts, cb) { | ||||
|     assert.string(opts.provision_start_ip, 'opts.provision_start_ip'); | ||||
|     assert.string(opts.provision_end_ip, 'opts.provision_end_ip'); | ||||
|     assert.optionalString(opts.gateway, 'opts.gateway'); | ||||
|     assert.optionalString(opts.resolvers, 'opts.resolvers'); | ||||
|     assert.optionalString(opts.routes, 'opts.routes'); | ||||
|     assert.optionalArrayOfString(opts.resolvers, 'opts.resolvers'); | ||||
|     assert.optionalObject(opts.routes, 'opts.routes'); | ||||
|     assert.optionalBool(opts.internet_nat, 'opts.internet_nat'); | ||||
| 
 | ||||
|     var data = common.objCopy(opts); | ||||
|  | ||||
| @ -31,7 +31,11 @@ function do_get(subcmd, opts, args, callback) { | ||||
|             callback(setupErr); | ||||
|             return; | ||||
|         } | ||||
|         tritonapi.getImage(args[0], function onRes(err, img) { | ||||
|         var getOpts = { | ||||
|             name: args[0], | ||||
|             excludeInactive: !opts.all | ||||
|         }; | ||||
|         tritonapi.getImage(getOpts, function onRes(err, img) { | ||||
|             if (err) { | ||||
|                 return callback(err); | ||||
|             } | ||||
| @ -56,6 +60,15 @@ do_get.options = [ | ||||
|         names: ['json', 'j'], | ||||
|         type: 'bool', | ||||
|         help: 'JSON stream output.' | ||||
|     }, | ||||
|     { | ||||
|         group: 'Filtering options' | ||||
|     }, | ||||
|     { | ||||
|         names: ['all', 'a'], | ||||
|         type: 'bool', | ||||
|         help: 'Include all images when matching by name or short ID, not ' + | ||||
|             'just "active" ones. By default only active images are included.' | ||||
|     } | ||||
| ]; | ||||
| 
 | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
|  */ | ||||
| 
 | ||||
| /* | ||||
|  * Copyright 2018 Joyent, Inc. | ||||
|  * Copyright 2019 Joyent, Inc. | ||||
|  * | ||||
|  * `triton instance create ...` | ||||
|  */ | ||||
| @ -203,6 +203,7 @@ function do_create(subcmd, opts, args, cb) { | ||||
|         function getImg(ctx, next) { | ||||
|             var _opts = { | ||||
|                 name: args[0], | ||||
|                 excludeInactive: true, | ||||
|                 useCache: true | ||||
|             }; | ||||
|             tritonapi.getImage(_opts, function (err, img) { | ||||
|  | ||||
| @ -5,11 +5,12 @@ | ||||
|  */ | ||||
| 
 | ||||
| /* | ||||
|  * Copyright 2017 Joyent, Inc. | ||||
|  * Copyright (c) 2018, Joyent, Inc. | ||||
|  * | ||||
|  * `triton instance ssh ...` | ||||
|  */ | ||||
| 
 | ||||
| var assert = require('assert-plus'); | ||||
| var path = require('path'); | ||||
| var spawn = require('child_process').spawn; | ||||
| var vasync = require('vasync'); | ||||
| @ -17,6 +18,30 @@ var vasync = require('vasync'); | ||||
| var common = require('../common'); | ||||
| var errors = require('../errors'); | ||||
| 
 | ||||
| /* | ||||
|  * The tag "tritoncli.ssh.ip" may be set to an IP address that belongs to the | ||||
|  * instance but which is not the primary IP.  If set, we will use that IP | ||||
|  * address for the SSH connection instead of the primary IP. | ||||
|  */ | ||||
| var TAG_SSH_IP = 'tritoncli.ssh.ip'; | ||||
| 
 | ||||
| /* | ||||
|  * The tag "tritoncli.ssh.proxy" may be set to either the name or the UUID of | ||||
|  * another instance in this account.  If set, we will use the "ProxyJump" | ||||
|  * feature of SSH to tunnel through the SSH server on that host.  This is | ||||
|  * useful when exposing a single zone to the Internet while keeping the rest of | ||||
|  * your infrastructure on a private fabric. | ||||
|  */ | ||||
| var TAG_SSH_PROXY = 'tritoncli.ssh.proxy'; | ||||
| 
 | ||||
| /* | ||||
|  * The tag "tritoncli.ssh.proxyuser" may be set on the instance used as an SSH | ||||
|  * proxy.  If set, we will use this value when making the proxy connection | ||||
|  * (i.e., it will be passed via the "ProxyJump" option).  If not set, the | ||||
|  * default user selection behaviour applies. | ||||
|  */ | ||||
| var TAG_SSH_PROXY_USER = 'tritoncli.ssh.proxyuser'; | ||||
| 
 | ||||
| 
 | ||||
| function do_ssh(subcmd, opts, args, callback) { | ||||
|     if (opts.help) { | ||||
| @ -30,10 +55,12 @@ function do_ssh(subcmd, opts, args, callback) { | ||||
|     var id = args.shift(); | ||||
| 
 | ||||
|     var user; | ||||
|     var overrideUser = false; | ||||
|     var i = id.indexOf('@'); | ||||
|     if (i >= 0) { | ||||
|         user = id.substr(0, i); | ||||
|         id = id.substr(i + 1); | ||||
|         overrideUser = true; | ||||
|     } | ||||
| 
 | ||||
|     vasync.pipeline({arg: {cli: this.top}, funcs: [ | ||||
| @ -48,17 +75,112 @@ function do_ssh(subcmd, opts, args, callback) { | ||||
| 
 | ||||
|                 ctx.inst = inst; | ||||
| 
 | ||||
|                 ctx.ip = inst.primaryIp; | ||||
|                 if (inst.tags && inst.tags[TAG_SSH_IP]) { | ||||
|                     ctx.ip = inst.tags[TAG_SSH_IP]; | ||||
|                     if (!inst.ips || inst.ips.indexOf(ctx.ip) === -1) { | ||||
|                         next(new Error('IP address ' + ctx.ip + ' not ' + | ||||
|                             'attached to the instance')); | ||||
|                         return; | ||||
|                     } | ||||
|                 } else { | ||||
|                     ctx.ip = inst.primaryIp; | ||||
|                 } | ||||
| 
 | ||||
|                 if (!ctx.ip) { | ||||
|                     next(new Error('primaryIp not found for instance')); | ||||
|                     next(new Error('IP address not found for instance')); | ||||
|                     return; | ||||
|                 } | ||||
|                 next(); | ||||
|             }); | ||||
|         }, | ||||
| 
 | ||||
|         function getInstanceBastionIp(ctx, next) { | ||||
|             if (opts.no_proxy) { | ||||
|                 setImmediate(next); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             if (!ctx.inst.tags || !ctx.inst.tags[TAG_SSH_PROXY]) { | ||||
|                 setImmediate(next); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             ctx.cli.tritonapi.getInstance(ctx.inst.tags[TAG_SSH_PROXY], | ||||
|                 function (err, proxy) { | ||||
| 
 | ||||
|                 if (err) { | ||||
|                     next(err); | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                 if (proxy.tags && proxy.tags[TAG_SSH_IP]) { | ||||
|                     ctx.proxyIp = proxy.tags[TAG_SSH_IP]; | ||||
|                     if (!proxy.ips || proxy.ips.indexOf(ctx.proxyIp) === -1) { | ||||
|                         next(new Error('IP address ' + ctx.proxyIp + ' not ' + | ||||
|                             'attached to the instance')); | ||||
|                         return; | ||||
|                     } | ||||
|                 } else { | ||||
|                     ctx.proxyIp = proxy.primaryIp; | ||||
|                 } | ||||
| 
 | ||||
|                 ctx.proxyImage = proxy.image; | ||||
| 
 | ||||
|                 /* | ||||
|                  * Selecting the right user to use for the proxy connection is | ||||
|                  * somewhat nuanced, in order to allow for various useful | ||||
|                  * configurations.  We wish to enable the following cases: | ||||
|                  * | ||||
|                  * 1. The least sophisticated configuration; i.e., using two | ||||
|                  *    instances (the target instance and the proxy instnace) | ||||
|                  *    with the default "root" (or, e.g., "ubuntu") account | ||||
|                  *    and smartlogin or authorized_keys metadata for SSH key | ||||
|                  *    management. | ||||
|                  * | ||||
|                  * 2. The user has set up their own accounts (e.g., "roberta") | ||||
|                  *    in all of their instances and does their own SSH key | ||||
|                  *    management.  They connect with: | ||||
|                  * | ||||
|                  *        triton inst ssh roberta@instance | ||||
|                  * | ||||
|                  *    In this case we will use "roberta" for both the proxy | ||||
|                  *    and the target instance.  This means a user provided on | ||||
|                  *    the command line will override the per-image default | ||||
|                  *    user (e.g., "root" or "ubuntu") -- if the user wants to | ||||
|                  *    retain the default account for the proxy, they should | ||||
|                  *    use case 3 below. | ||||
|                  * | ||||
|                  * 3. The user has set up their own accounts in the target | ||||
|                  *    instance (e.g., "felicity"), but the proxy instance is | ||||
|                  *    using a single specific account that should be used by | ||||
|                  *    all users in the organisation (e.g., "partyline").  In | ||||
|                  *    this case, we want the user to be able to specify the | ||||
|                  *    global proxy account setting as a tag on the proxy | ||||
|                  *    instance, so that for: | ||||
|                  * | ||||
|                  *        triton inst ssh felicity@instance | ||||
|                  * | ||||
|                  *    ... we will use "-o ProxyJump partyline@proxy" but | ||||
|                  *    still use "felicity" for the target connection.  This | ||||
|                  *    last case requires the proxy user tag (if set) to | ||||
|                  *    override a user provided on the command line. | ||||
|                  */ | ||||
|                 if (proxy.tags && proxy.tags[TAG_SSH_PROXY_USER]) { | ||||
|                     ctx.proxyUser = proxy.tags[TAG_SSH_PROXY_USER]; | ||||
|                 } | ||||
| 
 | ||||
|                 if (!ctx.proxyIp) { | ||||
|                     next(new Error('IP address not found for proxy instance')); | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                 next(); | ||||
|             }); | ||||
|         }, | ||||
| 
 | ||||
|         function getUser(ctx, next) { | ||||
|             if (user) { | ||||
|             if (overrideUser) { | ||||
|                 assert.string(user, 'user'); | ||||
|                 next(); | ||||
|                 return; | ||||
|             } | ||||
| @ -73,8 +195,8 @@ function do_ssh(subcmd, opts, args, callback) { | ||||
|                 } | ||||
| 
 | ||||
|                 /* | ||||
|                  * This is a convention as seen on Joyent's | ||||
|                  * "ubuntu-certified" KVM images. | ||||
|                  * This is a convention as seen on Joyent's "ubuntu-certified" | ||||
|                  * KVM images. | ||||
|                  */ | ||||
|                 if (image.tags && image.tags.default_user) { | ||||
|                     user = image.tags.default_user; | ||||
| @ -86,9 +208,64 @@ function do_ssh(subcmd, opts, args, callback) { | ||||
|             }); | ||||
|         }, | ||||
| 
 | ||||
|         function getBastionUser(ctx, next) { | ||||
|             if (!ctx.proxyImage || ctx.proxyUser) { | ||||
|                 /* | ||||
|                  * If there is no image for the proxy host, or an override user | ||||
|                  * was already provided in the tags of the proxy instance | ||||
|                  * itself, we don't need to look up the default user. | ||||
|                  */ | ||||
|                 next(); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             if (overrideUser) { | ||||
|                 /* | ||||
|                  * A user was provided on the command line, but no user | ||||
|                  * override tag was present on the proxy instance.  To enable | ||||
|                  * use case 2 (see comments above) we'll prefer this user over | ||||
|                  * the image default. | ||||
|                  */ | ||||
|                 assert.string(user, 'user'); | ||||
|                 ctx.proxyUser = user; | ||||
|                 next(); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             ctx.cli.tritonapi.getImage({ | ||||
|                 name: ctx.proxyImage, | ||||
|                 useCache: true | ||||
|             }, function (getImageErr, image) { | ||||
|                 if (getImageErr) { | ||||
|                     next(getImageErr); | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                 /* | ||||
|                  * This is a convention as seen on Joyent's "ubuntu-certified" | ||||
|                  * KVM images. | ||||
|                  */ | ||||
|                 assert.ok(!ctx.proxyUser, 'proxy user set twice'); | ||||
|                 if (image.tags && image.tags.default_user) { | ||||
|                     ctx.proxyUser = image.tags.default_user; | ||||
|                 } else { | ||||
|                     ctx.proxyUser = 'root'; | ||||
|                 } | ||||
| 
 | ||||
|                 next(); | ||||
|             }); | ||||
|         }, | ||||
| 
 | ||||
|         function doSsh(ctx, next) { | ||||
|             args = ['-l', user, ctx.ip].concat(args); | ||||
| 
 | ||||
|             if (ctx.proxyIp) { | ||||
|                 assert.string(ctx.proxyUser, 'ctx.proxyUser'); | ||||
|                 args = [ | ||||
|                     '-o', 'ProxyJump=' + ctx.proxyUser + '@' + ctx.proxyIp | ||||
|                 ].concat(args); | ||||
|             } | ||||
| 
 | ||||
|             /* | ||||
|              * By default we disable ControlMaster (aka mux, aka SSH | ||||
|              * connection multiplexing) because of | ||||
| @ -133,6 +310,11 @@ do_ssh.options = [ | ||||
|         names: ['help', 'h'], | ||||
|         type: 'bool', | ||||
|         help: 'Show this help.' | ||||
|     }, | ||||
|     { | ||||
|         names: ['no-proxy'], | ||||
|         type: 'bool', | ||||
|         help: 'Disable SSH proxy support (ignore "tritoncli.ssh.proxy" tag)' | ||||
|     } | ||||
| ]; | ||||
| do_ssh.synopses = ['{{name}} ssh [-h] [USER@]INST [SSH-ARGUMENTS]']; | ||||
| @ -150,6 +332,26 @@ do_ssh.help = [ | ||||
|     'If USER is not specified and the default_user tag is not set, the user', | ||||
|     'is assumed to be \"root\".', | ||||
|     '', | ||||
|     'The "tritoncli.ssh.proxy" tag on the target instance may be set to', | ||||
|     'the name or the UUID of another instance through which to proxy this', | ||||
|     'SSH connection.  If set, the primary IP of the proxy instance will be', | ||||
|     'loaded and passed to SSH via the ProxyJump option.  The --no-proxy', | ||||
|     'flag can be used to ignore the tag and force a direct connection.', | ||||
|     '', | ||||
|     'For example, to proxy connections to zone "narnia" through "wardrobe":', | ||||
|     '    triton instance tag set narnia tritoncli.ssh.proxy=wardrobe', | ||||
|     '', | ||||
|     'The "tritoncli.ssh.ip" tag on the target instance may be set to the', | ||||
|     'IP address to use for SSH connections.  This may be useful if the', | ||||
|     'primary IP address is not available for SSH connections.  This address', | ||||
|     'must be set to one of the IP addresses attached to the instance.', | ||||
|     '', | ||||
|     'The "tritoncli.ssh.proxyuser" tag on the proxy instance may be set to', | ||||
|     'the user account that should be used for the proxy connection (i.e., via', | ||||
|     'the SSH ProxyJump option).  This is useful when all users of the proxy', | ||||
|     'instance should use a special common account, and will override the USER', | ||||
|     'value (if one is provided) for the SSH connection to the target instance.', | ||||
|     '', | ||||
|     'There is a known issue with SSH connection multiplexing (a.k.a. ', | ||||
|     'ControlMaster, mux) where stdout/stderr is lost. As a workaround, `ssh`', | ||||
|     'is spawned with options disabling ControlMaster. See ', | ||||
|  | ||||
| @ -18,8 +18,6 @@ var common = require('../common'); | ||||
| var errors = require('../errors'); | ||||
| 
 | ||||
| 
 | ||||
| var OPTIONAL_OPTS = ['description', 'gateway', 'resolvers', 'routes']; | ||||
| 
 | ||||
| 
 | ||||
| function do_create(subcmd, opts, args, cb) { | ||||
|     assert.optionalString(opts.name, 'opts.name'); | ||||
| @ -28,13 +26,15 @@ function do_create(subcmd, opts, args, cb) { | ||||
|     assert.optionalString(opts.end_ip, 'opts.end_ip'); | ||||
|     assert.optionalString(opts.description, 'opts.description'); | ||||
|     assert.optionalString(opts.gateway, 'opts.gateway'); | ||||
|     assert.optionalString(opts.resolvers, 'opts.resolvers'); | ||||
|     assert.optionalString(opts.routes, 'opts.routes'); | ||||
|     assert.optionalArrayOfString(opts.resolver, 'opts.resolver'); | ||||
|     assert.optionalArrayOfString(opts.route, 'opts.route'); | ||||
|     assert.optionalBool(opts.no_nat, 'opts.no_nat'); | ||||
|     assert.optionalBool(opts.json, 'opts.json'); | ||||
|     assert.optionalBool(opts.help, 'opts.help'); | ||||
|     assert.func(cb, 'cb'); | ||||
| 
 | ||||
|     var i; | ||||
| 
 | ||||
|     if (opts.help) { | ||||
|         this.do_help('help', {}, [subcmd], cb); | ||||
|         return; | ||||
| @ -53,23 +53,74 @@ function do_create(subcmd, opts, args, cb) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (!opts.subnet) { | ||||
|         cb(new errors.UsageError('must specify --subnet (-s) option')); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (!opts.name) { | ||||
|         cb(new errors.UsageError('must specify --name (-n) option')); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (!opts.start_ip) { | ||||
|         cb(new errors.UsageError('must specify --start-ip (-S) option')); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (!opts.end_ip) { | ||||
|         cb(new errors.UsageError('must specify --end-ip (-E) option')); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     var createOpts = { | ||||
|         vlan_id: vlanId, | ||||
|         name:    opts.name, | ||||
|         subnet:  opts.subnet, | ||||
|         name: opts.name, | ||||
|         subnet: opts.subnet, | ||||
|         provision_start_ip: opts.start_ip, | ||||
|         provision_end_ip:   opts.end_ip | ||||
|         provision_end_ip: opts.end_ip, | ||||
|         resolvers: [], | ||||
|         routes: {} | ||||
|     }; | ||||
| 
 | ||||
|     if (opts.resolver) { | ||||
|         for (i = 0; i < opts.resolver.length; i++) { | ||||
|             if (createOpts.resolvers.indexOf(opts.resolver[i]) === -1) { | ||||
|                 createOpts.resolvers.push(opts.resolver[i]); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (opts.route) { | ||||
|         for (i = 0; i < opts.route.length; i++) { | ||||
|             var m = opts.route[i].match(new RegExp('^([^=]+)=([^=]+)$')); | ||||
| 
 | ||||
|             if (m === null) { | ||||
|                 cb(new errors.UsageError('invalid route: ' + opts.route[i])); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             createOpts.routes[m[1]] = m[2]; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (opts.no_nat) { | ||||
|         createOpts.internet_nat = false; | ||||
|     } | ||||
| 
 | ||||
|     OPTIONAL_OPTS.forEach(function (attr) { | ||||
|         if (opts[attr]) { | ||||
|             createOpts[attr] = opts[attr]; | ||||
|     if (opts.gateway) { | ||||
|         createOpts.gateway = opts.gateway; | ||||
|     } else { | ||||
|         if (!opts.no_nat) { | ||||
|             cb(new errors.UsageError('without a --gateway (-g), you must ' + | ||||
|               'specify --no-nat (-x)')); | ||||
|             return; | ||||
|         } | ||||
|     }); | ||||
|     } | ||||
| 
 | ||||
|     if (opts.description) { | ||||
|         createOpts.description = opts.description; | ||||
|     } | ||||
| 
 | ||||
|     var cli = this.top; | ||||
| 
 | ||||
| @ -106,9 +157,7 @@ do_create.options = [ | ||||
|         help: 'Show this help.' | ||||
|     }, | ||||
|     { | ||||
|         names: ['json', 'j'], | ||||
|         type: 'bool', | ||||
|         help: 'JSON stream output.' | ||||
|         group: 'Create options' | ||||
|     }, | ||||
|     { | ||||
|         names: ['name', 'n'], | ||||
| @ -123,46 +172,67 @@ do_create.options = [ | ||||
|         help: 'Description of the NETWORK.' | ||||
|     }, | ||||
|     { | ||||
|         names: ['subnet'], | ||||
|         group: '' | ||||
|     }, | ||||
|     { | ||||
|         names: ['subnet', 's'], | ||||
|         type: 'string', | ||||
|         helpArg: 'SUBNET', | ||||
|         help: 'A CIDR string describing the NETWORK.' | ||||
|     }, | ||||
|     { | ||||
|         names: ['start_ip'], | ||||
|         names: ['start-ip', 'S', 'start_ip'], | ||||
|         type: 'string', | ||||
|         helpArg: 'START_IP', | ||||
|         help: 'First assignable IP address on NETWORK.' | ||||
|     }, | ||||
|     { | ||||
|         names: ['end_ip'], | ||||
|         names: ['end-ip', 'E', 'end_ip'], | ||||
|         type: 'string', | ||||
|         helpArg: 'END_IP', | ||||
|         help: 'Last assignable IP address on NETWORK.' | ||||
|     }, | ||||
|     { | ||||
|         names: ['gateway'], | ||||
|         type: 'string', | ||||
|         helpArg: 'GATEWAY', | ||||
|         help: 'Gateway IP address.' | ||||
|         group: '' | ||||
|     }, | ||||
|     { | ||||
|         names: ['resolvers'], | ||||
|         names: ['gateway', 'g'], | ||||
|         type: 'string', | ||||
|         helpArg: 'RESOLVERS', | ||||
|         help: 'Resolver IP addresses.' | ||||
|         helpArg: 'IP', | ||||
|         help: 'Default gateway IP address.' | ||||
|     }, | ||||
|     { | ||||
|         names: ['routes'], | ||||
|         type: 'string', | ||||
|         helpArg: 'ROUTES', | ||||
|         help: 'Static routes for hosts on NETWORK.' | ||||
|         names: ['resolver', 'r'], | ||||
|         type: 'arrayOfString', | ||||
|         helpArg: 'RESOLVER', | ||||
|         help: 'DNS resolver IP address.  Specify multiple -r options for ' + | ||||
|             'multiple resolvers.' | ||||
|     }, | ||||
|     { | ||||
|         names: ['no_nat'], | ||||
|         names: ['route', 'R'], | ||||
|         type: 'arrayOfString', | ||||
|         helpArg: 'SUBNET=IP', | ||||
|         help: [ 'Static route for network.  Each route must include the', | ||||
|             'subnet (IP address with CIDR prefix length) and the router', | ||||
|             'address.  Specify multiple -R options for multiple static', | ||||
|             'routes.' ].join(' ') | ||||
|     }, | ||||
|     { | ||||
|         group: '' | ||||
|     }, | ||||
|     { | ||||
|         names: ['no-nat', 'x', 'no_nat'], | ||||
|         type: 'bool', | ||||
|         helpArg: 'NO_NAT', | ||||
|         help: 'Disable creation of an Internet NAT zone on GATEWAY.' | ||||
|     }, | ||||
|     { | ||||
|         group: 'Other options' | ||||
|     }, | ||||
|     { | ||||
|         names: ['json', 'j'], | ||||
|         type: 'bool', | ||||
|         help: 'JSON stream output.' | ||||
|     } | ||||
| ]; | ||||
| 
 | ||||
| @ -175,13 +245,29 @@ do_create.help = [ | ||||
|     '', | ||||
|     '{{options}}', | ||||
|     '', | ||||
|     'Example:', | ||||
|     '    triton network create -n accounting --subnet=192.168.0.0/24', | ||||
|     '           --start_ip=192.168.0.1 --end_ip=192.168.0.254 2' | ||||
|     'Examples:', | ||||
|     '    Create the "accounting" network on VLAN 1000:', | ||||
|     '        triton network create -n accounting --subnet 192.168.0.0/24 \\', | ||||
|     '            --start-ip 192.168.0.1 --end-ip 192.168.0.254 --no-nat \\', | ||||
|     '            1000', | ||||
|     '', | ||||
|     '    Create the "eng" network on VLAN 1001 with a pair of static routes:', | ||||
|     '        triton network create -n eng -s 192.168.1.0/24 \\', | ||||
|     '            -S 192.168.1.1 -E 192.168.1.249 --no-nat \\', | ||||
|     '            --route 10.1.1.0/24=192.168.1.50 \\', | ||||
|     '            --route 10.1.2.0/24=192.168.1.100 \\', | ||||
|     '            1001', | ||||
|     '', | ||||
|     '    Create the "ops" network on VLAN 1002 with DNS resolvers and NAT:', | ||||
|     '        triton network create -n ops -s 192.168.2.0/24 \\', | ||||
|     '            -S 192.168.2.10 -E 192.168.2.249 \\', | ||||
|     '            --resolver 8.8.8.8 --resolver 8.4.4.4 \\', | ||||
|     '            --gateway 192.168.2.1 \\', | ||||
|     '            1002' | ||||
| ].join('\n'); | ||||
| 
 | ||||
| do_create.helpOpts = { | ||||
|     helpCol: 25 | ||||
|     helpCol: 16 | ||||
| }; | ||||
| 
 | ||||
| module.exports = do_create; | ||||
|  | ||||
| @ -5,13 +5,14 @@ | ||||
|  */ | ||||
| 
 | ||||
| /* | ||||
|  * Copyright 2017 Joyent, Inc. | ||||
|  * Copyright (c) 2018, Joyent, Inc. | ||||
|  * | ||||
|  * `triton vlan create ...` | ||||
|  */ | ||||
| 
 | ||||
| var assert = require('assert-plus'); | ||||
| var format = require('util').format; | ||||
| var jsprim = require('jsprim'); | ||||
| var vasync = require('vasync'); | ||||
| 
 | ||||
| var common = require('../common'); | ||||
| @ -37,12 +38,22 @@ function do_create(subcmd, opts, args, cb) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     var createOpts = { | ||||
|         vlan_id: +args[0] | ||||
|     }; | ||||
|     if (opts.name) { | ||||
|         createOpts.name = opts.name; | ||||
|     var vlanId = jsprim.parseInteger(args[0], { allowSign: false }); | ||||
|     if (typeof (vlanId) !== 'number') { | ||||
|         cb(new errors.UsageError('VLAN must be an integer')); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (!opts.name) { | ||||
|         cb(new errors.UsageError('must provide a --name (-n)')); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     var createOpts = { | ||||
|         vlan_id: vlanId, | ||||
|         name: opts.name | ||||
|     }; | ||||
| 
 | ||||
|     if (opts.description) { | ||||
|         createOpts.description = opts.description; | ||||
|     } | ||||
| @ -86,9 +97,7 @@ do_create.options = [ | ||||
|         help: 'Show this help.' | ||||
|     }, | ||||
|     { | ||||
|         names: ['json', 'j'], | ||||
|         type: 'bool', | ||||
|         help: 'JSON stream output.' | ||||
|         group: 'Create options' | ||||
|     }, | ||||
|     { | ||||
|         names: ['name', 'n'], | ||||
| @ -101,6 +110,14 @@ do_create.options = [ | ||||
|         type: 'string', | ||||
|         helpArg: 'DESC', | ||||
|         help: 'Description of the VLAN.' | ||||
|     }, | ||||
|     { | ||||
|         group: 'Other options' | ||||
|     }, | ||||
|     { | ||||
|         names: ['json', 'j'], | ||||
|         type: 'bool', | ||||
|         help: 'JSON stream output.' | ||||
|     } | ||||
| ]; | ||||
| 
 | ||||
| @ -117,7 +134,7 @@ do_create.help = [ | ||||
| ].join('\n'); | ||||
| 
 | ||||
| do_create.helpOpts = { | ||||
|     helpCol: 25 | ||||
|     helpCol: 16 | ||||
| }; | ||||
| 
 | ||||
| module.exports = do_create; | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
|  */ | ||||
| 
 | ||||
| /* | ||||
|  * Copyright (c) 2018, Joyent, Inc. | ||||
|  * Copyright 2019 Joyent, Inc. | ||||
|  */ | ||||
| 
 | ||||
| /* BEGIN JSSTYLED */ | ||||
| @ -693,6 +693,10 @@ TritonApi.prototype.listImages = function listImages(opts, cb) { | ||||
|  * | ||||
|  * If there is more than one image with that name, then the latest | ||||
|  * (by published_at) is returned. | ||||
|  * | ||||
|  * @param {Boolean} opts.excludeInactive - Exclude inactive images when | ||||
|  *      matching. By default inactive images are included. This param is *not* | ||||
|  *      used when a full image ID (a UUID) is given. | ||||
|  */ | ||||
| TritonApi.prototype.getImage = function getImage(opts, cb) { | ||||
|     var self = this; | ||||
| @ -700,10 +704,13 @@ TritonApi.prototype.getImage = function getImage(opts, cb) { | ||||
|         opts = {name: opts}; | ||||
|     assert.object(opts, 'opts'); | ||||
|     assert.string(opts.name, 'opts.name'); | ||||
|     assert.optionalBool(opts.excludeInactive, 'opts.excludeInactive'); | ||||
|     assert.optionalBool(opts.useCache, 'opts.useCache'); | ||||
|     assert.func(cb, 'cb'); | ||||
| 
 | ||||
|     var excludeInactive = Boolean(opts.excludeInactive); | ||||
|     var img; | ||||
| 
 | ||||
|     if (common.isUUID(opts.name)) { | ||||
|         vasync.pipeline({funcs: [ | ||||
|             function tryCache(_, next) { | ||||
| @ -755,10 +762,8 @@ TritonApi.prototype.getImage = function getImage(opts, cb) { | ||||
|         var version = s[1]; | ||||
|         var nameSelector; | ||||
| 
 | ||||
|         var listOpts = { | ||||
|             // Explicitly include inactive images.
 | ||||
|             state: 'all' | ||||
|         }; | ||||
|         var listOpts = {}; | ||||
|         listOpts.state = (excludeInactive ? 'active' : 'all'); | ||||
|         if (version) { | ||||
|             nameSelector = name + '@' + version; | ||||
|             listOpts.name = name; | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| { | ||||
|   "name": "spearhead", | ||||
|   "description": "Spearhead Cloud CLI and client (https://spearhead.cloud)", | ||||
|   "version": "6.1.4", | ||||
|   "version": "7.0.0", | ||||
|   "author": "Spearhead Systems (spearhead.systems)", | ||||
|   "homepage": "https://code.spearhead.cloud/Spearhead/node-spearhead", | ||||
|   "dependencies": { | ||||
|  | ||||
		Odkázat v novém úkolu
	
	Zablokovat Uživatele