Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Added
#4757
* Add ``user`` parameter to ``re_run`` method of st2client. #4785
* Install pack dependencies automatically. #4769
* Add support for `immutable_parameters` on Action Aliases. This feature allows default parameters to be supplied to the action on every execution of the alias. #4786

Changed
~~~~~~~
Expand Down
6 changes: 6 additions & 0 deletions st2api/st2api/controllers/v1/aliasexecution.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from st2common.models.db.notification import NotificationSchema, NotificationSubSchema
from st2common.models.utils import action_param_utils
from st2common.models.utils.action_alias_utils import extract_parameters_for_action_alias_db
from st2common.models.utils.action_alias_utils import inject_immutable_parameters
from st2common.persistence.actionalias import ActionAlias
from st2common.services import action as action_service
from st2common.util import action_db as action_utils
Expand Down Expand Up @@ -146,6 +147,11 @@ def _post(self, payload, requester_user, show_secrets=False, match_multiple=Fals
'source_channel': payload.source_channel,
}

inject_immutable_parameters(
action_alias_db=action_alias_db,
multiple_execution_parameters=multiple_execution_parameters,
action_context=context)

results = []
for execution_parameters in multiple_execution_parameters:
execution = self._schedule_execution(action_alias_db=action_alias_db,
Expand Down
12 changes: 11 additions & 1 deletion st2api/tests/unit/controllers/v1/test_alias_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

TEST_MODELS = {
'aliases': ['alias1.yaml', 'alias2.yaml', 'alias_with_undefined_jinja_in_ack_format.yaml',
'alias4.yaml', 'alias_fixes1.yaml', 'alias_fixes2.yaml',
'alias4.yaml', 'alias5.yaml', 'alias_fixes1.yaml', 'alias_fixes2.yaml',
'alias_match_multiple.yaml'],
'actions': ['action1.yaml', 'action2.yaml'],
'runners': ['runner1.yaml']
Expand Down Expand Up @@ -64,6 +64,7 @@ def setUpClass(cls):
cls.alias1 = cls.models['aliases']['alias1.yaml']
cls.alias2 = cls.models['aliases']['alias2.yaml']
cls.alias4 = cls.models['aliases']['alias4.yaml']
cls.alias5 = cls.models['aliases']['alias5.yaml']
cls.alias_with_undefined_jinja_in_ack_format = \
cls.models['aliases']['alias_with_undefined_jinja_in_ack_format.yaml']

Expand All @@ -76,6 +77,15 @@ def test_basic_execution(self, request):
expected_parameters = {'param1': 'value1', 'param2': 'value2 value3'}
self.assertEquals(request.call_args[0][0].parameters, expected_parameters)

@mock.patch.object(action_service, 'request',
return_value=(None, EXECUTION))
def test_basic_execution_with_immutable_parameters(self, request):
command = 'lorem ipsum'
post_resp = self._do_post(alias_execution=self.alias5, command=command)
self.assertEqual(post_resp.status_int, 201)
expected_parameters = {'param1': 'value1', 'param2': 'value2'}
self.assertEquals(request.call_args[0][0].parameters, expected_parameters)

@mock.patch.object(action_service, 'request',
return_value=(None, EXECUTION))
def test_invalid_format_string_referenced_in_request(self, request):
Expand Down
6 changes: 6 additions & 0 deletions st2common/st2common/models/api/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,10 @@ class ActionAliasAPI(BaseAPI, APIUIDMixin):
"type": "object",
"description": "Extra parameters, usually adapter-specific."
},
"immutable_parameters": {
"type": "object",
"description": "Parameters to be passed to the action on every execution."
},
"metadata_file": {
"description": "Path to the metadata file relative to the pack directory.",
"type": "string",
Expand All @@ -645,11 +649,13 @@ def to_model(cls, alias):
ack = getattr(alias, 'ack', None)
result = getattr(alias, 'result', None)
extra = getattr(alias, 'extra', None)
immutable_parameters = getattr(alias, 'immutable_parameters', None)
metadata_file = getattr(alias, 'metadata_file', None)

model = cls.model(name=name, description=description, pack=pack, ref=ref,
enabled=enabled, action_ref=action_ref, formats=formats,
ack=ack, result=result, extra=extra,
immutable_parameters=immutable_parameters,
metadata_file=metadata_file)
return model

Expand Down
2 changes: 2 additions & 0 deletions st2common/st2common/models/db/actionalias.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ class ActionAliasDB(stormbase.StormFoundationDB, stormbase.ContentPackResourceMi
extra = me.DictField(
help_text='Additional parameters (usually adapter-specific) not covered in the schema.'
)
immutable_parameters = me.DictField(
help_text='Parameters to be passed to the action on every execution.')

meta = {
'indexes': [
Expand Down
38 changes: 38 additions & 0 deletions st2common/st2common/models/utils/action_alias_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
BRANCH, SUBPATTERN,
)

from st2common.util.jinja import render_values
from st2common.constants import keyvalue as kv_constants
from st2common.services import keyvalues as kv_service
from st2common.exceptions.content import ParseException
from st2common import log

Expand Down Expand Up @@ -220,6 +223,41 @@ def extract_parameters(format_str, param_stream, match_multiple=False):
return parser.get_extracted_param_value()


def inject_immutable_parameters(action_alias_db, multiple_execution_parameters, action_context):
"""
Inject immutable parameters from the alias definiton on the execution parameters.
Jinja expressions will be resolved.
"""
immutable_parameters = action_alias_db.immutable_parameters or {}
if not immutable_parameters:
return multiple_execution_parameters

user = action_context.get('user', None)

context = {}
context.update({
kv_constants.DATASTORE_PARENT_SCOPE: {
kv_constants.SYSTEM_SCOPE: kv_service.KeyValueLookup(
scope=kv_constants.FULL_SYSTEM_SCOPE),
kv_constants.USER_SCOPE: kv_service.UserKeyValueLookup(
scope=kv_constants.FULL_USER_SCOPE, user=user)
}
})
context.update(action_context)
rendered_params = render_values(immutable_parameters, context)

for exec_params in multiple_execution_parameters:
overriden = [param for param in immutable_parameters.keys() if param in exec_params]
if overriden:
raise ValueError(
"Immutable arguments cannot be overriden: {}".format(
','.join(overriden)))

exec_params.update(rendered_params)

return multiple_execution_parameters


def search_regex_tokens(needle_tokens, haystack_tokens, backwards=False):
"""
Search a tokenized regex for any tokens in needle_tokens. Returns True if
Expand Down
29 changes: 29 additions & 0 deletions st2common/tests/unit/test_action_alias_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@

from __future__ import absolute_import
from sre_parse import (parse, AT, AT_BEGINNING, AT_BEGINNING_STRING, AT_END, AT_END_STRING)
from mock import Mock
from unittest2 import TestCase
from st2common.exceptions.content import ParseException
from st2common.models.utils.action_alias_utils import (
ActionAliasFormatParser, search_regex_tokens,
inject_immutable_parameters
)


Expand Down Expand Up @@ -320,3 +322,30 @@ def test_branches(self):
def test_subpatterns(self):
tokens = parse("^(?:asdf|fdsa$)")
self.assertTrue(search_regex_tokens(self.end_tokens, tokens))


class TestInjectImmutableParameters(TestCase):
def test_immutable_parameters_are_injected(self):
action_alias_db = Mock()
action_alias_db.immutable_parameters = {"env": "dev"}
exec_params = [{"param1": "value1", "param2": "value2"}]
inject_immutable_parameters(action_alias_db, exec_params, {})
self.assertEqual(
exec_params,
[{"param1": "value1", "param2": "value2", "env": "dev"}])

def test_immutable_parameters_with_jinja(self):
action_alias_db = Mock()
action_alias_db.immutable_parameters = {"env": '{{ "dev" + "1" }}'}
exec_params = [{"param1": "value1", "param2": "value2"}]
inject_immutable_parameters(action_alias_db, exec_params, {})
self.assertEqual(
exec_params,
[{"param1": "value1", "param2": "value2", "env": "dev1"}])

def test_override_raises_error(self):
action_alias_db = Mock()
action_alias_db.immutable_parameters = {"env": "dev"}
exec_params = [{"param1": "value1", "env": "prod"}]
with self.assertRaises(ValueError):
inject_immutable_parameters(action_alias_db, exec_params, {})
10 changes: 10 additions & 0 deletions st2tests/st2tests/fixtures/aliases/aliases/alias5.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
name: "alias5"
pack: "aliases"
description: "Static test"
action_ref: "wolfpack.action1"
formats:
- "lorem ipsum"
immutable_parameters:
param1: value1
param2: value2