Add basic output updates with websockets

This commit is contained in:
Jake McDermott 2020-05-14 12:46:49 -04:00
parent 24691f6c75
commit d40497aca5
No known key found for this signature in database
GPG Key ID: 0E56ED990CDFCB4F
4 changed files with 68 additions and 9 deletions

View File

@ -20,6 +20,8 @@ you'll need to update your django settings and use the `TARGET_HOST` and `TARGET
echo "CSRF_TRUSTED_ORIGINS = ['awx.local:8043']" >> /awx/settings/development.py
TARGET_HOST='awx.local:8043' TARGET_PORT=8043 npm --prefix awx/ui_next start
```
**Note:** When using an external server, you must also manually update the `proxy` field in `package.json`
to point to the new websocket url.
## Testing
```shell

View File

@ -87,5 +87,6 @@
"<rootDir>/src/locales",
"index.js"
]
}
},
"proxy": "https://localhost:8043/websocket"
}

View File

@ -63,6 +63,44 @@ const OutputFooter = styled.div`
flex: 1;
`;
let ws;
function connectJobSocket({ type, id }, onMessage) {
ws = new WebSocket(`wss://${window.location.host}/websocket/`);
ws.onopen = () => {
const xrftoken = `; ${document.cookie}`
.split('; csrftoken=')
.pop()
.split(';')
.shift();
const eventGroup = `${type}_events`;
ws.send(
JSON.stringify({
xrftoken,
groups: { jobs: ['summary', 'status_changed'], [eventGroup]: [id] },
})
);
};
ws.onmessage = e => {
onMessage(JSON.parse(e.data));
};
ws.onclose = e => {
// eslint-disable-next-line no-console
console.debug('Socket closed. Reconnecting...', e);
setTimeout(() => {
connectJobSocket({ type, id }, onMessage);
}, 1000);
};
ws.onerror = err => {
// eslint-disable-next-line no-console
console.debug('Socket error: ', err, 'Disconnecting...');
ws.close();
};
}
function range(low, high) {
const numbers = [];
for (let n = low; n <= high; n++) {
@ -105,11 +143,22 @@ class JobOutput extends Component {
this.isRowLoaded = this.isRowLoaded.bind(this);
this.loadMoreRows = this.loadMoreRows.bind(this);
this.scrollToRow = this.scrollToRow.bind(this);
this.monitorJobSocketCounter = this.monitorJobSocketCounter.bind(this);
}
componentDidMount() {
const { job } = this.props;
this._isMounted = true;
this.loadJobEvents();
connectJobSocket(job, data => {
if (data.counter && data.counter > this.jobSocketCounter) {
this.jobSocketCounter = data.counter;
} else if (data.final_counter && data.unified_job_id === job.id) {
this.jobSocketCounter = data.final_counter;
}
});
this.interval = setInterval(() => this.monitorJobSocketCounter(), 5000);
}
componentDidUpdate(prevProps, prevState) {
@ -131,9 +180,21 @@ class JobOutput extends Component {
}
componentWillUnmount() {
if (ws) {
ws.close();
}
clearInterval(this.interval);
this._isMounted = false;
}
monitorJobSocketCounter() {
const { remoteRowCount } = this.state;
if (this.jobSocketCounter >= remoteRowCount) {
this._isMounted &&
this.setState({ remoteRowCount: this.jobSocketCounter + 1 });
}
}
async loadJobEvents() {
const { job, type } = this.props;

View File

@ -4,6 +4,9 @@ const TARGET_PORT = process.env.TARGET_PORT || 8043;
const TARGET_HOST = process.env.TARGET_HOST || 'localhost';
const TARGET = `https://${TARGET_HOST}:${TARGET_PORT}`;
// Note: The websocket proxy is configured
// manually using the 'proxy' field in package.json
module.exports = app => {
app.use(
'/api/login/',
@ -29,12 +32,4 @@ module.exports = app => {
req.originalUrl.includes('login'),
})
);
app.use(
'/websocket',
createProxyMiddleware({
target: TARGET,
secure: false,
ws: true,
})
);
};