Пример #1
0
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})
Пример #2
0
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})
Пример #3
0
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})
Пример #4
0
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})
Пример #5
0
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()
Пример #6
0
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()
Пример #7
0
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})
Пример #8
0
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})
Пример #9
0
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})
Пример #10
0
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})
Пример #11
0
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'
    })
Пример #12
0
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'
    })
Пример #13
0
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})
Пример #14
0
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})
Пример #15
0
 def test_empty(self):
     # An empty response is correctly generated.
     expected = {'Response': {}}
     response = utils.response()
     self.assert_response(expected, response)
Пример #16
0
 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')
Пример #17
0
 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)
Пример #18
0
 def test_success(self):
     # A success response is correctly generated.
     expected = {'Response': {'foo': 'bar'}}
     response = utils.response({'foo': 'bar'})
     self.assert_response(expected, response)
Пример #19
0
 def test_empty(self):
     # An empty response is correctly generated.
     expected = {'Response': {}}
     response = utils.response()
     self.assert_response(expected, response)
Пример #20
0
 def myview(request, deployer):
     """An example testing view."""
     self.assertEqual(self.deployer, deployer)
     raise utils.response(info='ok')
Пример #21
0
 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')
Пример #22
0
 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)
Пример #23
0
 def myview(request, argument):
     """An example testing view."""
     self.assertEqual(self.argument, argument)
     raise utils.response(info='ok')
Пример #24
0
 def test_success(self):
     # A success response is correctly generated.
     expected = {'Response': {'foo': 'bar'}}
     response = utils.response({'foo': 'bar'})
     self.assert_response(expected, response)
Пример #25
0
 def myview(request, argument):
     """An example testing view."""
     self.assertEqual(self.argument, argument)
     raise utils.response(info='ok')