1 | /**
|
2 | * Copyright 2016-2018 F5 Networks, Inc.
|
3 | *
|
4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
5 | * you may not use this file except in compliance with the License.
|
6 | * You may obtain a copy of the License at
|
7 | *
|
8 | * http://www.apache.org/licenses/LICENSE-2.0
|
9 | *
|
10 | * Unless required by applicable law or agreed to in writing, software
|
11 | * distributed under the License is distributed on an "AS IS" BASIS,
|
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 | * See the License for the specific language governing permissions and
|
14 | * limitations under the License.
|
15 | */
|
16 |
|
17 | ;
|
18 |
|
19 | const q = require('q');
|
20 | const Logger = require('./logger');
|
21 | const BigIq = require('./bigIq');
|
22 |
|
23 | const MAX_STORED_INSTANCE_AGE = 60000 * 60 * 24; // 1 day
|
24 |
|
25 | /**
|
26 | * Constructor.
|
27 | * @class
|
28 | * @classdesc
|
29 | * Abstract cloud provider implementation.
|
30 | *
|
31 | * This class should be inherited from to implement cloud-provider
|
32 | * specific implementations. Any method in this class that throws
|
33 | * must be overridded. Methods that do not throw may be optionally
|
34 | * overridden.
|
35 | *
|
36 | * @param {Ojbect} [options] - Options for the instance.
|
37 | * @param {Object} [options.clOptions] - Command line options if called from a script.
|
38 | * @param {Object} [options.logger] - Logger to use. Or, pass loggerOptions to get your own logger.
|
39 | * @param {Object} [options.loggerOptions] - Options for the logger.
|
40 | * See {@link module:logger.getLogger} for details.
|
41 | */
|
42 | function CloudProvider(options) {
|
43 | const logger = options ? options.logger : undefined;
|
44 | let loggerOptions = options ? options.loggerOptions : undefined;
|
45 |
|
46 | this.options = {};
|
47 | if (options) {
|
48 | Object.keys(options).forEach((option) => {
|
49 | this.options[option] = options[option];
|
50 | });
|
51 | }
|
52 |
|
53 | this.clOptions = {};
|
54 | if (options && options.clOptions) {
|
55 | Object.keys(options.clOptions).forEach((option) => {
|
56 | this.clOptions[option] = options.clOptions[option];
|
57 | });
|
58 | }
|
59 |
|
60 | // Holder for supported features. If an implementation supports an features,
|
61 | // set them to true in this map
|
62 | this.features = {};
|
63 |
|
64 | if (logger) {
|
65 | this.logger = logger;
|
66 | } else {
|
67 | loggerOptions = loggerOptions || { logLevel: 'none' };
|
68 | loggerOptions.module = module;
|
69 | this.logger = Logger.getLogger(loggerOptions);
|
70 | this.loggerOptions = loggerOptions;
|
71 | }
|
72 | }
|
73 |
|
74 | // Public constants...
|
75 | // optional features that a provider can support...
|
76 |
|
77 | // ability to message other instances in the scale set
|
78 | CloudProvider.FEATURE_MESSAGING = 'FEATURE_MESSAGING';
|
79 | // supports stroing/retrieving public/private keys
|
80 | CloudProvider.FEATURE_ENCRYPTION = 'FEATURE_ENCRYPTION';
|
81 | // password is the same for all members of a cluster
|
82 | CloudProvider.FEATURE_SHARED_PASSWORD = 'FEATURE_SHARED_PASSWORD';
|
83 |
|
84 | // messages that can be sent if FEATURE_MESSAGING is supported...
|
85 |
|
86 | // add an instance to the cluster
|
87 | CloudProvider.MESSAGE_ADD_TO_CLUSTER = 'ADD_TO_CLUSTER';
|
88 | // a sync has been completed, you may need to update your password
|
89 | CloudProvider.MESSAGE_SYNC_COMPLETE = 'SYNC_COMPLETE';
|
90 |
|
91 | // For use in getPrimaryStatus...
|
92 | CloudProvider.STATUS_OK = 'OK';
|
93 | CloudProvider.STATUS_NOT_EXTERNAL = 'NOT_EXTERNAL';
|
94 | CloudProvider.STATUS_NOT_IN_CLOUD_LIST = 'NOT_IN_CLOUD_LIST';
|
95 | CloudProvider.STATUS_VERSION_NOT_UP_TO_DATE = 'VERSION_NOT_UP_TO_DATE';
|
96 | CloudProvider.STATUS_UNKNOWN = 'UNKNOWN';
|
97 |
|
98 | /**
|
99 | * Initialize class
|
100 | *
|
101 | * Override for implementation specific initialization needs (read info
|
102 | * from cloud provider, read database, etc.). Called at the start of
|
103 | * processing.
|
104 | *
|
105 | * @param {Object} providerOptions - Provider specific options.
|
106 | * @param {Object} [options] - Options for this instance.
|
107 | * @param {Boolean} [options.autoscale] - Whether or not this instance will be used for autoscaling.
|
108 | *
|
109 | * @returns {Promise} A promise which will be resolved when init is complete.
|
110 | */
|
111 | CloudProvider.prototype.init = function init(providerOptions, options) {
|
112 | this.logger.debug('No override for CloudProvider.init', providerOptions, options);
|
113 | return q();
|
114 | };
|
115 |
|
116 | /**
|
117 | * BIG-IP is now ready and providers can run BIG-IP functions
|
118 | * if necessary
|
119 | *
|
120 | * @returns {Promise} A promise which will be resolved when init is complete.
|
121 | */
|
122 | CloudProvider.prototype.bigIpReady = function bigIpReady() {
|
123 | this.logger.debug('No override for CloudProvider.bigIpReady');
|
124 | return q();
|
125 | };
|
126 |
|
127 | /**
|
128 | * Gets data from a provider specific URI
|
129 | *
|
130 | * Override for implementations that wish to allow retrieval of data from a
|
131 | * provider specific URI (for example, an ARN to an S3 bucket).
|
132 | *
|
133 | * @abstract
|
134 | *
|
135 | * @param {String} uri - The cloud-specific URI of the resource.
|
136 | *
|
137 | * @returns {Promise} A promise which will be resolved with the data from the URI
|
138 | * or rejected if an error occurs.
|
139 | */
|
140 | CloudProvider.prototype.getDataFromUri = function getDataFromUri(uri) {
|
141 | throw new Error('Unimplemented abstract method CloudProvider.getDataFromUri', uri);
|
142 | };
|
143 |
|
144 | /**
|
145 | * Gets the instance ID of this instance
|
146 | *
|
147 | * @abstract
|
148 | *
|
149 | * @returns {Promise} A promise which will be resolved with the instance ID of this instance
|
150 | * or rejected if an error occurs;
|
151 | */
|
152 | CloudProvider.prototype.getInstanceId = function getInstanceId() {
|
153 | throw new Error('Unimplemented abstract method CloudProvider.getInstanceId');
|
154 | };
|
155 |
|
156 | /**
|
157 | * Gets info for each instance
|
158 | *
|
159 | * Retrieval is cloud specific. Likely either from the cloud infrastructure
|
160 | * itself, stored info that we have in a database, or both.
|
161 | *
|
162 | * @abstract
|
163 | *
|
164 | * @param {Object} [options] - Optional parameters
|
165 | * @param {String} [options.externalTag] - Also look for instances with this
|
166 | * tag (outside of the autoscale group/set)
|
167 | *
|
168 | * @returns {Promise} A promise which will be resolved with a dictionary of instances
|
169 | * keyed by instance ID. Each instance value should be:
|
170 | *
|
171 | * {
|
172 | * isPrimary: <Boolean>,
|
173 | * hostname: <String>,
|
174 | * mgmtIp: <String>,
|
175 | * privateIp: <String>,
|
176 | * publicIp: <String>,
|
177 | * providerVisible: <Boolean> (does the cloud provider know about this instance),
|
178 | * external: <Boolean> (true if this instance is external to the autoscale group/set)
|
179 | * }
|
180 | */
|
181 | CloudProvider.prototype.getInstances = function getInstances(options) {
|
182 | throw new Error('Unimplemented abstract method CloudProvider.getInstances', options);
|
183 | };
|
184 |
|
185 |
|
186 | /**
|
187 | * Called to delete a stored UCS file based on filename
|
188 | *
|
189 | * @param {String} UCS filename
|
190 | *
|
191 | * @returns {Promise} returns a promise which resolves with status of delete operation
|
192 | * or gets rejected in a case of failures
|
193 | *
|
194 | */
|
195 | CloudProvider.prototype.deleteStoredUcs = function deleteStoredUcs() {
|
196 | this.logger.debug('No override for method CloudProvider.deleteStoredUcs');
|
197 | return q.resolve();
|
198 | };
|
199 |
|
200 | /**
|
201 | * Searches for NICs that have a given tag.
|
202 | *
|
203 | * @param {Object} tag - Tag to search for. Tag is of the format:
|
204 | *
|
205 | * {
|
206 | * key: optional key
|
207 | * value: value to search for
|
208 | * }
|
209 | *
|
210 | * @returns {Promise} A promise which will be resolved with an array of instances.
|
211 | * Each instance value should be:
|
212 | *
|
213 | * {
|
214 | * id: NIC ID,
|
215 | * ip: {
|
216 | * public: public IP (or first public IP on the NIC),
|
217 | * private: private IP (or first private IP on the NIC)
|
218 | * }
|
219 | * }
|
220 | */
|
221 | CloudProvider.prototype.getNicsByTag = function getNicsByTag(tag) {
|
222 | this.logger.debug('No override for CloudProvider.getNicsByTag', tag);
|
223 | return q();
|
224 | };
|
225 |
|
226 | /**
|
227 | * Searches for VMs that have a given tag.
|
228 | *
|
229 | * @param {Object} tag - Tag to search for. Tag is of the format:
|
230 | *
|
231 | * {
|
232 | * key: optional key
|
233 | * value: value to search for
|
234 | * }
|
235 | *
|
236 | * @returns {Promise} A promise which will be resolved with an array of instances.
|
237 | * Each instance value should be:
|
238 | *
|
239 | * {
|
240 | * id: instance ID,
|
241 | * ip: {
|
242 | * public: public IP (or first public IP on the first NIC),
|
243 | * private: private IP (or first private IP on the first NIC)
|
244 | * }
|
245 | * }
|
246 | */
|
247 | CloudProvider.prototype.getVmsByTag = function getVmsByTag(tag) {
|
248 | this.logger.debug('No override for CloudProvider.getVmsByTag', tag);
|
249 | return q();
|
250 | };
|
251 |
|
252 | /**
|
253 | * Elects a new primary instance from the available instances
|
254 | *
|
255 | * @abstract
|
256 | *
|
257 | * @param {Object} instances - Dictionary of instances as returned by getInstances.
|
258 | *
|
259 | * @returns {Promise} A promise which will be resolved with the instance ID of the
|
260 | * elected primary.
|
261 | */
|
262 | CloudProvider.prototype.electPrimary = function electPrimary(instances) {
|
263 | throw new Error('Unimplemented abstract method CloudProvider.electPrimary', instances);
|
264 | };
|
265 |
|
266 | /**
|
267 | * Called to retrieve primary instance credentials
|
268 | *
|
269 | * Must be implemented if FEATURE_MESSAGING is not supported.
|
270 | *
|
271 | * If FEATURE_MESSAGING is not supported, when joining a cluster we need the
|
272 | * username and password for the primary instance.
|
273 | *
|
274 | * If FEATURE_MESSAGING is supported, the primary will be sent a message to
|
275 | * add an instance.
|
276 | *
|
277 | * Management IP and port are passed in so that credentials can be
|
278 | * validated desired.
|
279 | *
|
280 | * @abstract
|
281 | *
|
282 | * @param {String} mgmtIp - Management IP of primary.
|
283 | * @param {String} port - Management port of primary.
|
284 | *
|
285 | * @returns {Promise} A promise which will be resolved with:
|
286 | *
|
287 | * {
|
288 | * username: <admin_user>,
|
289 | * password: <admin_password>
|
290 | * }
|
291 | */
|
292 | CloudProvider.prototype.getPrimaryCredentials = function getPrimaryCredentials(mgmtIp, mgmtPort) {
|
293 | if (!this.hasFeature(CloudProvider.FEATURE_MESSAGING)) {
|
294 | throw new Error(
|
295 | 'Unimplemented abstract method CloudProvider.getPrimaryCredentials',
|
296 | mgmtIp,
|
297 | mgmtPort
|
298 | );
|
299 | } else {
|
300 | this.logger.debug('No override for CloudProvider.getPrimaryCredentials');
|
301 | return q(true);
|
302 | }
|
303 | };
|
304 |
|
305 | /**
|
306 | * Called to store primary credentials
|
307 | *
|
308 | * When joining a cluster we need the username and password for the
|
309 | * primary instance. This method is called to tell us that we are
|
310 | * the primary and we should store our credentials if we need to store
|
311 | * them for later retrieval in getPrimaryCredentials.
|
312 | *
|
313 | * @returns {Promise} A promise which will be resolved when the operation
|
314 | * is complete
|
315 | */
|
316 | CloudProvider.prototype.putPrimaryCredentials = function putPrimaryCredentials() {
|
317 | this.logger.debug('No override for CloudProvider.putPrimaryCredentials');
|
318 | return q();
|
319 | };
|
320 |
|
321 | /**
|
322 | * Gets info on what this instance thinks the primary status is
|
323 | *
|
324 | * Info is retrieval is cloud specific. Likely either from the cloud infrastructure
|
325 | * itself, stored info that we have in a database, or both.
|
326 | *
|
327 | * @returns {Promise} A promise which will be resolved with a dictionary of primary
|
328 | * status:
|
329 | *
|
330 | * {
|
331 | * "instanceId": primaryInstanceId
|
332 | * "status": CloudProvider.STATUS_*
|
333 | * "lastUpdate": Date,
|
334 | * "lastStatusChange": Date
|
335 | * }
|
336 | *
|
337 | */
|
338 | CloudProvider.prototype.getPrimaryStatus = function getPrimaryStatus() {
|
339 | this.logger.debug('No override for CloudProvider.getPrimaryStatus');
|
340 | return q();
|
341 | };
|
342 |
|
343 | /**
|
344 | * Gets the public key for an instanceId.
|
345 | *
|
346 | * @param {String} instanceId - Instance ID to validate as a valid primary.
|
347 | *
|
348 | * @returns {Promise} A promise which will be resolved when the operation
|
349 | * is complete
|
350 | */
|
351 | CloudProvider.prototype.getPublicKey = function getPublicKey(instanceId) {
|
352 | if (this.hasFeature(CloudProvider.FEATURE_ENCRYPTION)) {
|
353 | throw new Error('Unimplemented abstract method CloudProvider.getPublicKey', instanceId);
|
354 | } else {
|
355 | this.logger.debug('No override for CloudProvider.getPublicKey');
|
356 | return q(true);
|
357 | }
|
358 | };
|
359 |
|
360 | /**
|
361 | * Determines if the provider supports a feature.
|
362 | *
|
363 | * @param {String} feature - Feature to check for
|
364 | *
|
365 | * @returns {Boolean} Whether or not the provider supports the feature
|
366 | */
|
367 | CloudProvider.prototype.hasFeature = function hasFeature(feature) {
|
368 | return !!this.features[feature];
|
369 | };
|
370 |
|
371 | /**
|
372 | * Stores the public key for an instanceId.
|
373 | *
|
374 | * The public key should later be able to retrieved given the instanceId.
|
375 | * Must be implemented if provider supports FEATURE_ENCRYPTION.
|
376 | *
|
377 | * @param {String} instanceId - Instance ID to validate as a valid primary.
|
378 | * @param {String} publicKey - The public key
|
379 | *
|
380 | * @returns {Promise} A promise which will be resolved when the operation
|
381 | * is complete
|
382 | */
|
383 | CloudProvider.prototype.putPublicKey = function putPublicKey(instanceId, publicKey) {
|
384 | if (this.hasFeature(CloudProvider.FEATURE_ENCRYPTION)) {
|
385 | throw new Error(
|
386 | 'Unimplemented abstract method CloudProvider.putPublicKey',
|
387 | instanceId,
|
388 | publicKey
|
389 | );
|
390 | } else {
|
391 | this.logger.debug('No override for CloudProvider.putPublicKey');
|
392 | return q(true);
|
393 | }
|
394 | };
|
395 |
|
396 | /**
|
397 | * Determines if a given instanceId is a valid primary
|
398 | *
|
399 | * In some cloud environments, the primary may change unexpectedly.
|
400 | * Override this method if implementing such a cloud provider.
|
401 | *
|
402 | * @param {String} instanceId - Instance ID to validate as a valid primary.
|
403 | * @param {Object} instances - Dictionary of instances as returned by getInstances.
|
404 | *
|
405 | * @returns {Promise} A promise which will be resolved with a boolean indicating
|
406 | * wether or not the given instanceId is a valid primary.
|
407 | */
|
408 | CloudProvider.prototype.isValidPrimary = function isValidPrimary(instanceId, instances) {
|
409 | this.logger.debug('No override for CloudProvider.isValidPrimary', instanceId, instances);
|
410 | return q(true);
|
411 | };
|
412 |
|
413 | /**
|
414 | * Called when a primary has been elected
|
415 | *
|
416 | * In some cloud environments, information about the primary needs to be
|
417 | * stored in persistent storage. Override this method if implementing
|
418 | * such a cloud provider.
|
419 | *
|
420 | * @param {String} instancId - Instance ID that was elected primary.
|
421 | *
|
422 | * @returns {Promise} A promise which will be resolved when processing is complete.
|
423 | */
|
424 | CloudProvider.prototype.primaryElected = function primaryElected(instanceId) {
|
425 | this.logger.debug('No override for CloudProvider.primaryElected', instanceId);
|
426 | return q();
|
427 | };
|
428 |
|
429 | /**
|
430 | * Called after a primary has been elected.
|
431 | *
|
432 | * In some cloud environments, instances running a primary should be tagged through
|
433 | * cloud provider specific tagging. Override this method for cloud providers that support
|
434 | * instance tagging for instances running primarys.
|
435 | *
|
436 | * @param {String} instanceId - Instance ID that was elected primary.
|
437 | * @param {Object} instances - Dictionary of instances as returned by getInstances
|
438 | *
|
439 | * @returns {Promise} A promise which will be resolved when processing is complete.
|
440 | */
|
441 | CloudProvider.prototype.tagPrimaryInstance = function tagPrimaryInstance(instanceId, instances) {
|
442 | this.logger.debug('No override for CloudProvider.tagPrimaryInstance', instanceId, instances);
|
443 | return q();
|
444 | };
|
445 |
|
446 | /**
|
447 | * Indicates that an instance that was primary is now invalid
|
448 | *
|
449 | * Override for cloud providers that need to take some action when a primary
|
450 | * becomes invalid.
|
451 | *
|
452 | * @param {String} instanceId - Instance ID of instnace that is no longer a valid
|
453 | * primary.
|
454 | * @param {Object} instances - Dictionary of instances as returned by getInstances.
|
455 | *
|
456 | * @returns {Promise} A promise which will be resolved when processing is complete.
|
457 | */
|
458 | CloudProvider.prototype.primaryInvalidated = function primaryInvalidated(instanceId, instances) {
|
459 | this.logger.debug('No override for CloudProvider.primaryInvalidated', instanceId, instances);
|
460 | return q();
|
461 | };
|
462 |
|
463 | /**
|
464 | * Called to get check for and retrieve a stored UCS file
|
465 | *
|
466 | * Provider implementations can optionally store a UCS to be
|
467 | * used to restore a primary instance to a last known good state
|
468 | *
|
469 | * @returns {Promise} A promise which will be resolved with a Buffer containing
|
470 | * the UCS data if it is present, resolved with undefined if not
|
471 | * found, or rejected if an error occurs.
|
472 | */
|
473 | CloudProvider.prototype.getStoredUcs = function getStoredUcs() {
|
474 | this.logger.debug('No override for CloudProvider.putPrimaryCredentials');
|
475 | return q();
|
476 | };
|
477 |
|
478 | /**
|
479 | * Stores a UCS file in cloud storage
|
480 | *
|
481 | * @param {String} file - Full path to file to store.
|
482 | * @param {Number} maxCopies - Number of files to store. Oldest files over
|
483 | * this number should be deleted.
|
484 | * @param {String} prefix - The common prefix for autosaved UCS files
|
485 | *
|
486 | * @returns {Promise} A promise which is resolved when processing is complete.
|
487 | */
|
488 | CloudProvider.prototype.storeUcs = function storeUcs(file, maxCopies, prefix) {
|
489 | this.logger.debug('No override for CloudProvider.storeUcs', file, maxCopies, prefix);
|
490 | return q();
|
491 | };
|
492 |
|
493 | /**
|
494 | * Saves instance info
|
495 | *
|
496 | * Override for cloud implementations which store instance information.
|
497 | *
|
498 | * @param {String} instanceId - ID of instance
|
499 | * @param {Object} instance - Instance information as returned by getInstances.
|
500 | *
|
501 | * @returns {Promise} A promise which will be resolved with instance info.
|
502 | */
|
503 | CloudProvider.prototype.putInstance = function putInstance(instanceId, instance) {
|
504 | this.logger.debug('No override for CloudProvider.putInstance', instance);
|
505 | return q();
|
506 | };
|
507 |
|
508 | /**
|
509 | * Sends a message to other instances in the scale set
|
510 | *
|
511 | * Must be implemented if FEATURE_MESSAGING is supported
|
512 | *
|
513 | * @abstract
|
514 | *
|
515 | * @param {String} action - Action id of message to send
|
516 | * @param {Object} [options] - Optional parameters
|
517 | * @param {String} [options.toInstanceId] - Instance ID that message is for
|
518 | * @param {String} [options.fromInstanceId] - Instance ID that message is from
|
519 | * @param {Object} [options.data] - Message specific data
|
520 | *
|
521 | * @returns {Promise} A promise which will be resolved when the message
|
522 | * has been sent or rejected if an error occurs
|
523 | */
|
524 | CloudProvider.prototype.sendMessage = function sendMessage(action, data) {
|
525 | if (this.hasFeature(CloudProvider.FEATURE_MESSAGING)) {
|
526 | throw new Error('Unimplemented abstract method CloudProvider.sendMessage', action, data);
|
527 | } else {
|
528 | this.logger.debug('No override for CloudProvider.sendMessage');
|
529 | return q(true);
|
530 | }
|
531 | };
|
532 |
|
533 | /**
|
534 | * Revokes licenses for instances licensed from BIG-IQ
|
535 | *
|
536 | * We only make a best effort here. If revoke fails, this still succeeds. This allows
|
537 | * us not to care if the license even can be revoked (perhaps it is not issued by BIG-IQ).
|
538 | *
|
539 | * @param {Object[]} instances - Instances for which to revoke licenses. Instances
|
540 | * should be as returned by getInstances
|
541 | * @param {Object} [options] - Original command line options
|
542 | * @param {Object} [options.bigIp] - Base {@link BigIp} object.
|
543 | *
|
544 | * @returns {Promise} A promise which will be resolved when processing is complete.
|
545 | */
|
546 | CloudProvider.prototype.revokeLicenses = function revokeLicenses(instances, options) {
|
547 | const promises = [];
|
548 |
|
549 | if (instances.length > 0) {
|
550 | if (!this.clOptions.licensePool) {
|
551 | this.logger.debug('Can only revoke licenses retrieved from BIG-IQ. Ignoring.');
|
552 | return q.resolve();
|
553 | }
|
554 |
|
555 | // this.bigIq can be set for testing
|
556 | const bigIq = this.bigIq || new BigIq(this.options);
|
557 |
|
558 | return bigIq.init(
|
559 | this.clOptions.bigIqHost,
|
560 | this.clOptions.bigIqUser,
|
561 | this.clOptions.bigIqPassword || this.clOptions.bigIqPasswordUri,
|
562 | {
|
563 | passwordIsUri: typeof this.clOptions.bigIqPasswordUri !== 'undefined',
|
564 | passwordEncrypted: this.clOptions.bigIqPasswordEncrypted,
|
565 | bigIp: options.bigIp
|
566 | }
|
567 | )
|
568 | .then(() => {
|
569 | instances.forEach((instance) => {
|
570 | const noUnreachable = !(this.clOptions.unreachable);
|
571 | promises.push(bigIq.revokeLicense(
|
572 | this.clOptions.licensePoolName, instance, { noUnreachable }
|
573 | ));
|
574 | });
|
575 | return q.all(promises);
|
576 | })
|
577 | .catch((err) => {
|
578 | this.logger.debug('Could not revoke all licenses', err);
|
579 | return q.reject(err);
|
580 | });
|
581 | }
|
582 |
|
583 | this.logger.silly('No licenses to revoke');
|
584 | return q();
|
585 | };
|
586 |
|
587 | /**
|
588 | * Gets messages from other instances in the scale set
|
589 | *
|
590 | * @param {String[]} actions - Array of actions to get. Other messages will be ignored.
|
591 | * Default (empty or undefined) is all actions.
|
592 | * @param {Object} [options] - Optional parameters
|
593 | * @param {String} [options.toInstanceId] - toInstanceId of messsages we are interested in
|
594 | *
|
595 | * @returns {Promise} A promise which will be resolved when the messages
|
596 | * have been received and processed. Promise should be
|
597 | * resolved with an array of messages of the form
|
598 | *
|
599 | * {
|
600 | * action: message action id,
|
601 | * toInstanceId: instanceId,
|
602 | * fromInstanceId: instanceId,
|
603 | * data: message specific data used in sendMessage,
|
604 | * completionHandler: optional completionHandler to call wnen done processing
|
605 | * {
|
606 | * this: this arg for callback context,
|
607 | * callback: function to call,
|
608 | * data: data to send to function
|
609 | * }
|
610 | * }
|
611 | */
|
612 | CloudProvider.prototype.getMessages = function getMessages(actions, options) {
|
613 | if (this.hasFeature(CloudProvider.FEATURE_MESSAGING)) {
|
614 | throw new Error('Unimplemented abstract method CloudProvider.getMessages', actions, options);
|
615 | } else {
|
616 | this.logger.debug('No override for CloudProvider.getMessages');
|
617 | return q(true);
|
618 | }
|
619 | };
|
620 |
|
621 | /**
|
622 | * Informs the provider that a sync has completed in case the
|
623 | * password needs to be updated
|
624 | *
|
625 | * When a sync is complete, the user and password will exist on
|
626 | * the synced to device.
|
627 | *
|
628 | * @param {String} fromUser - User that was synced from
|
629 | * @param {String} fromPassword - Password that was synced from
|
630 | *
|
631 | * @returns {Promise} A promise which will be resolved when the messages
|
632 | * have been received and processed
|
633 | */
|
634 | // eslint-disable-next-line no-unused-vars
|
635 | CloudProvider.prototype.syncComplete = function syncComplete(fromUser, fromPassword) {
|
636 | this.logger.debug('No override for CloudProvider.syncComplete', fromUser);
|
637 | return q(true);
|
638 | };
|
639 |
|
640 | /**
|
641 | * Informs the provider that the instance has been provisioned
|
642 | *
|
643 | * @param {String} instanceId - Instance ID of instance to mark as provisioned. If not provided,
|
644 | * instanceId will be instanceId as set by init().
|
645 | *
|
646 | * @returns {Promise} A promise which will be resolved when the instance has been signalled to the
|
647 | * provider as provisioned
|
648 | */
|
649 | CloudProvider.prototype.signalInstanceProvisioned = function signalInstanceProvisioned(instanceId) {
|
650 | this.logger.debug('No override for CloudProvider.signalInstanceProvisioned', instanceId);
|
651 | return q(true);
|
652 | };
|
653 |
|
654 | /**
|
655 | * Determines whether a stored instance is so old it should not be considered
|
656 | *
|
657 | * @param {Object} instance - Instance data
|
658 | *
|
659 | * @returns {Boolean} Whether or not the instance is expired
|
660 | */
|
661 | CloudProvider.prototype.isInstanceExpired = function isInstanceExpired(instance) {
|
662 | let isExpired = false;
|
663 |
|
664 | const lastUpdate = instance.lastUpdate || new Date();
|
665 | const age = new Date() - new Date(lastUpdate);
|
666 |
|
667 | if (age > MAX_STORED_INSTANCE_AGE) {
|
668 | isExpired = true;
|
669 | }
|
670 |
|
671 | return isExpired;
|
672 | };
|
673 |
|
674 | /**
|
675 | * Gets nodes from the provided URI. The resource should be in JSON
|
676 | * format as an array of objects. JSON strings that parse to an array
|
677 | * of objects are also supported.
|
678 | *
|
679 | * @param {String} uri - The URI of the resource.
|
680 | * @param {Object} [options] - Optional parameters
|
681 | * @param {Object} [options.headers] - Map of headers to add to the request. Format:
|
682 | *
|
683 | * {
|
684 | * <header1_name>: <header1_value>,
|
685 | * <header2_name>: <header2_value>
|
686 | * }
|
687 | *
|
688 | * @returns {Promise} A promise which will be resolved with an array of instances.
|
689 | * Each instance value should be:
|
690 | *
|
691 | * {
|
692 | * id: Node ID,
|
693 | * ip: {
|
694 | * public: public IP,
|
695 | * private: private IP
|
696 | * }
|
697 | * }
|
698 | */
|
699 | CloudProvider.prototype.getNodesFromUri = function getNodesFromUri(uri, options) {
|
700 | this.logger.debug('No override for CloudProvider.getNodesFromUri', uri, options);
|
701 | return q();
|
702 | };
|
703 |
|
704 | /**
|
705 | * Gets nodes by a resourceId. The resourceId is a string and its meaning is
|
706 | * provider specific. The meaning is interpreted by the provider by setting a resourceType,
|
707 | * which is also provider specific.
|
708 | *
|
709 | * @param {String} resourceId - The ID of the resource.
|
710 | * @param {Object} resourceType - The type of resource. Provider specific.
|
711 | * @param {Object} [options] - Optional parameters
|
712 | *
|
713 | * @returns {Promise} A promise which will be resolved with an array of instances.
|
714 | * Each instance value should be:
|
715 | *
|
716 | * {
|
717 | * id: Node ID,
|
718 | * ip: {
|
719 | * public: public IP,
|
720 | * private: private IP
|
721 | * }
|
722 | * }
|
723 | */
|
724 | CloudProvider.prototype.getNodesByResourceId = function getNodesFromUri(resourceId, resourceType, options) {
|
725 | this.logger.debug('No override for CloudProvider.getNodesFromUri', resourceId, resourceType, options);
|
726 | return q();
|
727 | };
|
728 |
|
729 | module.exports = CloudProvider;
|