Bootaction cleanup

- Clean up the docs and code around bootactions to support the
  baclient work

- Update the Jinja2 environment in the bootaction rendering
- Refactor authentication of bootaction signal API calls

Change-Id: Ic64f0c7ee09a487be750188953013f1ed3cd99cb
This commit is contained in:
Scott Hussey 2018-08-29 08:09:46 -05:00
parent 0f39a55942
commit 9a52dca199
9 changed files with 58 additions and 14 deletions

View File

@ -112,6 +112,9 @@ object in details can be extended with additional fields as needed.
Once a POST containing the ``status`` field is made to a bootaction-id, that bootaction-id can no Once a POST containing the ``status`` field is made to a bootaction-id, that bootaction-id can no
longer be updated with status changes nor additional detailed status messages. longer be updated with status changes nor additional detailed status messages.
Each request made must contain the ``X-Bootaction-Key`` header with the correct hex
key for ``bootaction-id``.
validatedesign API validatedesign API
------------------ ------------------

View File

@ -111,9 +111,11 @@ template
- node.network.[network_name].cidr - CIDR of [network_name] - node.network.[network_name].cidr - CIDR of [network_name]
- node.network.[network_name].dns_suffix - DNS suffix of [network_name] - node.network.[network_name].dns_suffix - DNS suffix of [network_name]
- node.hostname - Hostname of the node - node.hostname - Hostname of the node
- node.domain - DNS Domain of the primary network on the node
- node.tags - Sequence of tags assigned to this node - node.tags - Sequence of tags assigned to this node
- node.labels - Key, value pairs of both explicit and dynamic labels for this node - node.labels - Key, value pairs of both explicit and dynamic labels for this node
- action.key - A key that uniquely identifies this boot action on this node. Can be used for signaling boot action result. - action.action_id - A ULID that uniquely identifies this boot action on this node. Can be used for signaling boot action result.
- action.action_key - A random key in hex that authenticates API calls for signaling boot action result.
- action.report_url - The URL that can be POSTed to for reporting boot action result. - action.report_url - The URL that can be POSTed to for reporting boot action result.
- action.design_ref - The design reference for the deployment that initiated the bootaction - action.design_ref - The design reference for the deployment that initiated the bootaction

View File

@ -43,6 +43,8 @@ RUN pip3 install \
--no-cache-dir \ --no-cache-dir \
-r /tmp/drydock/requirements-lock.txt -r /tmp/drydock/requirements-lock.txt
COPY ./alembic /tmp/drydock/alembic
COPY ./alembic.ini /tmp/drydock/alembic.ini
COPY ./python /tmp/drydock/python COPY ./python /tmp/drydock/python
COPY ${BUILD_DIR}/baclient /tmp/drydock/python/drydock_provisioner/assets/baclient COPY ${BUILD_DIR}/baclient /tmp/drydock/python/drydock_provisioner/assets/baclient

View File

@ -176,11 +176,13 @@ class BootactionAssetsResource(StatefulResource):
if hostname in ba.target_nodes: if hostname in ba.target_nodes:
ba_status = ba_status_list.get(ba.name, None) ba_status = ba_status_list.get(ba.name, None)
action_id = ba_status.get('action_id') action_id = ba_status.get('action_id')
action_key = ba_status.get('identity_key')
assets.extend( assets.extend(
ba.render_assets( ba.render_assets(
hostname, hostname,
site_design, site_design,
action_id, action_id,
action_key,
task.design_ref, task.design_ref,
type_filter=asset_type_filter)) type_filter=asset_type_filter))

View File

