Skip to content

Commit 5d46fe2

Browse files
authored
Merge pull request #3612 from StackStorm/more_v2.3.2_changes
More changes for v2.3.2
2 parents 50cf2d6 + 682eb35 commit 5d46fe2

File tree

22 files changed

+445
-52
lines changed

22 files changed

+445
-52
lines changed

CHANGELOG.rst

+9-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ Added
1818
Contributed by Nick Maludy. #3508
1919
* Update ``st2`` CLI so it also displays "there are more results" note when ``-n`` flag is
2020
used and there are more items available. (improvement) #3552
21-
* Add ability to explicitly set ``stream_url`` in st2client and CLI. (improvement) #3432
21+
* Add ability to explicitly set ``stream_url`` in st2client. (improvement) #3432
22+
* Add support for handling arrays of dictionaries to ``st2 config`` CLI command. (improvement)
23+
#3594
24+
25+
Contributed by Hiroyasu OHYAMA.
2226

2327
Fixed
2428
~~~~~
@@ -42,6 +46,10 @@ Fixed
4246
Reported by sibirajal.
4347
* Add a check to make sure action exists in the POST of the action execution API. (bug fix)
4448
* Fix api key generation, to use system user, when auth is disabled. (bug fix) #3578 #3593
49+
* Fix invocation of Mistral workflow from Action Chain with jinja in params. (bug fix) #3440
50+
* Fix st2client API bug, a backward incompatible change in `query()` method, introduced in note
51+
implementation (#3514) in 2.3.1. The `query()` method is now backward compatible (pre 2.3) and
52+
`query_with_count()` method is used for results pagination and note. #3616
4553

4654
2.3.1 - July 07, 2017
4755
---------------------
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
chain:
2+
-
3+
name: task1
4+
ref: examples.mistral-basic
5+
params:
6+
cmd: "{{cmd}}"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
name: invoke-mistral-with-jinja
3+
description: Example to invoke a Mistral workflow from an Action Chain
4+
runner_type: action-chain
5+
entry_point: chains/invoke_mistral_with_jinja.yaml
6+
enabled: true
7+
parameters:
8+
cmd:
9+
type: string
10+
required: true

contrib/runners/mistral_v2/mistral_v2.py

+12
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from st2common.persistence.execution import ActionExecution
3030
from st2common.persistence.liveaction import LiveAction
3131
from st2common.services import action as action_service
32+
from st2common.util import jinja
3233
from st2common.util.workflow import mistral as utils
3334
from st2common.util.url import get_url_without_trailing_slash
3435
from st2common.util.api import get_full_public_api_url
@@ -161,9 +162,20 @@ def _construct_workflow_execution_options(self):
161162
parent_context = {
162163
'execution_id': self.execution_id
163164
}
165+
164166
if getattr(self.liveaction, 'context', None):
165167
parent_context.update(self.liveaction.context)
166168

169+
# Convert jinja expressions in the params of Action Chain under the parent context
170+
# into raw block. If there is any jinja expressions, Mistral will try to evaulate
171+
# the expression. If there is a local context reference, the evaluation will fail
172+
# because the local context reference is out of scope.
173+
chain_ctx = parent_context.get('chain') or {}
174+
chain_params_ctx = chain_ctx.get('params') or {}
175+
176+
for k, v in six.iteritems(chain_params_ctx):
177+
parent_context['chain']['params'][k] = jinja.convert_jinja_to_raw_block(v)
178+
167179
st2_execution_context = {
168180
'api_url': api_url,
169181
'endpoint': endpoint,

contrib/runners/mistral_v2/tests/unit/test_mistral_v2.py

+187
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,193 @@ def test_launch_workflow(self):
270270
executions.ExecutionManager.create.assert_called_with(
271271
WF1_NAME, workflow_input=workflow_input, env=env)
272272

273+
@mock.patch.object(
274+
workflows.WorkflowManager, 'list',
275+
mock.MagicMock(return_value=[]))
276+
@mock.patch.object(
277+
workflows.WorkflowManager, 'get',
278+
mock.MagicMock(return_value=WF1))
279+
@mock.patch.object(
280+
workflows.WorkflowManager, 'create',
281+
mock.MagicMock(return_value=[WF1]))
282+
@mock.patch.object(
283+
executions.ExecutionManager, 'create',
284+
mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
285+
def test_launch_workflow_under_parent_chain_with_jinja_params(self):
286+
ac_ctx = {
287+
'chain': {
288+
'params': {
289+
'var1': 'foobar',
290+
'var2': '{{foobar}}',
291+
'var3': ['{{foo}}', '{{bar}}'],
292+
'var4': {
293+
'foobar': '{{foobar}}'
294+
},
295+
'var5': {
296+
'foobar': '{% for item in items %}foobar{% end for %}'
297+
}
298+
}
299+
}
300+
}
301+
302+
liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=ac_ctx)
303+
liveaction, execution = action_service.request(liveaction)
304+
liveaction = LiveAction.get_by_id(str(liveaction.id))
305+
self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
306+
307+
mistral_context = liveaction.context.get('mistral', None)
308+
self.assertIsNotNone(mistral_context)
309+
self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
310+
self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))
311+
312+
workflow_input = copy.deepcopy(ACTION_PARAMS)
313+
workflow_input.update({'count': '3'})
314+
315+
env = {
316+
'st2_execution_id': str(execution.id),
317+
'st2_liveaction_id': str(liveaction.id),
318+
'st2_action_api_url': 'http://0.0.0.0:9101/v1',
319+
'__actions': {
320+
'st2.action': {
321+
'st2_context': {
322+
'api_url': 'http://0.0.0.0:9101/v1',
323+
'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
324+
'parent': {
325+
'execution_id': str(execution.id),
326+
'chain': {
327+
'params': {
328+
'var1': 'foobar',
329+
'var2': '{% raw %}{{foobar}}{% endraw %}',
330+
'var3': [
331+
'{% raw %}{{foo}}{% endraw %}',
332+
'{% raw %}{{bar}}{% endraw %}'
333+
],
334+
'var4': {
335+
'foobar': '{% raw %}{{foobar}}{% endraw %}'
336+
},
337+
'var5': {
338+
'foobar': (
339+
'{% raw %}{% for item in items %}'
340+
'foobar{% end for %}{% endraw %}'
341+
)
342+
}
343+
}
344+
}
345+
},
346+
'notify': {},
347+
'skip_notify_tasks': []
348+
}
349+
}
350+
}
351+
}
352+
353+
executions.ExecutionManager.create.assert_called_with(
354+
WF1_NAME, workflow_input=workflow_input, env=env)
355+
356+
@mock.patch.object(
357+
workflows.WorkflowManager, 'list',
358+
mock.MagicMock(return_value=[]))
359+
@mock.patch.object(
360+
workflows.WorkflowManager, 'get',
361+
mock.MagicMock(return_value=WF1))
362+
@mock.patch.object(
363+
workflows.WorkflowManager, 'create',
364+
mock.MagicMock(return_value=[WF1]))
365+
@mock.patch.object(
366+
executions.ExecutionManager, 'create',
367+
mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
368+
def test_launch_workflow_under_parent_chain_with_nonetype_in_chain_context(self):
369+
ac_ctx = {'chain': None}
370+
371+
liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=ac_ctx)
372+
liveaction, execution = action_service.request(liveaction)
373+
liveaction = LiveAction.get_by_id(str(liveaction.id))
374+
self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
375+
376+
mistral_context = liveaction.context.get('mistral', None)
377+
self.assertIsNotNone(mistral_context)
378+
self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
379+
self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))
380+
381+
workflow_input = copy.deepcopy(ACTION_PARAMS)
382+
workflow_input.update({'count': '3'})
383+
384+
env = {
385+
'st2_execution_id': str(execution.id),
386+
'st2_liveaction_id': str(liveaction.id),
387+
'st2_action_api_url': 'http://0.0.0.0:9101/v1',
388+
'__actions': {
389+
'st2.action': {
390+
'st2_context': {
391+
'api_url': 'http://0.0.0.0:9101/v1',
392+
'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
393+
'parent': {
394+
'execution_id': str(execution.id),
395+
'chain': None
396+
},
397+
'notify': {},
398+
'skip_notify_tasks': []
399+
}
400+
}
401+
}
402+
}
403+
404+
executions.ExecutionManager.create.assert_called_with(
405+
WF1_NAME, workflow_input=workflow_input, env=env)
406+
407+
@mock.patch.object(
408+
workflows.WorkflowManager, 'list',
409+
mock.MagicMock(return_value=[]))
410+
@mock.patch.object(
411+
workflows.WorkflowManager, 'get',
412+
mock.MagicMock(return_value=WF1))
413+
@mock.patch.object(
414+
workflows.WorkflowManager, 'create',
415+
mock.MagicMock(return_value=[WF1]))
416+
@mock.patch.object(
417+
executions.ExecutionManager, 'create',
418+
mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
419+
def test_launch_workflow_under_parent_chain_with_nonetype_in_params_context(self):
420+
ac_ctx = {'chain': {'params': None}}
421+
422+
liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=ac_ctx)
423+
liveaction, execution = action_service.request(liveaction)
424+
liveaction = LiveAction.get_by_id(str(liveaction.id))
425+
self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
426+
427+
mistral_context = liveaction.context.get('mistral', None)
428+
self.assertIsNotNone(mistral_context)
429+
self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
430+
self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))
431+
432+
workflow_input = copy.deepcopy(ACTION_PARAMS)
433+
workflow_input.update({'count': '3'})
434+
435+
env = {
436+
'st2_execution_id': str(execution.id),
437+
'st2_liveaction_id': str(liveaction.id),
438+
'st2_action_api_url': 'http://0.0.0.0:9101/v1',
439+
'__actions': {
440+
'st2.action': {
441+
'st2_context': {
442+
'api_url': 'http://0.0.0.0:9101/v1',
443+
'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
444+
'parent': {
445+
'execution_id': str(execution.id),
446+
'chain': {
447+
'params': None
448+
}
449+
},
450+
'notify': {},
451+
'skip_notify_tasks': []
452+
}
453+
}
454+
}
455+
}
456+
457+
executions.ExecutionManager.create.assert_called_with(
458+
WF1_NAME, workflow_input=workflow_input, env=env)
459+
273460
@mock.patch.object(
274461
workflows.WorkflowManager, 'list',
275462
mock.MagicMock(return_value=[]))

