From 18e073d9a48aecd3c09caf2de8d610b1d13279c4 Mon Sep 17 00:00:00 2001 From: Stephen Sawchuk Date: Tue, 29 Mar 2016 09:42:35 -0400 Subject: [PATCH] compute: support Forwarding Rules --- lib/compute/index.js | 184 ++++++++++++++++++++++++++ lib/compute/region.js | 183 +++++++++++++++++++++++++- lib/compute/rule.js | 240 ++++++++++++++++++++++++++++++++++ system-test/compute.js | 285 +++++++++++++++++++++++++++++++++-------- test/compute/index.js | 1 + test/compute/region.js | 2 +- test/compute/rule.js | 69 ++++++++++ 7 files changed, 907 insertions(+), 57 deletions(-) create mode 100644 lib/compute/rule.js create mode 100644 test/compute/rule.js diff --git a/lib/compute/index.js b/lib/compute/index.js index 0272121c5286..8bbfa7f80524 100644 --- a/lib/compute/index.js +++ b/lib/compute/index.js @@ -49,6 +49,12 @@ var Operation = require('./operation.js'); */ var Region = require('./region.js'); +/** + * @type {module:compute/rule} + * @private + */ +var Rule = require('./rule.js'); + /** * @type {module:common/service} * @private @@ -311,6 +317,67 @@ Compute.prototype.createNetwork = function(name, config, callback) { }); }; +/** + * Create a global forwarding rule. + * + * @resource [GlobalForwardingRule Resource]{@link https://cloud.google.com/compute/docs/reference/v1/globalForwardingRules#resource} + * @resource [GlobalForwardingRules: insert API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/globalForwardingRules/insert} + * + * @param {string} name - Name of the rule. + * @param {object} config - See a + * [GlobalForwardingRule resource](https://cloud.google.com/compute/docs/reference/v1/globalForwardingRules#resource). + * @param {string} config.target - The full or valid partial URL of the target + * resource to receive the matched traffic. This target must be a global + * `TargetHttpProxy` or `TargetHttpsProxy` resource. + * @param {function} callback - The callback function. + * @param {?error} callback.err - An error returned while making this request. + * @param {module:compute/rule} callback.rule - The created Rule object. + * @param {module:compute/operation} callback.operation - An operation object + * that can be used to check the status of the request. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * var name = 'new-rule-name'; + * + * var config = { + * target: '...', + * portRange: '8080', + * IPProtocol: 'TCP' + * }; + * + * gce.createRule(name, config, function (err, rule, operation, apiResponse) { + * // `rule` is a Rule object. + * + * // `operation` is an Operation object that can be used to check the status + * // of the request. + * }); + */ +Compute.prototype.createRule = function(name, config, callback) { + var self = this; + + var body = extend({}, config, { + name: name + }); + + this.request({ + method: 'POST', + uri: '/global/forwardingRules', + json: body + }, function(err, resp) { + if (err) { + callback(err, null, null, resp); + return; + } + + var rule = self.rule(name); + + var operation = self.operation(resp.name); + operation.metadata = resp; + + callback(null, rule, operation, resp); + }); +}; + /** * Create a backend service. * @@ -1159,6 +1226,109 @@ Compute.prototype.getRegions = function(options, callback) { }); }; +/** + * Get a list of forwading rules. + * + * @resource [GlobalForwardingRules: list API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/globalForwardingRules/list} + * + * @param {object=} options - Rules search options. + * @param {boolean} options.autoPaginate - Have pagination handled + * automatically. Default: true. + * @param {string} options.filter - Search filter in the format of + * `{name} {comparison} {filterString}`. + * - **`name`**: the name of the field to compare + * - **`comparison`**: the comparison operator, `eq` (equal) or `ne` + * (not equal) + * - **`filterString`**: the string to filter to. For string fields, this + * can be a regular expression. + * @param {number} options.maxResults - Maximum number of rules to return. + * @param {string} options.pageToken - A previously-returned page token + * representing part of the larger set of results to view. + * @param {function} callback - The callback function. + * @param {?error} callback.err - An error returned while making this request. + * @param {module:compute/rule} callback.rules - Rule objects from this region. + * @param {?object} callback.nextQuery - If present, query with this object to + * check for more results. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * gce.getRules(function(err, rules) { + * // `rules` is an array of `Rule` objects. + * }); + * + * //- + * // To control how many API requests are made and page through the results + * // manually, set `autoPaginate` to `false`. + * //- + * function callback(err, rules, nextQuery, apiResponse) { + * if (nextQuery) { + * // More results exist. + * gce.getRules(nextQuery, callback); + * } + * } + * + * gce.getRules({ + * autoPaginate: false + * }, callback); + * + * //- + * // Get the rules from your project as a readable object stream. + * //- + * gce.getRules() + * .on('error', console.error) + * .on('data', function(rule) { + * // `rule` is a `Rule` object. + * }) + * .on('end', function() { + * // All rules retrieved. + * }); + * + * //- + * // If you anticipate many results, you can end a stream early to prevent + * // unnecessary processing and API requests. + * //- + * gce.getRules() + * .on('data', function(rule) { + * this.end(); + * }); + */ +Compute.prototype.getRules = function(options, callback) { + var self = this; + + if (is.fn(options)) { + callback = options; + options = {}; + } + + options = options || {}; + + this.request({ + uri: '/global/forwardingRules', + qs: options + }, function(err, resp) { + if (err) { + callback(err, null, null, resp); + return; + } + + var nextQuery = null; + + if (resp.nextPageToken) { + nextQuery = extend({}, options, { + pageToken: resp.nextPageToken + }); + } + + var rules = (resp.items || []).map(function(rule) { + var ruleInstance = self.rule(rule.name); + ruleInstance.metadata = rule; + return ruleInstance; + }); + + callback(null, rules, nextQuery, resp); + }); +}; + /** * Get a list of backend services. * @@ -1630,6 +1800,19 @@ Compute.prototype.region = function(name) { return new Region(this, name); }; +/** + * Get a reference to a Google Compute Engine forwading rule. + * + * @param {string} name - Name of the rule. + * @return {module:compute/rule} + * + * @example + * var rule = gce.rule('rule-name'); + */ +Compute.prototype.rule = function(name) { + return new Rule(this, name); +}; + /** * Get a reference to a Google Compute Engine backend service. * @@ -1688,6 +1871,7 @@ streamRouter.extend(Compute, [ 'getNetworks', 'getOperations', 'getRegions', + 'getRules', 'getServices', 'getSnapshots', 'getVMs', diff --git a/lib/compute/region.js b/lib/compute/region.js index fb6b90d83195..0dc8d840f6c5 100644 --- a/lib/compute/region.js +++ b/lib/compute/region.js @@ -36,6 +36,12 @@ var Address = require('./address.js'); */ var Operation = require('./operation.js'); +/** + * @type {module:compute/rule} + * @private + */ +var Rule = require('./rule.js'); + /** * @type {module:common/serviceObject} * @private @@ -199,6 +205,65 @@ Region.prototype.createAddress = function(name, options, callback) { }); }; +/** + * Create a forwarding rule in this region. + * + * @resource [ForwardingRule Resource]{@link https://cloud.google.com/compute/docs/reference/latest/forwardingRules#resource} + * @resource [ForwardingRules: insert API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/forwardingRules/insert} + * + * @param {string} name - Name of the rule. + * @param {object} config - See a + * [ForwardingRule resource](https://cloud.google.com/compute/docs/reference/latest/forwardingRules#resource). + * @param {string} config.target - The full or valid partial URL of the target + * resource to receive the matched traffic. This target must live in this + * region. + * @param {function} callback - The callback function. + * @param {?error} callback.err - An error returned while making this request. + * @param {module:compute/rule} callback.rule - The created Rule object. + * @param {module:compute/operation} callback.operation - An operation object + * that can be used to check the status of the request. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * var name = 'new-rule-name'; + * + * var cfg = { + * target: '...', + * portRange: '8080', + * IPProtocol: 'TCP' + * }; + * + * region.createRule(name, cfg, function (err, rule, operation, apiResponse) { + * // `rule` is a Rule object. + * + * // `operation` is an Operation object that can be used to check the status + * // of the request. + * }); + */ +Region.prototype.createRule = function(name, config, callback) { + var self = this; + + this.request({ + method: 'POST', + uri: '/forwardingRules', + json: extend({}, config, { + name: name + }) + }, function(err, resp) { + if (err) { + callback(err, null, null, resp); + return; + } + + var rule = self.rule(name); + + var operation = self.operation(resp.name); + operation.metadata = resp; + + callback(null, rule, operation, resp); + }); +}; + /** * Get a list of addresses in this region. * @@ -409,6 +474,109 @@ Region.prototype.getOperations = function(options, callback) { }); }; +/** + * Get a list of forwading rules in this region. + * + * @resource [ForwardingRules: list API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/forwardingRules/list} + * + * @param {object=} options - Rules search options. + * @param {boolean} options.autoPaginate - Have pagination handled + * automatically. Default: true. + * @param {string} options.filter - Search filter in the format of + * `{name} {comparison} {filterString}`. + * - **`name`**: the name of the field to compare + * - **`comparison`**: the comparison operator, `eq` (equal) or `ne` + * (not equal) + * - **`filterString`**: the string to filter to. For string fields, this + * can be a regular expression. + * @param {number} options.maxResults - Maximum number of rules to return. + * @param {string} options.pageToken - A previously-returned page token + * representing part of the larger set of results to view. + * @param {function} callback - The callback function. + * @param {?error} callback.err - An error returned while making this request. + * @param {module:compute/rule} callback.rules - Rule objects from this region. + * @param {?object} callback.nextQuery - If present, query with this object to + * check for more results. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * region.getRules(function(err, rules) { + * // `rules` is an array of `Rule` objects. + * }); + * + * //- + * // To control how many API requests are made and page through the results + * // manually, set `autoPaginate` to `false`. + * //- + * function callback(err, rules, nextQuery, apiResponse) { + * if (nextQuery) { + * // More results exist. + * region.getRules(nextQuery, callback); + * } + * } + * + * region.getRules({ + * autoPaginate: false + * }, callback); + * + * //- + * // Get the rules from this region as a readable object stream. + * //- + * region.getRules() + * .on('error', console.error) + * .on('data', function(rule) { + * // `rule` is a `Rule` object. + * }) + * .on('end', function() { + * // All rules retrieved. + * }); + * + * //- + * // If you anticipate many results, you can end a stream early to prevent + * // unnecessary processing and API requests. + * //- + * region.getRules() + * .on('data', function(rule) { + * this.end(); + * }); + */ +Region.prototype.getRules = function(options, callback) { + var self = this; + + if (is.fn(options)) { + callback = options; + options = {}; + } + + options = options || {}; + + this.request({ + uri: '/forwardingRules', + qs: options + }, function(err, resp) { + if (err) { + callback(err, null, null, resp); + return; + } + + var nextQuery = null; + + if (resp.nextPageToken) { + nextQuery = extend({}, options, { + pageToken: resp.nextPageToken + }); + } + + var rules = (resp.items || []).map(function(rule) { + var ruleInstance = self.rule(rule.name); + ruleInstance.metadata = rule; + return ruleInstance; + }); + + callback(null, rules, nextQuery, resp); + }); +}; + /** * Get a reference to a Google Compute Engine region operation. * @@ -424,11 +592,24 @@ Region.prototype.operation = function(name) { return new Operation(this, name); }; +/** + * Get a reference to a Google Compute Engine forwarding rule in this region. + * + * @param {string} name - Name of the rule. + * @return {module:compute/rule} + * + * @example + * var rule = region.rule('rule-name'); + */ +Region.prototype.rule = function(name) { + return new Rule(this, name); +}; + /*! Developer Documentation * * These methods can be used with either a callback or as a readable object * stream. `streamRouter` is used to add this dual behavior. */ -streamRouter.extend(Region, ['getAddresses', 'getOperations']); +streamRouter.extend(Region, ['getAddresses', 'getOperations', 'getRules']); module.exports = Region; diff --git a/lib/compute/rule.js b/lib/compute/rule.js new file mode 100644 index 000000000000..fc33ad1d0056 --- /dev/null +++ b/lib/compute/rule.js @@ -0,0 +1,240 @@ +/*! + * Copyright 2015 Google 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. + */ + +/*! + * @module compute/rule + */ + +'use strict'; + +var nodeutil = require('util'); + +/** + * @type {module:common/serviceObject} + * @private + */ +var ServiceObject = require('../common/service-object.js'); + +/** + * @type {module:common/util} + * @private + */ +var util = require('../common/util.js'); + +/*! Developer Documentation + * + * @param {module:compute|module:compute/region} scope - The parent scope this + * firewall rule belongs to. + * @param {string} name - Rule name. +/** + * Forwarding rules are... + * + * @resource [Title]{@link https://...} + * + * @constructor + * @alias module:compute/rule + * + * @example + * var gcloud = require('gcloud')({ + * keyFilename: '/path/to/keyfile.json', + * projectId: 'grape-spaceship-123' + * }); + * + * var gce = gcloud.compute(); + * + * //- + * // Reference a global rule. + * //- + * var rule = gce.rule('rule-name'); + * + * //- + * // Reference a region rule. + * //- + * var region = gce.region('us-central1'); + * var rule = region.rule('rule-name'); + */ +function Rule(scope, name) { + var isGlobalRule = scope.constructor.name === 'Compute'; + + var methods = { + /** + * Create a forwarding rule. + * + * @param {object} config - See {module:compute#createRule} or + * {module:compute/region#createRule} if accessing this object through + * a Region. + * + * @example + * var config = { + * target: '...', + * portRange: '8080', + * IPProtocol: 'TCP' + * }; + * + * rule.create(config, function(err, rule, operation, apiResponse) { + * // `rule` is a Rule object. + * + * // `operation` is an Operation object that can be used to check the + * // of the request. + * }); + */ + create: true, + + /** + * Check if the forwarding rule exists. + * + * @param {function} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {boolean} callback.exists - Whether the rule exists or not. + * + * @example + * rule.exists(function(err, exists) {}); + */ + exists: true, + + /** + * Get a forwarding rule if it exists. + * + * You may optionally use this to "get or create" an object by providing an + * object with `autoCreate` set to `true`. Any extra configuration that is + * normally required for the `create` method must be contained within this + * object as well. + * + * @param {options=} options - Configuration object. + * @param {boolean} options.autoCreate - Automatically create the object if + * it does not exist. Default: `false` + * + * @example + * rule.get(function(err, rule, apiResponse) { + * // `rule` is a Rule object. + * }); + */ + get: true, + + /** + * Get the metadata of this rule. + * + * @resource [GlobalForwardingRule Resource]{@link https://cloud.google.com/compute/docs/reference/v1/globalForwardingRules#resource} + * @resource [ForwardingRule Resource]{@link https://cloud.google.com/compute/docs/reference/v1/globalForwardingRules#resource} + * @resource [GlobalForwardingRules: get API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/globalForwardingRules/get} + * @resource [ForwardingRules: get API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/forwardingRules/get} + * + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {object} callback.metadata - The rule's metadata. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * rule.getMetadata(function(err, metadata, apiResponse) {}); + */ + getMetadata: true + }; + + ServiceObject.call(this, { + parent: scope, + baseUrl: (isGlobalRule ? '/global' : '') + '/forwardingRules', + id: name, + createMethod: scope.createRule.bind(scope), + methods: methods + }); + + this.scope = scope; +} + +nodeutil.inherits(Rule, ServiceObject); + +/** + * Delete the rule. + * + * @resource [GlobalForwardingRules: delete API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/globalForwardingRules/delete} + * @resource [ForwardingRules: delete API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/forwardingRules/delete} + * + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this request. + * @param {module:compute/operation} callback.operation - An operation object + * that can be used to check the status of the request. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * rule.delete(function(err, operation, apiResponse) { + * // `operation` is an Operation object that can be used to check the status + * // of the request. + * }); + */ +Rule.prototype.delete = function(callback) { + callback = callback || util.noop; + + var scope = this.scope; + + ServiceObject.prototype.delete.call(this, function(err, resp) { + if (err) { + callback(err, null, resp); + return; + } + + var operation = scope.operation(resp.name); + operation.metadata = resp; + + callback(null, operation, resp); + }); +}; + +/** + * Set the target for this forwarding rule. + * + * @resource [GlobalForwardingRules: setTarget API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/globalForwardingRules/setTarget} + * @resource [ForwardingRules: setTarget API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/forwardingRules/setTarget} + * + * @param {string} target - The full or valid partial URL of the target resource + * to receive the matched traffic. For regional forwarding rules, this + * target must live in the same region as the forwarding rule. For global + * forwarding rules, this target must be a global `TargetHttpProxy` + * resource. + * @param {function} callback - The callback function. + * @param {?error} callback.err - An error returned while making this request. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * rule.setTarget('new-target', function(err, operation, apiResponse) { + * // `operation` is an Operation object that can be used to check the status + * // of the request. + * }); + */ +Rule.prototype.setTarget = function(target, callback) { + var scope = this.scope; + + this.request({ + method: 'POST', + uri: '/setTarget', + json: { + target: target + } + }, function(err, resp) { + if (err) { + callback(err, null, resp); + return; + } + + var operation = scope.operation(resp.name); + operation.metadata = resp; + + callback(null, operation, resp); + }); +}; + +module.exports = Rule; diff --git a/system-test/compute.js b/system-test/compute.js index 1be59763f561..e791837cb48f 100644 --- a/system-test/compute.js +++ b/system-test/compute.js @@ -55,7 +55,7 @@ describe('Compute', function() { }); describe('addresses', function() { - var ADDRESS_NAME = generateName(); + var ADDRESS_NAME = generateName('address'); var address = region.address(ADDRESS_NAME); before(function(done) { @@ -106,13 +106,13 @@ describe('Compute', function() { }); describe('autoscalers', function() { - var AUTOSCALER_NAME = generateName(); + var AUTOSCALER_NAME = generateName('autoscaler'); var autoscaler = zone.autoscaler(AUTOSCALER_NAME); // Some of the services we support require an instance group to be created. // Util `instanceGroups` are officially supported by gcloud-node, we make // manual requests to create and delete them. - var INSTANCE_GROUP_NAME = generateName(); + var INSTANCE_GROUP_NAME = generateName('instance-group'); before(function(done) { async.series([ @@ -199,7 +199,7 @@ describe('Compute', function() { }); describe('disks', function() { - var DISK_NAME = generateName(); + var DISK_NAME = generateName('disk'); var disk = zone.disk(DISK_NAME); before(function(done) { @@ -253,20 +253,21 @@ describe('Compute', function() { }); it('should take a snapshot', function(done) { - disk.snapshot(generateName()).create(function(err, snapshot, operation) { - assert.ifError(err); + disk.snapshot(generateName('snapshot')) + .create(function(err, snapshot, operation) { + assert.ifError(err); - operation - .on('error', done) - .on('complete', function() { - done(); - }); - }); + operation + .on('error', done) + .on('complete', function() { + done(); + }); + }); }); }); describe('firewalls', function() { - var FIREWALL_NAME = generateName(); + var FIREWALL_NAME = generateName('firewall'); var firewall = compute.firewall(FIREWALL_NAME); var CONFIG = { @@ -337,7 +338,7 @@ describe('Compute', function() { }); describe('networks', function() { - var NETWORK_NAME = generateName(); + var NETWORK_NAME = generateName('network'); var network = compute.network(NETWORK_NAME); var CONFIG = { @@ -479,53 +480,51 @@ describe('Compute', function() { }); }); - describe('services', function() { - var service = compute.service(generateName()); + describe('rules', function() { + var RULE_NAME = generateName('rule'); + var globalRule = compute.rule(RULE_NAME); - var INSTANCE_GROUP_NAME = generateName(); - var groupUrl; + var service = compute.service(generateName('service')); - var HEALTH_CHECK_NAME = generateName(); - var healthCheckUrl; + var INSTANCE_GROUP_NAME = generateName('instance-group'); + var HEALTH_CHECK_NAME = generateName('health-check'); + + // To create a rule, we need to also create a TargetHttpProxy and UrlMap. + // Until they are officially supported by gcloud-node, we make manual + // requests to create and delete them. + var TARGET_PROXY_NAME = generateName('target-proxy'); + var URL_MAP_NAME = generateName('url-map'); before(function(done) { async.series([ function(callback) { - createInstanceGroup(INSTANCE_GROUP_NAME, function(err, metadata) { - if (err) { - callback(err); - return; - } - - groupUrl = metadata.selfLink; - - callback(); - }); + createService( + service.name, + INSTANCE_GROUP_NAME, + HEALTH_CHECK_NAME, + callback + ); }, function(callback) { - createHttpHealthCheck(HEALTH_CHECK_NAME, function(err, metadata) { - if (err) { - callback(err); - return; - } - - healthCheckUrl = metadata.selfLink; + createUrlMap({ + name: URL_MAP_NAME, + defaultService: 'global/backendServices/' + service.name + }, callback); + }, - callback(); - }); + function(callback) { + createTargetProxy({ + name: TARGET_PROXY_NAME, + urlMap: 'global/urlMaps/' + URL_MAP_NAME + }, callback); }, function(callback) { - service.create({ - backends: [ - { - group: groupUrl - } - ], - healthChecks: [ - healthCheckUrl - ] + globalRule.create({ + target: 'global/targetHttpProxies/' + TARGET_PROXY_NAME, + portRange: '8080', + IPProtocol: 'TCP' }, execAfterOperationComplete(callback)); } ], done); @@ -534,19 +533,51 @@ describe('Compute', function() { after(function(done) { async.series([ function(callback) { - service.delete(execAfterOperationComplete(callback)); + globalRule.delete(execAfterOperationComplete(callback)); }, function(callback) { - deleteHttpHealthCheck(HEALTH_CHECK_NAME, callback); + deleteTargetProxy(TARGET_PROXY_NAME, callback); }, function(callback) { - deleteInstanceGroup(INSTANCE_GROUP_NAME, callback); + deleteUrlMap(URL_MAP_NAME, callback); + }, + + function(callback) { + deleteService( + service.name, + INSTANCE_GROUP_NAME, + HEALTH_CHECK_NAME, + callback + ); } ], done); }); + it('should get a list of rules', function(done) { + compute.getRules(function(err, rules) { + assert.ifError(err); + assert(rules.length > 0); + done(); + }); + }); + }); + + describe('services', function() { + var service = compute.service(generateName('service')); + + var INSTANCE_GROUP_NAME = generateName('instance-group'); + var HEALTH_CHECK_NAME = generateName('health-check'); + + before(function(done) { + createService(service.name, INSTANCE_GROUP_NAME, HEALTH_CHECK_NAME, done); + }); + + after(function(done) { + deleteService(service.name, INSTANCE_GROUP_NAME, HEALTH_CHECK_NAME, done); + }); + it('should get a list of services', function(done) { compute.getServices(function(err, services) { assert.ifError(err); @@ -630,7 +661,7 @@ describe('Compute', function() { }); describe('vms', function() { - var VM_NAME = generateName(); + var VM_NAME = generateName('vm'); var vm = zone.vm(VM_NAME); before(function(done) { @@ -700,7 +731,7 @@ describe('Compute', function() { }); it('should attach and detach a disk', function(done) { - var name = generateName(); + var name = generateName('disk'); var disk = zone.disk(name); async.series([ @@ -863,8 +894,8 @@ describe('Compute', function() { }); }); - function generateName() { - return TESTS_PREFIX + Date.now(); + function generateName(customPrefix) { + return TESTS_PREFIX + customPrefix + '-' + Date.now(); } function deleteAllTestObjects(callback) { @@ -874,6 +905,7 @@ describe('Compute', function() { 'getDisks', 'getFirewalls', 'getNetworks', + 'getRules', 'getServices', 'getSnapshots', 'getVMs' @@ -910,6 +942,71 @@ describe('Compute', function() { }; } + function createService(name, instanceGroupName, healthCheckName, callback) { + var service = compute.service(name); + var groupUrl; + var healthCheckUrl; + + async.series([ + function(callback) { + createInstanceGroup(instanceGroupName, function(err, metadata) { + if (err) { + callback(err); + return; + } + + groupUrl = metadata.selfLink; + + callback(); + }); + }, + + function(callback) { + createHttpHealthCheck(healthCheckName, function(err, metadata) { + if (err) { + callback(err); + return; + } + + healthCheckUrl = metadata.selfLink; + + callback(); + }); + }, + + function(callback) { + service.create({ + backends: [ + { + group: groupUrl + } + ], + healthChecks: [ + healthCheckUrl + ] + }, execAfterOperationComplete(callback)); + } + ], callback); + } + + function deleteService(name, instanceGroupName, healthCheckName, callback) { + var service = compute.service(name); + + async.series([ + function(callback) { + service.delete(execAfterOperationComplete(callback)); + }, + + function(callback) { + deleteHttpHealthCheck(healthCheckName, callback); + }, + + function(callback) { + deleteInstanceGroup(instanceGroupName, callback); + } + ], callback); + } + function createInstanceGroup(name, callback) { zone.request({ method: 'POST', @@ -999,4 +1096,82 @@ describe('Compute', function() { }); }); } + + function createUrlMap(config, callback) { + compute.request({ + method: 'POST', + uri: '/global/urlMaps', + json: config + }, function(err, resp) { + if (err) { + callback(err); + return; + } + + var operation = compute.operation(resp.name); + operation + .on('error', callback) + .on('complete', function() { + callback(); + }); + }); + } + + function deleteUrlMap(name, callback) { + compute.request({ + method: 'DELETE', + uri: '/global/urlMaps/' + name + }, function(err, resp) { + if (err) { + callback(err); + return; + } + + var operation = compute.operation(resp.name); + operation + .on('error', callback) + .on('complete', function() { + callback(); + }); + }); + } + + function createTargetProxy(config, callback) { + compute.request({ + method: 'POST', + uri: '/global/targetHttpProxies', + json: config + }, function(err, resp) { + if (err) { + callback(err); + return; + } + + var operation = compute.operation(resp.name); + operation + .on('error', callback) + .on('complete', function() { + callback(); + }); + }); + } + + function deleteTargetProxy(name, callback) { + compute.request({ + method: 'DELETE', + uri: '/global/targetHttpProxies/' + name + }, function(err, resp) { + if (err) { + callback(err); + return; + } + + var operation = compute.operation(resp.name); + operation + .on('error', callback) + .on('complete', function() { + callback(); + }); + }); + } }); diff --git a/test/compute/index.js b/test/compute/index.js index fd11d8df8a99..9d95768f7f43 100644 --- a/test/compute/index.js +++ b/test/compute/index.js @@ -48,6 +48,7 @@ var fakeStreamRouter = { 'getNetworks', 'getOperations', 'getRegions', + 'getRules', 'getServices', 'getSnapshots', 'getVMs', diff --git a/test/compute/region.js b/test/compute/region.js index c1484b0feab7..99ad0933a932 100644 --- a/test/compute/region.js +++ b/test/compute/region.js @@ -49,7 +49,7 @@ var fakeStreamRouter = { extended = true; methods = arrify(methods); assert.equal(Class.name, 'Region'); - assert.deepEqual(methods, ['getAddresses', 'getOperations']); + assert.deepEqual(methods, ['getAddresses', 'getOperations', 'getRules']); } }; diff --git a/test/compute/rule.js b/test/compute/rule.js new file mode 100644 index 000000000000..b258f1fa5fab --- /dev/null +++ b/test/compute/rule.js @@ -0,0 +1,69 @@ +/** + * Copyright 2015 Google 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. + */ + +'use strict'; + +var assert = require('assert'); +var mockery = require('mockery-next'); + +var util = require('../../lib/common/util.js'); + +function FakeServiceObject() { + this.calledWith_ = arguments; +} + +describe('Rule', function() { + var Rule; + var rule; + + var COMPUTE = { + createRule: util.noop + }; + var RULE_NAME = 'rule-name'; + + before(function() { + mockery.registerMock( + '../../lib/common/service-object.js', + FakeServiceObject + ); + mockery.enable({ + useCleanCache: true, + warnOnUnregistered: false + }); + + Rule = require('../../lib/compute/rule.js'); + }); + + after(function() { + mockery.deregisterAll(); + mockery.disable(); + }); + + beforeEach(function() { + rule = new Rule(COMPUTE, RULE_NAME); + }); + + describe('instantiation', function() { + it('should inherit from ServiceObject', function() { + assert(rule instanceof FakeServiceObject); + }); + }); + + describe('aMethod', function() { + it('should ...', function() { + }); + }); +});