123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- /**
- * Copyright 2012-2013 Amazon.com, Inc. or its affiliates. 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. A copy of
- * the License is located at
- *
- * http://aws.amazon.com/apache2.0/
- *
- * or in the "license" file accompanying this file. This file is
- * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
- * ANY KIND, either express or implied. See the License for the specific
- * language governing permissions and limitations under the License.
- */
- var AWS = require('./core');
- var inherit = AWS.util.inherit;
- var jmespath = require('jmespath');
- /**
- * @api private
- */
- function CHECK_ACCEPTORS(resp) {
- var waiter = resp.request._waiter;
- var acceptors = waiter.config.acceptors;
- var acceptorMatched = false;
- var state = 'retry';
- acceptors.forEach(function(acceptor) {
- if (!acceptorMatched) {
- var matcher = waiter.matchers[acceptor.matcher];
- if (matcher && matcher(resp, acceptor.expected, acceptor.argument)) {
- acceptorMatched = true;
- state = acceptor.state;
- }
- }
- });
- if (!acceptorMatched && resp.error) state = 'failure';
- if (state === 'success') {
- waiter.setSuccess(resp);
- } else {
- waiter.setError(resp, state === 'retry');
- }
- }
- /**
- * @api private
- */
- AWS.ResourceWaiter = inherit({
- /**
- * Waits for a given state on a service object
- * @param service [Service] the service object to wait on
- * @param state [String] the state (defined in waiter configuration) to wait
- * for.
- * @example Create a waiter for running EC2 instances
- * var ec2 = new AWS.EC2;
- * var waiter = new AWS.ResourceWaiter(ec2, 'instanceRunning');
- */
- constructor: function constructor(service, state) {
- this.service = service;
- this.state = state;
- this.loadWaiterConfig(this.state);
- },
- service: null,
- state: null,
- config: null,
- matchers: {
- path: function(resp, expected, argument) {
- try {
- var result = jmespath.search(resp.data, argument);
- } catch (err) {
- return false;
- }
- return jmespath.strictDeepEqual(result,expected);
- },
- pathAll: function(resp, expected, argument) {
- try {
- var results = jmespath.search(resp.data, argument);
- } catch (err) {
- return false;
- }
- if (!Array.isArray(results)) results = [results];
- var numResults = results.length;
- if (!numResults) return false;
- for (var ind = 0 ; ind < numResults; ind++) {
- if (!jmespath.strictDeepEqual(results[ind], expected)) {
- return false;
- }
- }
- return true;
- },
- pathAny: function(resp, expected, argument) {
- try {
- var results = jmespath.search(resp.data, argument);
- } catch (err) {
- return false;
- }
- if (!Array.isArray(results)) results = [results];
- var numResults = results.length;
- for (var ind = 0 ; ind < numResults; ind++) {
- if (jmespath.strictDeepEqual(results[ind], expected)) {
- return true;
- }
- }
- return false;
- },
- status: function(resp, expected) {
- var statusCode = resp.httpResponse.statusCode;
- return (typeof statusCode === 'number') && (statusCode === expected);
- },
- error: function(resp, expected) {
- if (typeof expected === 'string' && resp.error) {
- return expected === resp.error.code;
- }
- // if expected is not string, can be boolean indicating presence of error
- return expected === !!resp.error;
- }
- },
- listeners: new AWS.SequentialExecutor().addNamedListeners(function(add) {
- add('RETRY_CHECK', 'retry', function(resp) {
- var waiter = resp.request._waiter;
- if (resp.error && resp.error.code === 'ResourceNotReady') {
- resp.error.retryDelay = (waiter.config.delay || 0) * 1000;
- }
- });
- add('CHECK_OUTPUT', 'extractData', CHECK_ACCEPTORS);
- add('CHECK_ERROR', 'extractError', CHECK_ACCEPTORS);
- }),
- /**
- * @return [AWS.Request]
- */
- wait: function wait(params, callback) {
- if (typeof params === 'function') {
- callback = params; params = undefined;
- }
- if (params && params.$waiter) {
- params = AWS.util.copy(params);
- if (typeof params.$waiter.delay === 'number') {
- this.config.delay = params.$waiter.delay;
- }
- if (typeof params.$waiter.maxAttempts === 'number') {
- this.config.maxAttempts = params.$waiter.maxAttempts;
- }
- delete params.$waiter;
- }
- var request = this.service.makeRequest(this.config.operation, params);
- request._waiter = this;
- request.response.maxRetries = this.config.maxAttempts;
- request.addListeners(this.listeners);
- if (callback) request.send(callback);
- return request;
- },
- setSuccess: function setSuccess(resp) {
- resp.error = null;
- resp.data = resp.data || {};
- resp.request.removeAllListeners('extractData');
- },
- setError: function setError(resp, retryable) {
- resp.data = null;
- resp.error = AWS.util.error(resp.error || new Error(), {
- code: 'ResourceNotReady',
- message: 'Resource is not in the state ' + this.state,
- retryable: retryable
- });
- },
- /**
- * Loads waiter configuration from API configuration
- *
- * @api private
- */
- loadWaiterConfig: function loadWaiterConfig(state) {
- if (!this.service.api.waiters[state]) {
- throw new AWS.util.error(new Error(), {
- code: 'StateNotFoundError',
- message: 'State ' + state + ' not found.'
- });
- }
- this.config = AWS.util.copy(this.service.api.waiters[state]);
- }
- });
|