st2api/st2api/controllers/v1/timers.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ def __init__(self):
8787

8888
def get_all(self, timer_type=None):
8989
if timer_type and timer_type not in self._allowed_timer_types:
90-
msg = 'Timer type %s not in supported types - %s.' % self._allowed_timer_types
90+
msg = 'Timer type %s not in supported types - %s.' % (timer_type,
91+
self._allowed_timer_types)
9192
abort(http_client.BAD_REQUEST, msg)
9293

9394
t_all = self._timers.get_all(timer_type=timer_type)

st2client/st2client/commands/action.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -1062,8 +1062,7 @@ def run(self, args, **kwargs):
10621062
exclude_attributes = ','.join(exclude_attributes)
10631063
kwargs['exclude_attributes'] = exclude_attributes
10641064

1065-
result, count = self.manager.query(limit=args.last, **kwargs)
1066-
return (result, count)
1065+
return self.manager.query_with_count(limit=args.last, **kwargs)
10671066

10681067
def run_and_print(self, args, **kwargs):
10691068

@@ -1084,7 +1083,7 @@ def run_and_print(self, args, **kwargs):
10841083
attributes=args.attr, widths=args.width,
10851084
attribute_transform_functions=self.attribute_transform_functions)
10861085

1087-
if args.last and count and int(count) > args.last:
1086+
if args.last and count and count > args.last:
10881087
table.SingleRowTable.note_box(self.resource_name, args.last)
10891088

