Refactor upgrade websocket progress, so "anyone" can join in to see
now while an upgrade is executing, anyone with permission can "view" the upgrade and see the same progress the executor is seeing
This commit is contained in:
parent
18cec49a86
commit
e93063a344
|
@ -40,73 +40,22 @@
|
||||||
|
|
||||||
<%def name="extra_styles()">
|
<%def name="extra_styles()">
|
||||||
${parent.extra_styles()}
|
${parent.extra_styles()}
|
||||||
<style type="text/css">
|
% if master.has_perm('execute'):
|
||||||
.progress-with-textout {
|
<style type="text/css">
|
||||||
border: 1px solid Black;
|
.progress-with-textout {
|
||||||
line-height: 1.2;
|
border: 1px solid Black;
|
||||||
overflow: auto;
|
line-height: 1.2;
|
||||||
padding: 1rem;
|
overflow: auto;
|
||||||
}
|
padding: 1rem;
|
||||||
</style>
|
}
|
||||||
|
</style>
|
||||||
|
% endif
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="render_this_page()">
|
<%def name="render_this_page()">
|
||||||
${parent.render_this_page()}
|
${parent.render_this_page()}
|
||||||
|
|
||||||
% if master.has_perm('execute'):
|
% if expose_websockets and master.has_perm('execute'):
|
||||||
${h.form(master.get_action_url('declare_failure', instance), ref='declareFailureForm')}
|
|
||||||
${h.csrf_token(request)}
|
|
||||||
${h.end_form()}
|
|
||||||
% endif
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
<%def name="render_buefy_form()">
|
|
||||||
<div class="form">
|
|
||||||
<${form.component}
|
|
||||||
% if master.has_perm('execute'):
|
|
||||||
@declare-failure="declareFailure"
|
|
||||||
% endif
|
|
||||||
>
|
|
||||||
</${form.component}>
|
|
||||||
</div>
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
<%def name="render_form_buttons()">
|
|
||||||
% if not instance.executed and instance.status_code == enum.UPGRADE_STATUS_PENDING and master.has_perm('execute'):
|
|
||||||
<div class="buttons">
|
|
||||||
% if instance.enabled and not instance.executing:
|
|
||||||
% if use_buefy and expose_websockets:
|
|
||||||
<b-button type="is-primary"
|
|
||||||
icon-pack="fas"
|
|
||||||
icon-left="arrow-circle-right"
|
|
||||||
:disabled="upgradeExecuting"
|
|
||||||
@click="executeUpgrade()">
|
|
||||||
{{ upgradeExecuting ? "Working, please wait..." : "Execute this upgrade" }}
|
|
||||||
</b-button>
|
|
||||||
% elif use_buefy:
|
|
||||||
${h.form(url('{}.execute'.format(route_prefix), uuid=instance.uuid), **{'@submit': 'submitForm'})}
|
|
||||||
${h.csrf_token(request)}
|
|
||||||
<b-button type="is-primary"
|
|
||||||
native-type="submit"
|
|
||||||
icon-pack="fas"
|
|
||||||
icon-left="arrow-circle-right"
|
|
||||||
:disabled="formSubmitting">
|
|
||||||
{{ formSubmitting ? "Working, please wait..." : "Execute this upgrade" }}
|
|
||||||
</b-button>
|
|
||||||
${h.end_form()}
|
|
||||||
% else:
|
|
||||||
${h.form(url('{}.execute'.format(route_prefix), uuid=instance.uuid), class_='autodisable')}
|
|
||||||
${h.csrf_token(request)}
|
|
||||||
${h.submit('execute', "Execute this upgrade", class_='button is-primary')}
|
|
||||||
${h.end_form()}
|
|
||||||
% endif
|
|
||||||
% elif instance.enabled:
|
|
||||||
<button type="button" class="button is-primary" disabled="disabled" title="This upgrade is currently executing">Execute this upgrade</button>
|
|
||||||
% else:
|
|
||||||
<button type="button" class="button is-primary" disabled="disabled" title="This upgrade is not enabled">Execute this upgrade</button>
|
|
||||||
% endif
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<b-modal :active.sync="upgradeExecuting"
|
<b-modal :active.sync="upgradeExecuting"
|
||||||
full-screen
|
full-screen
|
||||||
:can-cancel="false">
|
:can-cancel="false">
|
||||||
|
@ -116,12 +65,15 @@
|
||||||
<div class="level">
|
<div class="level">
|
||||||
<div class="level-item has-text-centered"
|
<div class="level-item has-text-centered"
|
||||||
style="display: flex; flex-direction: column;">
|
style="display: flex; flex-direction: column;">
|
||||||
<p class="block">Upgrading (please wait) ...</p>
|
<p class="block">
|
||||||
|
Upgrading (please wait) ...
|
||||||
|
{{ executeUpgradeComplete ? "DONE!" : "" }}
|
||||||
|
</p>
|
||||||
<b-progress size="is-large"
|
<b-progress size="is-large"
|
||||||
style="width: 400px;"
|
style="width: 400px;"
|
||||||
## :value="80"
|
## :value="80"
|
||||||
## show-value
|
## show-value
|
||||||
## format="percent"
|
## format="percent"
|
||||||
>
|
>
|
||||||
</b-progress>
|
</b-progress>
|
||||||
</div>
|
</div>
|
||||||
|
@ -151,7 +103,64 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</b-modal>
|
</b-modal>
|
||||||
|
% endif
|
||||||
|
|
||||||
|
% if master.has_perm('execute'):
|
||||||
|
${h.form(master.get_action_url('declare_failure', instance), ref='declareFailureForm')}
|
||||||
|
${h.csrf_token(request)}
|
||||||
|
${h.end_form()}
|
||||||
|
% endif
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="render_buefy_form()">
|
||||||
|
<div class="form">
|
||||||
|
<${form.component}
|
||||||
|
% if expose_websockets and master.has_perm('execute'):
|
||||||
|
@execute-upgrade-click="executeUpgrade"
|
||||||
|
:upgrade-executing="upgradeExecuting"
|
||||||
|
@declare-failure-click="declareFailureClick"
|
||||||
|
:declare-failure-submitting="declareFailureSubmitting"
|
||||||
|
% endif
|
||||||
|
>
|
||||||
|
</${form.component}>
|
||||||
|
</div>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="render_form_buttons()">
|
||||||
|
% if instance_executable and master.has_perm('execute'):
|
||||||
|
<div class="buttons">
|
||||||
|
% if instance.enabled and not instance.executing:
|
||||||
|
% if use_buefy and expose_websockets:
|
||||||
|
<b-button type="is-primary"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="arrow-circle-right"
|
||||||
|
:disabled="upgradeExecuting"
|
||||||
|
@click="$emit('execute-upgrade-click')">
|
||||||
|
{{ upgradeExecuting ? "Working, please wait..." : "Execute this upgrade" }}
|
||||||
|
</b-button>
|
||||||
|
% elif use_buefy:
|
||||||
|
${h.form(url('{}.execute'.format(route_prefix), uuid=instance.uuid), **{'@submit': 'submitForm'})}
|
||||||
|
${h.csrf_token(request)}
|
||||||
|
<b-button type="is-primary"
|
||||||
|
native-type="submit"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="arrow-circle-right"
|
||||||
|
:disabled="formSubmitting">
|
||||||
|
{{ formSubmitting ? "Working, please wait..." : "Execute this upgrade" }}
|
||||||
|
</b-button>
|
||||||
|
${h.end_form()}
|
||||||
|
% else:
|
||||||
|
${h.form(url('{}.execute'.format(route_prefix), uuid=instance.uuid), class_='autodisable')}
|
||||||
|
${h.csrf_token(request)}
|
||||||
|
${h.submit('execute', "Execute this upgrade", class_='button is-primary')}
|
||||||
|
${h.end_form()}
|
||||||
|
% endif
|
||||||
|
% elif instance.enabled:
|
||||||
|
<button type="button" class="button is-primary" disabled="disabled" title="This upgrade is currently executing">Execute this upgrade</button>
|
||||||
|
% else:
|
||||||
|
<button type="button" class="button is-primary" disabled="disabled" title="This upgrade is not enabled">Execute this upgrade</button>
|
||||||
|
% endif
|
||||||
|
</div>
|
||||||
% endif
|
% endif
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
@ -165,69 +174,111 @@
|
||||||
|
|
||||||
% if expose_websockets:
|
% if expose_websockets:
|
||||||
|
|
||||||
TailboneFormData.upgradeExecuting = ${json.dumps(instance.executing)|n}
|
ThisPageData.ws = null
|
||||||
TailboneFormData.progressOutput = []
|
|
||||||
TailboneFormData.progressOutputCounter = 0
|
|
||||||
|
|
||||||
TailboneForm.methods.executeUpgrade = function() {
|
//////////////////////////////
|
||||||
this.upgradeExecuting = true
|
// execute upgrade
|
||||||
|
//////////////////////////////
|
||||||
|
|
||||||
|
TailboneForm.props.upgradeExecuting = {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
ThisPageData.upgradeExecuting = false
|
||||||
|
ThisPageData.progressOutput = []
|
||||||
|
ThisPageData.progressOutputCounter = 0
|
||||||
|
ThisPageData.executeUpgradeComplete = false
|
||||||
|
|
||||||
|
ThisPage.methods.adjustTextoutHeight = function() {
|
||||||
|
|
||||||
// grow the textout area to fill most of screen
|
// grow the textout area to fill most of screen
|
||||||
|
let textout = this.$refs.textout
|
||||||
|
let height = window.innerHeight - textout.offsetTop - 50
|
||||||
|
textout.style.height = height + 'px'
|
||||||
|
}
|
||||||
|
|
||||||
|
ThisPage.methods.showExecuteDialog = function() {
|
||||||
|
this.upgradeExecuting = true
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
let textout = this.$refs.textout
|
this.adjustTextoutHeight()
|
||||||
let height = window.innerHeight - textout.offsetTop - 50
|
|
||||||
textout.style.height = height + 'px'
|
|
||||||
})
|
|
||||||
|
|
||||||
let url = '${master.get_action_url('execute', instance)}'
|
|
||||||
this.submitForm(url, {ws: true}, response => {
|
|
||||||
|
|
||||||
## TODO: should be a cleaner way to get this url?
|
|
||||||
url = '${request.route_url('ws.upgrades.execution_progress', _query={'uuid': instance.uuid})}'
|
|
||||||
url = url.replace(/^https?:/, 'wss:')
|
|
||||||
|
|
||||||
this.ws = new WebSocket(url)
|
|
||||||
let that = this
|
|
||||||
|
|
||||||
## TODO: add support for this here?
|
|
||||||
// this.ws.onclose = (event) => {
|
|
||||||
// // websocket closing means 1 of 2 things:
|
|
||||||
// // - user navigated away from page intentionally
|
|
||||||
// // - server connection was broken somehow
|
|
||||||
// // only one of those is "bad" and we only want to
|
|
||||||
// // display warning in 2nd case. so we simply use a
|
|
||||||
// // brief delay to "rule out" the 1st scenario
|
|
||||||
// setTimeout(() => { that.websocketBroken = true },
|
|
||||||
// 3000)
|
|
||||||
// }
|
|
||||||
|
|
||||||
this.ws.onmessage = (event) => {
|
|
||||||
let data = JSON.parse(event.data)
|
|
||||||
|
|
||||||
if (data.complete) {
|
|
||||||
|
|
||||||
// upgrade has completed; reload page to view result
|
|
||||||
location.reload()
|
|
||||||
|
|
||||||
} else if (data.stdout) {
|
|
||||||
|
|
||||||
// add lines to textout area
|
|
||||||
that.progressOutput.push({
|
|
||||||
key: ++that.progressOutputCounter,
|
|
||||||
text: data.stdout})
|
|
||||||
|
|
||||||
// scroll down to end of textout area
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$refs.seeme.scrollIntoView()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThisPage.methods.establishWebsocket = function() {
|
||||||
|
|
||||||
|
## TODO: should be a cleaner way to get this url?
|
||||||
|
url = '${request.route_url('ws.upgrades.execution_progress', _query={'uuid': instance.uuid})}'
|
||||||
|
url = url.replace(/^https?:/, 'wss:')
|
||||||
|
|
||||||
|
this.ws = new WebSocket(url)
|
||||||
|
|
||||||
|
## TODO: add support for this here?
|
||||||
|
// this.ws.onclose = (event) => {
|
||||||
|
// // websocket closing means 1 of 2 things:
|
||||||
|
// // - user navigated away from page intentionally
|
||||||
|
// // - server connection was broken somehow
|
||||||
|
// // only one of those is "bad" and we only want to
|
||||||
|
// // display warning in 2nd case. so we simply use a
|
||||||
|
// // brief delay to "rule out" the 1st scenario
|
||||||
|
// setTimeout(() => { that.websocketBroken = true },
|
||||||
|
// 3000)
|
||||||
|
// }
|
||||||
|
|
||||||
|
this.ws.onmessage = (event) => {
|
||||||
|
let data = JSON.parse(event.data)
|
||||||
|
|
||||||
|
if (data.complete) {
|
||||||
|
|
||||||
|
// upgrade has completed; reload page to view result
|
||||||
|
this.executeUpgradeComplete = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
location.reload()
|
||||||
|
})
|
||||||
|
|
||||||
|
} else if (data.stdout) {
|
||||||
|
|
||||||
|
// add lines to textout area
|
||||||
|
this.progressOutput.push({
|
||||||
|
key: ++this.progressOutputCounter,
|
||||||
|
text: data.stdout})
|
||||||
|
|
||||||
|
// scroll down to end of textout area
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.seeme.scrollIntoView()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
% if instance.executing:
|
||||||
|
ThisPage.mounted = function() {
|
||||||
|
this.showExecuteDialog()
|
||||||
|
this.establishWebsocket()
|
||||||
|
}
|
||||||
|
% endif
|
||||||
|
|
||||||
|
% if instance_executable:
|
||||||
|
|
||||||
|
ThisPage.methods.executeUpgrade = function() {
|
||||||
|
this.showExecuteDialog()
|
||||||
|
|
||||||
|
let url = '${master.get_action_url('execute', instance)}'
|
||||||
|
this.submitForm(url, {ws: true}, response => {
|
||||||
|
|
||||||
|
this.establishWebsocket()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
% endif
|
||||||
|
|
||||||
% else:
|
% else:
|
||||||
## no websockets
|
## no websockets
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
// execute upgrade
|
||||||
|
//////////////////////////////
|
||||||
|
|
||||||
TailboneFormData.formSubmitting = false
|
TailboneFormData.formSubmitting = false
|
||||||
|
|
||||||
TailboneForm.methods.submitForm = function() {
|
TailboneForm.methods.submitForm = function() {
|
||||||
|
@ -236,17 +287,26 @@
|
||||||
|
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
TailboneFormData.declareFailureSubmitting = false
|
//////////////////////////////
|
||||||
|
// declare failure
|
||||||
|
//////////////////////////////
|
||||||
|
|
||||||
TailboneForm.methods.declareFailureClick = function() {
|
TailboneForm.props.declareFailureSubmitting = {
|
||||||
if (confirm("Really declare this upgrade a failure?")) {
|
type: Boolean,
|
||||||
this.declareFailureSubmitting = true
|
default: false,
|
||||||
this.$emit('declare-failure')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ThisPage.methods.declareFailure = function() {
|
TailboneForm.methods.declareFailureClick = function() {
|
||||||
this.$refs.declareFailureForm.submit()
|
this.$emit('declare-failure-click')
|
||||||
|
}
|
||||||
|
|
||||||
|
ThisPageData.declareFailureSubmitting = false
|
||||||
|
|
||||||
|
ThisPage.methods.declareFailureClick = function() {
|
||||||
|
if (confirm("Really declare this upgrade a failure?")) {
|
||||||
|
this.declareFailureSubmitting = true
|
||||||
|
this.$refs.declareFailureForm.submit()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
% endif
|
% endif
|
||||||
|
|
|
@ -1063,6 +1063,8 @@ class MasterView(View):
|
||||||
'instance_deletable': self.deletable_instance(instance),
|
'instance_deletable': self.deletable_instance(instance),
|
||||||
'form': form,
|
'form': form,
|
||||||
}
|
}
|
||||||
|
if self.executable:
|
||||||
|
context['instance_executable'] = self.executable_instance(instance)
|
||||||
if hasattr(form, 'make_deform_form'):
|
if hasattr(form, 'make_deform_form'):
|
||||||
context['dform'] = form.make_deform_form()
|
context['dform'] = form.make_deform_form()
|
||||||
|
|
||||||
|
@ -1784,6 +1786,14 @@ class MasterView(View):
|
||||||
elif importer.allow_create:
|
elif importer.allow_create:
|
||||||
return importer.create_object(key, host_data)
|
return importer.create_object(key, host_data)
|
||||||
|
|
||||||
|
def executable_instance(self, instance):
|
||||||
|
"""
|
||||||
|
Returns boolean indicating whether or not the given instance
|
||||||
|
can be considered "executable". Returns ``True`` by default;
|
||||||
|
override as necessary.
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
"""
|
"""
|
||||||
Execute an object.
|
Execute an object.
|
||||||
|
|
|
@ -475,6 +475,13 @@ class UpgradeView(MasterView):
|
||||||
# key = '{}.execute'.format(self.get_grid_key())
|
# key = '{}.execute'.format(self.get_grid_key())
|
||||||
# return SessionProgress(self.request, key, session_type='file')
|
# return SessionProgress(self.request, key, session_type='file')
|
||||||
|
|
||||||
|
def executable_instance(self, upgrade):
|
||||||
|
if upgrade.executed:
|
||||||
|
return False
|
||||||
|
if upgrade.status_code != self.enum.UPGRADE_STATUS_PENDING:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def execute_instance(self, upgrade, user, progress=None, **kwargs):
|
def execute_instance(self, upgrade, user, progress=None, **kwargs):
|
||||||
app = self.get_rattail_app()
|
app = self.get_rattail_app()
|
||||||
session = app.get_session(upgrade)
|
session = app.get_session(upgrade)
|
||||||
|
|
Loading…
Reference in a new issue