Skip to content

Commit d88b562

Browse files
authored
Merge pull request #868 from StackStorm/dont_load_large_execution_result
Don't display and render large execution results, fix lint gulp task and lint violations, use latest up to date docker compose setup for e2e tests
2 parents b80301b + f53595b commit d88b562

File tree

25 files changed

+264
-91
lines changed

25 files changed

+264
-91
lines changed

.circleci/config.yml

+17-21
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ jobs:
1212
DEPLOY_PACKAGES: 1
1313
DEB: xenial bionic
1414
RPM: el7 el8
15+
ST2_VERSION: "3.5dev"
1516
ST2_HOST: localhost
16-
ST2_USERNAME: admin
17-
ST2_PASSWORD: 123
17+
ST2_PROTOCOL: http
18+
ST2_USERNAME: st2admin
19+
ST2_PASSWORD: Ch@ngeMe
1820
ST2_TEST_ENVIRONMENT: https://github.com/StackStorm/st2-docker
1921
steps:
2022
- checkout
@@ -68,48 +70,42 @@ jobs:
6870
name: Update Docker Compose
6971
command: |
7072
set -x
71-
sudo sh -c "curl -L https://github.com/docker/compose/releases/download/1.14.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose"
73+
sudo sh -c "curl -L https://github.com/docker/compose/releases/download/1.28.6/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose"
7274
sudo chmod +x /usr/local/bin/docker-compose
7375
- run:
7476
name: Clone test containers
7577
command: |
76-
# Use DEPRECATED/all-in-one for now, we'll have to circle back around
77-
# and fix this to use the master branch
78-
echo "Cloning ${ST2_DOCKER_BRANCH:-DEPRECATED/all-in-one} branch of st2-docker"
79-
git clone --branch ${ST2_DOCKER_BRANCH:-DEPRECATED/all-in-one} --depth 1 ${ST2_TEST_ENVIRONMENT} ~/st2-docker
78+
echo "Cloning ${ST2_DOCKER_BRANCH:-master} branch of st2-docker"
79+
git clone --branch ${ST2_DOCKER_BRANCH:-master} --depth 1 ${ST2_TEST_ENVIRONMENT} ~/st2-docker
8080
- run:
81-
name: Update env variables for test containers
81+
name: Configufe docker compose config
8282
command: |
83-
make -C ~/st2-docker env
84-
echo -e "ST2_USER=${ST2_USERNAME}\nST2_PASSWORD=${ST2_PASSWORD}" > ~/st2-docker/conf/stackstorm.env
85-
cat ~/st2-docker/conf/stackstorm.env
83+
# Configure allow origin in the user config
84+
echo "[api]" > ~/st2-docker/files/st2.user.conf
85+
echo "allow_origin = *" >> ~/st2-docker/files/st2.user.conf
8686
- run:
8787
name: Start test containers
8888
command: |
8989
docker-compose -f ~/st2-docker/docker-compose.yml up -d
90-
sleep 60
91-
docker-compose -f ~/st2-docker/docker-compose.yml exec stackstorm crudini --set /etc/st2/st2.conf api allow_origin "*"
92-
docker-compose -f ~/st2-docker/docker-compose.yml exec stackstorm st2ctl restart
90+
sleep 100
9391
- run:
9492
name: Check test containers
9593
command: |
96-
docker-compose -f ~/st2-docker/docker-compose.yml exec stackstorm st2 run core.noop
94+
docker-compose -f ~/st2-docker/docker-compose.yml exec st2client st2 run core.noop
9795
- run:
9896
name: Run functional tests
9997
command: npm run test-functional
10098
- run:
10199
name: Reset test containers
102100
command: |
103-
docker-compose -f ~/st2-docker/docker-compose.yml down
101+
docker-compose -f ~/st2-docker/docker-compose.yml down --rmi
104102
docker-compose -f ~/st2-docker/docker-compose.yml up -d
105-
sleep 60
106-
docker-compose -f ~/st2-docker/docker-compose.yml exec stackstorm crudini --set /etc/st2/st2.conf api allow_origin "*"
107-
docker-compose -f ~/st2-docker/docker-compose.yml exec stackstorm st2ctl restart
103+
sleep 100
108104
- run:
109105
name: Recheck test containers
110106
command: |
111-
docker-compose -f ~/st2-docker/docker-compose.yml exec stackstorm st2 run core.noop
112-
docker-compose -f ~/st2-docker/docker-compose.yml exec stackstorm st2 execution list
107+
docker-compose -f ~/st2-docker/docker-compose.yml exec st2client st2 run core.noop
108+
docker-compose -f ~/st2-docker/docker-compose.yml exec st2client st2 execution list
113109
- run:
114110
name: Run tests on production version
115111
command: npm run test-production

.eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ modules/*/node_modules
33
tasks/*/node_modules
44
node_modules
55
js
6+
package.meta.js

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,9 @@ We're using [zombie](https://github.com/assaf/zombie) as our headless browser fo
123123

124124
First of all, you need to make sure you have a running copy of st2 to run tests against. We're using [official docker images](https://github.com/stackstorm/st2-docker) for our automated tests, but the [AIO](https://docs.stackstorm.com/install/index.html) deployment will work just as good (though will take more time to deploy).
125125

126-
To let test runner know the details of your st2 installation, you need to set ST2_HOST, ST2_USERNAME and ST2_PASSWORD env variables, then call `gulp test`.
126+
To let test runner know the details of your st2 installation, you need to set ST2_PROTOCOL, ST2_HOST, ST2_USERNAME and ST2_PASSWORD env variables, then call `gulp test`.
127127

128-
$ ST2_HOST=localhost ST2_USERNAME=admin ST2_PASSWORD=123 gulp test
128+
$ ST2_PROTOCOL=http ST2_HOST=localhost ST2_USERNAME=admin ST2_PASSWORD=123 gulp test
129129

130130
Copyright, License, and Contributors Agreement
131131
----------------------------------------------

apps/st2-actions/actions-details.component.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,11 @@ export default class ActionsDetails extends React.Component {
213213
},
214214
});
215215
}
216-
setWindowName(e){
217-
window.name="parent"
218-
}
216+
217+
setWindowName(e) {
218+
window.name = 'parent';
219+
}
220+
219221
handleRun(e, ...args) {
220222
e.preventDefault();
221223

@@ -259,7 +261,7 @@ setWindowName(e){
259261
target="_blank"
260262
to={`/action/${action.ref}`}
261263
className="st2-forms__button st2-details__toolbar-button"
262-
onClick ={e => this.setWindowName(e)}
264+
onClick={e => this.setWindowName(e)}
263265
>
264266
Edit
265267
</Link>

apps/st2-history/history-details.component.js

+10-3
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import HistoryPopup from './history-popup.component';
4747
const { execution } = state;
4848
return { execution };
4949
})
50+
5051
export default class HistoryDetails extends React.Component {
5152
static propTypes = {
5253
handleNavigate: PropTypes.func.isRequired,
@@ -55,7 +56,7 @@ export default class HistoryDetails extends React.Component {
5556

5657
id: PropTypes.string,
5758
section: PropTypes.string,
58-
execution: PropTypes.object,
59+
execution: PropTypes.object, // eslint-disable-line react/no-unused-prop-types
5960
displayUTC: PropTypes.bool.isRequired,
6061
handleToggleUTC: PropTypes.func,
6162
}
@@ -81,9 +82,15 @@ export default class HistoryDetails extends React.Component {
8182
}
8283

8384
fetchExecution(id) {
85+
// We utilize ?max_result_size query parameter filter so we don't retrieve
86+
// large results which we don't render due to that being very slow and
87+
// freezing the browser window
88+
const maxResultSizeForRender = ActionReporter.utils.getMaxExecutionResultSizeForRender();
89+
const path = `/executions/${id}?max_result_size=${maxResultSizeForRender}`;
90+
8491
store.dispatch({
8592
type: 'FETCH_EXECUTION',
86-
promise: api.request({ path: `/executions/${id}` }),
93+
promise: api.request({ path: path }),
8794
})
8895
.catch((err) => {
8996
notification.error(`Unable to retrieve execution "${id}".`, { err });
@@ -183,7 +190,7 @@ export default class HistoryDetails extends React.Component {
183190
</Link>
184191
</DetailsPanelHeading>
185192
<DetailsPanelBody data-test="action_output">
186-
<ActionReporter runner={execution.runner.name} execution={execution} />
193+
<ActionReporter runner={execution.runner.name} execution={execution} api={api} />
187194
</DetailsPanelBody>
188195
</DetailsPanel>
189196
{ execution.rule ? (

apps/st2-history/history-panel.component.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export default class HistoryPanel extends React.Component {
9494
}).isRequired,
9595

9696
filter: PropTypes.string,
97-
filters: PropTypes.object,
97+
filters: PropTypes.array,
9898
childExecutions: PropTypes.object,
9999
groups: PropTypes.array,
100100
collapsed: PropTypes.bool,

apps/st2-workflows/workflows.component.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export default class Workflows extends Component {
7373
pack: PropTypes.string,
7474
meta: PropTypes.object,
7575
metaSource: PropTypes.string,
76-
setMeta: PropTypes.func,
76+
setMeta: PropTypes.func, // eslint-disable-line react/no-unused-prop-types
7777
input: PropTypes.array,
7878
workflowSource: PropTypes.string,
7979
dirty: PropTypes.bool,
@@ -243,7 +243,7 @@ export default class Workflows extends Component {
243243
}
244244

245245
save() {
246-
const { pack, meta, actions, workflowSource, metaSource, setMeta } = this.props;
246+
const { pack, meta, actions, workflowSource, metaSource } = this.props;
247247
const existingAction = actions.find(e => e.name === meta.name && e.pack === pack);
248248

249249
if (!meta.name) {

config.js

+9
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@
1818
angular.module('main')
1919
.constant('st2Config', {
2020

21+
// In case you want to override default value for the result sizes we still render in the
22+
// history details widget. Keep in mind that anything above 200-500 KB will take a long time to
23+
// render and likely freeze the browser window for deeply nested JSON object results.
24+
// Value is in bytes.
25+
// max_execution_result_size_for_render: 200 * 1024,
26+
//
27+
// Set to true to display StackStorm and st2web version in the header
28+
//show_version_in_header: false;
29+
2130
// hosts: [
2231
// {
2332
// name: 'Dev Env',

index.html

+11-3
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,18 @@
1919
throw new Error('The st2web angular-config-polyfill only supports the "main" module.');
2020
}
2121

22-
if (constant !== 'st2Config') {
23-
throw new Error('The st2web angular-config-polyfill only supports the "st2Config" constant.');
22+
if (constant !== 'st2Config' && constant !== 'st2PackageMeta') {
23+
throw new Error('The st2web angular-config-polyfill only supports the "st2Config" and "st2PackageMeta" constant.');
2424
}
2525

2626
window.st2constants = window.st2constants || {};
27-
window.st2constants.st2Config = value;
27+
28+
if (constant === 'st2Config') {
29+
window.st2constants.st2Config = value;
30+
}
31+
else if (constant === 'st2PackageMeta') {
32+
window.st2constants.st2PackageMeta = value;
33+
}
2834
},
2935
run: (fn) => {
3036
if (module !== 'main') {
@@ -33,6 +39,7 @@
3339

3440
window.st2constants = window.st2constants || {};
3541
window.st2constants.st2Config = window.st2constants.st2Config || {};
42+
window.st2constants.st2PackageMeta = window.st2constants.st2PackageMeta || {};
3643

3744
fn(window.st2constants.st2Config);
3845
},
@@ -44,6 +51,7 @@
4451
<body>
4552
<div class="wrapper" id="container"></div>
4653
<script src="config.js"></script>
54+
<script src="package.meta.js"></script>
4755
<script src="js/main.js"></script>
4856
</body>
4957

modules/st2-action-reporter/action-reporter.component.js

+94-1
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,114 @@ import reporters from './reporters';
2020

2121
import style from './style.css';
2222

23+
// If action execution result is larger than this value (in bytes) we won't try to render it in
24+
// the code highlighter widget, but display a link to the raw result output instead.
25+
// This way we avoid large results freezing and blocking the browser window.
26+
// Keep in mind that rendering time also depends on the result type (aka deeply
27+
// nested JSON object vs a more flat one).
28+
// Based on testing, any larger and more nested JSON object over 100 KB will
29+
// take a while to render and consume a lot of memory (and in case of even
30+
// larger objects, freeze / block the whole browser window).
31+
// Technically we could still display and render results up to 300 KB, but the
32+
// whole code widget and browser window gets lagy and slow.
33+
// Testing was also performed on relatively high end PC so on older ones, even
34+
// lower limit may be more appropriate.
35+
// Can be overriden in the config, but values over 50-100 KB (depending on the client
36+
// resources and how nested the result objects are) are not recommended.
37+
const DEFAULT_MAX_RESULT_SIZE = 100 * 1024; // 100 KB
38+
39+
40+
/**
41+
* Return base URL to the API service based on the config value.
42+
*/
43+
function getBaseAPIUrl(api) {
44+
if (!api.server) {
45+
console.log('config.js is not correctly configured - it\'s missing API server URL entry');
46+
return null;
47+
}
48+
49+
if (!api.server.api) {
50+
console.log('config.js is not correctly configured - it\'s missing API server URL entry');
51+
return null;
52+
}
53+
54+
const url = api.server.api;
55+
let baseUrl;
56+
57+
if (!url.startsWith('http://') && !(url.startsWith('https://'))) {
58+
baseUrl = `${window.location.protocol}${url}`;
59+
}
60+
else {
61+
baseUrl = `${url}`;
62+
}
63+
64+
return baseUrl;
65+
}
66+
67+
/**
68+
* Return value for the ?max_result_size query parameter aka the maximum number for the result size
69+
* (in bytes) we will still try to render and display.
70+
*
71+
* We specify a default value which can be overriden inside the config.
72+
*/
73+
function getMaxExecutionResultSizeForRender() {
74+
let maxResultSizeForRender;
75+
76+
try {
77+
maxResultSizeForRender = window.st2constants.st2Config.max_execution_result_size_for_render || DEFAULT_MAX_RESULT_SIZE;
78+
}
79+
catch (e) {
80+
maxResultSizeForRender = DEFAULT_MAX_RESULT_SIZE;
81+
}
82+
83+
return maxResultSizeForRender;
84+
}
85+
2386
export default class ActionReporter extends React.Component {
2487
static propTypes = {
2588
className: PropTypes.string,
2689
runner: PropTypes.string.isRequired,
2790
execution: PropTypes.object.isRequired,
91+
api: PropTypes.object.isRequired,
92+
}
93+
94+
static utils = {
95+
getMaxExecutionResultSizeForRender: getMaxExecutionResultSizeForRender,
96+
getBaseAPIUrl: getBaseAPIUrl,
2897
}
2998

3099
render() {
31-
const { className, runner, execution, ...props } = this.props;
100+
const { className, runner, execution, api, ...props } = this.props;
32101
const reporter = reporters[runner] || reporters.debug;
33102

34103
if (!execution) {
35104
return null;
36105
}
37106

107+
// For backward compatibility with older executions which may not have result_size attribute
108+
// we fall back to execution.result (if available - would only be available when using newer
109+
// st2web with older version of other StackStorm components).
110+
const resultSize = execution.result_size || JSON.stringify(execution.result || {}).length;
111+
const resultSizeMB = ((resultSize / 1024 / 1024)).toFixed(2);
112+
const maxResultSizeForRender = getMaxExecutionResultSizeForRender();
113+
114+
if (resultSize && resultSize > maxResultSizeForRender) {
115+
// TODO: Add methods to the client to retrieve full correct URL?
116+
const baseApiUrl = getBaseAPIUrl(api);
117+
const viewRawResultUrl = `${baseApiUrl}/v1/executions/${execution.id}/result?pretty_format=1`;
118+
const downloadRawResultUrl = `${baseApiUrl}/v1/executions/${execution.id}/result?download=1&pretty_format=1`;
119+
const downloadCompressedRawResultUrl = `${baseApiUrl}/v1/executions/${execution.id}/result?download=1&pretty_format=1&compress=1`;
120+
121+
return (
122+
<div {...props} className={cx(style.component, className)}>
123+
<div key="output" className={style.source}>Output</div>
124+
<p>
125+
Action output is too large to be displayed here ({`${resultSizeMB}`} MB).<br /><br />You can view raw execution output by clicking <a href={`${viewRawResultUrl}`} target="_blank" rel="noopener noreferrer">here</a> or you can download the output by clicking <a href={`${downloadRawResultUrl}`} target="_blank" rel="noopener noreferrer">here (uncompressed)</a> or <a href={`${downloadCompressedRawResultUrl}`} target="_blank" rel="noopener noreferrer">here (compressed)</a>.
126+
</p>
127+
</div>
128+
);
129+
}
130+
38131
return (
39132
<div {...props} className={cx(style.component, className)}>
40133
{ reporter(execution) }

0 commit comments

Comments
 (0)