10901089

st2client/st2client/commands/policy.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def run(self, args, **kwargs):
4949
if args.resource_type:
5050
filters = {'resource_type': args.resource_type}
5151
filters.update(**kwargs)
52-
instances, _ = self.manager.query(**filters)
52+
instances = self.manager.query(**filters)
5353
return instances
5454
else:
5555
return self.manager.get_all(**kwargs)

st2client/st2client/commands/rbac.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def run(self, args, **kwargs):
7272
kwargs['system'] = args.system
7373

7474
if args.system:
75-
result, _ = self.manager.query(**kwargs)
75+
result = self.manager.query(**kwargs)
7676
else:
7777
result = self.manager.get_all(**kwargs)
7878

@@ -143,7 +143,7 @@ def run(self, args, **kwargs):
143143
kwargs['remote'] = args.remote
144144

145145
if args.role or args.user or args.remote:
146-
result, _ = self.manager.query(**kwargs)
146+
result = self.manager.query(**kwargs)
147147
else:
148148
result = self.manager.get_all(**kwargs)
149149

st2client/st2client/commands/rule.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,7 @@ def run(self, args, **kwargs):
8383
# switch attr to display the trigger and action
8484
args.attr = self.display_attributes_iftt
8585

86-
result, count = self.manager.query(limit=args.last, **kwargs)
87-
return (result, count)
86+
return self.manager.query_with_count(limit=args.last, **kwargs)
8887

