def import_bundle(request, deployer): """Start or schedule a bundle deployment. If the request is valid, the response will contain the DeploymentId assigned to the bundle deployment. Request: 'Import'. Parameters example: { 'Name': 'bundle-name', 'YAML': 'bundles', 'Version': 4, 'BundleID': '~user/bundle-name', }. """ # Validate the request parameters. try: name, bundle, version, id_ = _validate_import_params(request.params) except ValueError as err: raise response(error='invalid request: {}'.format(err)) # Validate and prepare the bundle. try: prepare_bundle(bundle) except ValueError as err: error = 'invalid request: invalid bundle {}: {}'.format(name, err) raise response(error=error) # Validate the bundle against the current state of the Juju environment. err = yield deployer.validate(request.user, bundle) if err is not None: raise response(error='invalid request: {}'.format(err)) # Add the bundle deployment to the Deployer queue. logging.info('import_bundle: scheduling deployment of v{} bundle {!r}' ''.format(version, name)) deployment_id = deployer.import_bundle(request.user, name, bundle, version, id_) raise response({'DeploymentId': deployment_id})
def import_bundle(request, deployer): """Start or schedule a bundle deployment. If the request is valid, the response will contain the DeploymentId assigned to the bundle deployment. Request: 'Import'. Parameters example: {'Name': 'bundle-name', 'YAML': 'bundles'}. """ # Validate the request parameters. try: name, bundle, bundle_id = _validate_import_params(request.params) except ValueError as err: raise response(error='invalid request: {}'.format(err)) # Validate and prepare the bundle. try: prepare_bundle(bundle) except ValueError as err: error = 'invalid request: invalid bundle {}: {}'.format(name, err) raise response(error=error) # Validate the bundle against the current state of the Juju environment. err = yield deployer.validate(request.user, name, bundle) if err is not None: raise response(error='invalid request: {}'.format(err)) # Add the bundle deployment to the Deployer queue. logging.info('import_bundle: scheduling {!r} deployment'.format(name)) deployment_id = deployer.import_bundle( request.user, name, bundle, bundle_id) raise response({'DeploymentId': deployment_id})
def status(request, deployer): """Return the current status of all the bundle deployments. The 'Status' request does not receive parameters. """ if request.params: params = ', '.join(request.params) error = 'invalid request: invalid data parameters: {}'.format(params) raise response(error=error) last_changes = deployer.status() logging.info('status: returning last changes') raise response({'LastChanges': last_changes})
def cancel(request, deployer): """Cancel the given pending deployment. The deployment is identified in the request by the DeploymentId parameter. If the request is not valid or the deployment cannot be cancelled (e.g. because it is already started) an error response is returned. Request: 'Cancel'. Parameters example: {'DeploymentId': 42}. """ deployment_id = request.params.get('DeploymentId') if deployment_id is None: raise response(error='invalid request: invalid data parameters') # Use the Deployer instance to cancel the deployment. err = deployer.cancel(deployment_id) if err is not None: raise response(error='invalid request: {}'.format(err)) logging.info('cancel: deployment {} cancelled'.format(deployment_id)) raise response()
def watch(request, deployer): """Handle requests for watching a given deployment. The deployment is identified in the request by the DeploymentId parameter. If the request is valid, the response will contain the WatcherId to be used to observe the deployment progress. Request: 'Watch'. Parameters example: {'DeploymentId': 42}. """ deployment_id = request.params.get('DeploymentId') if deployment_id is None: raise response(error='invalid request: invalid data parameters') # Retrieve a watcher identifier from the Deployer. watcher_id = deployer.watch(deployment_id) if watcher_id is None: raise response(error='invalid request: deployment not found') logging.info('watch: deployment {} being observed by watcher {}'.format( deployment_id, watcher_id)) raise response({'WatcherId': watcher_id})
def next(request, deployer): """Wait until a new deployment event is available to be sent to the client. The request params must include a WatcherId value, used to identify the deployment being observed. If unsent changes are available, a response is immediately returned containing the changes. Otherwise, this views suspends its execution until a new change is notified by the Deployer. Request: 'Next'. Parameters example: {'WatcherId': 47}. """ watcher_id = request.params.get('WatcherId') if watcher_id is None: raise response(error='invalid request: invalid data parameters') # Wait for the Deployer to send changes. logging.info('next: requested changes for watcher {}'.format(watcher_id)) changes = yield deployer.next(watcher_id) if changes is None: raise response(error='invalid request: invalid watcher identifier') logging.info('next: returning changes for watcher {}:\n{}'.format( watcher_id, changes)) raise response({'Changes': changes})
def set_changes(request): """Store a change set for the provided bundle YAML content. Return a unique identifier that can be used to retrieve the change set later. The token expires in two minutes and can be only used once. Request: 'SetChanges'. Parameters example: { 'YAML': 'content ...', }. """ content = request.params.get('YAML') if content is None: error = 'invalid request: bundle YAML not found' raise response(error=error) changes, errors = _validate_and_parse_bundle(content) if errors: raise response({'Errors': errors}) # Create and store the bundle token. token = uuid.uuid4().hex def expire_token(): _bundle_changesets.pop(token, None) logging.info('set change set: expired token {}'.format(token)) io_loop = IOLoop.current() handle = io_loop.add_timeout(_bundle_max_life, expire_token) now = datetime.datetime.utcnow() _bundle_changesets[token] = { 'changes': changes, 'handle': handle, } raise response({ 'Token': token, 'Created': now.isoformat() + 'Z', 'Expires': (now + _bundle_max_life).isoformat() + 'Z' })
def get_changes(request): """Return a list of changes required to deploy a bundle. The bundle can be specified by either passing its YAML content or its unique identifier previously stored with a SetChanges request (see below). Request: 'GetChanges'. Parameters example: { 'YAML': 'content ...', }. Parameters example: { 'Token': 'unique-id', }. """ params = request.params if len(params) != 1: error = 'invalid request: too many data parameters: {}'.format( ', '.join(sorted(params.keys()))) raise response(error=error) token = params.get('Token') if token is not None: # Retrieve the change set using the provided token. data = _bundle_changesets.pop(token, None) if data is None: error = 'unknown, fulfilled, or expired bundle token' raise response(error=error) logging.info('get change set: using token {}'.format(token)) io_loop = IOLoop.current() io_loop.remove_timeout(data['handle']) raise response({'Changes': data['changes']}) # Retrieve the change set using the provided bundle content. content = params.get('YAML') if content is None: error = 'invalid request: expected YAML or Token to be provided' raise response(error=error) changes, errors = _validate_and_parse_bundle(content) if errors: raise response({'Errors': errors}) raise response({'Changes': changes})
def test_empty(self): # An empty response is correctly generated. expected = {'Response': {}} response = utils.response() self.assert_response(expected, response)
def test_log_failure(self): # An error log is written when a failure response is generated. with ExpectLog('', 'deployer: an error occurred', required=True): utils.response(error='an error occurred')
def test_failure(self): # A failure response is correctly generated. expected = {'Error': 'an error occurred', 'Response': {}} response = utils.response(error='an error occurred') self.assert_response(expected, response)
def test_success(self): # A success response is correctly generated. expected = {'Response': {'foo': 'bar'}} response = utils.response({'foo': 'bar'}) self.assert_response(expected, response)
def myview(request, deployer): """An example testing view.""" self.assertEqual(self.deployer, deployer) raise utils.response(info='ok')
def myview(request, argument): """An example testing view.""" self.assertEqual(self.argument, argument) raise utils.response(info='ok')