@ -64,6 +64,7 @@ class BootAction(base.DrydockPersistentObject, base.DrydockObject):
nodename, nodename,
site_design, site_design,
action_id, action_id,
action_key,
design_ref, design_ref,
type_filter=None): type_filter=None):
"""Render all of the assets in this bootaction. """Render all of the assets in this bootaction.
@ -77,6 +78,8 @@ class BootAction(base.DrydockPersistentObject, base.DrydockObject):
:param site_design: a objects.SiteDesign instance holding the design sets :param site_design: a objects.SiteDesign instance holding the design sets
:param action_id: a 128-bit ULID action_id of the boot action :param action_id: a 128-bit ULID action_id of the boot action
the assets are part of the assets are part of
:param action_key: a 256-bit random key to authenticate API calls
for updating the status of this bootaction
:param design_ref: the design ref this boot action was initiated under :param design_ref: the design ref this boot action was initiated under
:param type_filter: optional filter of the types of assets to render :param type_filter: optional filter of the types of assets to render
""" """
@ -84,7 +87,7 @@ class BootAction(base.DrydockPersistentObject, base.DrydockObject):
for a in self.asset_list: for a in self.asset_list:
if type_filter is None or (type_filter is not None if type_filter is None or (type_filter is not None
and a.type == type_filter): and a.type == type_filter):
a.render(nodename, site_design, action_id, design_ref) a.render(nodename, site_design, action_id, action_key, design_ref)
assets.append(a) assets.append(a)
return assets return assets
@ -140,7 +143,7 @@ class BootActionAsset(base.DrydockObject):
super().__init__(package_list=package_list, permissions=mode, **kwargs) super().__init__(package_list=package_list, permissions=mode, **kwargs)
self.rendered_bytes = None self.rendered_bytes = None
def render(self, nodename, site_design, action_id, design_ref): def render(self, nodename, site_design, action_id, action_key, design_ref):
"""Render this asset into a base64 encoded string. """Render this asset into a base64 encoded string.
The ``nodename`` and ``action_id`` will be used to construct The ``nodename`` and ``action_id`` will be used to construct
@ -149,10 +152,11 @@ class BootActionAsset(base.DrydockObject):
:param nodename: the name of the node where the asset will be deployed :param nodename: the name of the node where the asset will be deployed
:param site_design: instance of objects.SiteDesign :param site_design: instance of objects.SiteDesign
:param action_id: a 128-bit ULID boot action id :param action_id: a 128-bit ULID boot action id
:param action_key: a 256-bit random key for API auth
:param design_ref: The design ref this bootaction was initiated under :param design_ref: The design ref this bootaction was initiated under
""" """
tpl_ctx = self._get_template_context(nodename, site_design, action_id, tpl_ctx = self._get_template_context(nodename, site_design, action_id,
design_ref) action_key, design_ref)
if self.location is not None: if self.location is not None:
rendered_location = self.execute_pipeline( rendered_location = self.execute_pipeline(
@ -207,27 +211,30 @@ class BootActionAsset(base.DrydockObject):
return package_list return package_list
def _get_template_context(self, nodename, site_design, action_id, def _get_template_context(self, nodename, site_design, action_id,
design_ref): action_key, design_ref):
"""Create a context to be used for template rendering. """Create a context to be used for template rendering.
:param nodename: The name of the node for the bootaction :param nodename: The name of the node for the bootaction
:param site_design: The full site design :param site_design: The full site design
:param action_id: the ULID assigned to the boot action using this context :param action_id: the ULID assigned to the boot action using this context
:param action_key: a 256 bit random key for API auth
:param design_ref: The design reference representing ``site_design`` :param design_ref: The design reference representing ``site_design``
""" """
return dict( return dict(
node=self._get_node_context(nodename, site_design), node=self._get_node_context(nodename, site_design),
action=self._get_action_context(action_id, design_ref)) action=self._get_action_context(action_id, action_key, design_ref))
def _get_action_context(self, action_id, design_ref): def _get_action_context(self, action_id, action_key, design_ref):
"""Create the action-specific context items for template rendering. """Create the action-specific context items for template rendering.
:param action_id: ULID of this boot action :param action_id: ULID of this boot action
:param action_key: random key of this boot action
:param design_ref: Design reference representing the site design :param design_ref: Design reference representing the site design
""" """
return dict( return dict(
key=ulid2.ulid_to_base32(action_id), action_id=ulid2.ulid_to_base32(action_id),
action_key=action_key.hex(),
report_url=config.config_mgr.conf.bootactions.report_url, report_url=config.config_mgr.conf.bootactions.report_url,
design_ref=design_ref) design_ref=design_ref)

View File

@ -22,4 +22,4 @@ docker run --rm --net host postgres:9.5 psql -h localhost -c "create database dr
export DRYDOCK_DB_URL="postgresql+psycopg2://drydock:drydock@localhost:5432/drydock" export DRYDOCK_DB_URL="postgresql+psycopg2://drydock:drydock@localhost:5432/drydock"
sudo docker run --rm -t --net=host -e DRYDOCK_DB_URL="$DRYDOCK_DB_URL" --entrypoint /usr/local/bin/alembic $IMAGE upgrade head sudo docker run --rm -t --net=host -e DRYDOCK_DB_URL="$DRYDOCK_DB_URL" -w /tmp/drydock --entrypoint /usr/local/bin/alembic $IMAGE upgrade head

View File

@ -14,6 +14,7 @@
"""Test that boot action assets are rendered correctly.""" """Test that boot action assets are rendered correctly."""
import ulid2 import ulid2
import os
from drydock_provisioner.statemgmt.state import DrydockState from drydock_provisioner.statemgmt.state import DrydockState
@ -32,8 +33,9 @@ class TestBootactionRenderAction(object):
ba = design_data.get_bootaction('helloworld') ba = design_data.get_bootaction('helloworld')
action_id = ulid2.generate_binary_ulid() action_id = ulid2.generate_binary_ulid()
action_key = os.urandom(32)
assets = ba.render_assets('compute01', design_data, action_id, assets = ba.render_assets('compute01', design_data, action_id,
design_ref) action_key, design_ref)
assert 'compute01' in assets[0].rendered_bytes.decode('utf-8') assert 'compute01' in assets[0].rendered_bytes.decode('utf-8')
@ -50,12 +52,34 @@ class TestBootactionRenderAction(object):
ba = design_data.get_bootaction('helloworld') ba = design_data.get_bootaction('helloworld')
action_id = ulid2.generate_binary_ulid() action_id = ulid2.generate_binary_ulid()
action_key = os.urandom(32)
assets = ba.render_assets('compute01', design_data, action_id, assets = ba.render_assets('compute01', design_data, action_id,
design_ref) action_key, design_ref)
assert 'deckhand_fullsite.yaml' in assets[2].rendered_bytes.decode( assert 'deckhand_fullsite.yaml' in assets[2].rendered_bytes.decode(
'utf-8') 'utf-8')
def test_bootaction_render_key(self, input_files, deckhand_ingester,
setup):
"""Test that a bootaction can render the correct action_id and
action_key needed by the signalling API."""
input_file = input_files.join("deckhand_fullsite.yaml")
design_state = DrydockState()
design_ref = "file://%s" % str(input_file)
design_status, design_data = deckhand_ingester.ingest_data(
design_state=design_state, design_ref=design_ref)
ba = design_data.get_bootaction('helloworld')
action_id = ulid2.generate_binary_ulid()
action_key = os.urandom(32)
assets = ba.render_assets('compute01', design_data, action_id,
action_key, design_ref)
assert action_key.hex() in assets[2].rendered_bytes.decode('utf-8')
assert ulid2.ulid_to_base32(action_id) in assets[2].rendered_bytes.decode('utf-8')
def test_bootaction_network_context(self, input_files, def test_bootaction_network_context(self, input_files,
deckhand_orchestrator, setup): deckhand_orchestrator, setup):
"""Test that a boot action creates proper network context.""" """Test that a boot action creates proper network context."""

View File

@ -16,6 +16,7 @@
import ulid2 import ulid2
import tarfile import tarfile
import io import io
import os
import drydock_provisioner.objects as objects import drydock_provisioner.objects as objects
from drydock_provisioner.statemgmt.state import DrydockState from drydock_provisioner.statemgmt.state import DrydockState
@ -39,8 +40,9 @@ class TestClass(object):
ba = design_data.get_bootaction('helloworld') ba = design_data.get_bootaction('helloworld')
action_id = ulid2.generate_binary_ulid() action_id = ulid2.generate_binary_ulid()
action_key = os.urandom(32)
assets = ba.render_assets(target_host, design_data, action_id, assets = ba.render_assets(target_host, design_data, action_id,
design_ref) action_key, design_ref)
assert len(assets) > 0 assert len(assets) > 0

View File

@ -541,9 +541,11 @@ data:
- path: /var/tmp/designref.sh - path: /var/tmp/designref.sh
type: file type: file
permissions: '500' permissions: '500'
data: e3sgYWN0aW9uLmRlc2lnbl9yZWYgfX0K data: |
{{ action.design_ref }}
{{ action.action_id }}
{{ action.action_key }}
data_pipeline: data_pipeline:
- base64_decode
- utf8_decode - utf8_decode
- template - template
--- ---