8988
def run_and_print(self, args, **kwargs):
9089
instances, count = self.run(args, **kwargs)
@@ -96,7 +95,7 @@ def run_and_print(self, args, **kwargs):
9695
self.print_output(instances, table.MultiColumnTable,
9796
attributes=args.attr, widths=args.width)
9897

99-
if args.last and count and int(count) > args.last:
98+
if args.last and count and count > args.last:
10099
table.SingleRowTable.note_box(self.resource_name, args.last)
101100

102101

st2client/st2client/commands/rule_enforcement.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,7 @@ def run(self, args, **kwargs):
110110
if args.timestamp_lt:
111111
kwargs['enforced_at_lt'] = args.timestamp_lt
112112

113-
result, count = self.manager.query(limit=args.last, **kwargs)
114-
return (result, count)
113+
return self.manager.query_with_count(limit=args.last, **kwargs)
115114

116115
def run_and_print(self, args, **kwargs):
117116
instances, count = self.run(args, **kwargs)
@@ -123,5 +122,5 @@ def run_and_print(self, args, **kwargs):
123122
else:
124123
self.print_output(instances, table.MultiColumnTable,
125124
attributes=args.attr, widths=args.width)
126-
if args.last and count and int(count) > args.last:
125+
if args.last and count and count > args.last:
127126
table.SingleRowTable.note_box(self.resource_name, args.last)

st2client/st2client/commands/timer.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@ def run(self, args, **kwargs):
4646
kwargs['timer_type'] = args.timer_type
4747

4848
if kwargs:
49-
result, _ = self.manager.query(**kwargs)
50-
return result
49+
return self.manager.query(**kwargs)
5150
else:
5251
return self.manager.get_all(**kwargs)
5352

st2client/st2client/commands/trace.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,7 @@ def run(self, args, **kwargs):
163163
kwargs['sort_asc'] = True
164164
elif args.sort_order in ['desc', 'descending']:
165165
kwargs['sort_desc'] = True
166-
result, count = self.manager.query(limit=args.last, **kwargs)
167-
return (result, count)
166+
return self.manager.query_with_count(limit=args.last, **kwargs)
168167

169168
def run_and_print(self, args, **kwargs):
170169
instances, count = self.run(args, **kwargs)
@@ -177,7 +176,7 @@ def run_and_print(self, args, **kwargs):
177176
self.print_trace_details(trace=instances[0], args=args)
178177

179178
if not args.json and not args.yaml:
180-
if args.last and count and int(count) > args.last:
179+
if args.last and count and count > args.last:
181180
table.SingleRowTable.note_box(self.resource_name, 1)
182181
else:
183182
if args.json or args.yaml:
@@ -190,7 +189,7 @@ def run_and_print(self, args, **kwargs):
190189
attributes=args.attr, widths=args.width,
191190
attribute_transform_functions=self.attribute_transform_functions)
192191

193-
if args.last and count and int(count) > args.last:
192+
if args.last and count and count > args.last:
194193
table.SingleRowTable.note_box(self.resource_name, args.last)
195194

196195

0 commit comments

Comments
 (0)