Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run now dialog improvements #977

Merged
merged 29 commits into from Apr 5, 2016
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
dd62b3a
Merge branch 'master' into run-now-dialog-improvements
Mar 24, 2016
ad41a24
Run now dialog options UI implemented but they don't yet do anything
Mar 25, 2016
713a5bd
Create a runId in the UI (rather than let the API do it) in runNow di…
Mar 28, 2016
3c247cb
Fix for run now button broken for pending tasks. Also Autotailer is n…
Mar 29, 2016
ae8e2d6
Add ability to open up a task's page once it starts running. Big chan…
Mar 29, 2016
7d2f421
Merge branch 'master' into run-now-dialog-improvements
Mar 29, 2016
02cf54d
Browse to sandbox by default
Mar 29, 2016
715e18b
Clearer code for displaying 'Run' or 'Rerun'
Mar 29, 2016
741e82e
Better wording for browse to sandbox option
Mar 29, 2016
174069f
Default filename for run now is now the name of config.runningTaskLog…
Mar 29, 2016
9c8af8e
Change wording of 'stay on page' option to take out the word 'request'
Mar 30, 2016
150b47a
Change wording of 'stay on page' option to take out the word 'request'
Mar 30, 2016
0ff49d6
Fix task poller timeout constant ignored bug
Mar 30, 2016
f847347
Fix awkward wording
Mar 30, 2016
ed894db
Autopopulate command line args from last run task if not rerunning
Mar 30, 2016
a14b879
We don't need the prefix parameter since the localStorage command lin…
Mar 30, 2016
43a6966
Poll for historical tasks by runId instead of by reset event
Mar 30, 2016
096a89b
Better calculation of minutes to milliseconds for timeout; Smaller me…
Mar 31, 2016
688d659
draft of endpoint to fetch active task by runId
ssalinas Mar 31, 2016
481785f
Poll for running tasks like for completed tasks
Mar 31, 2016
2dd46b8
Detects if task has entered terminal state and will never have the co…
Mar 31, 2016
7f7e5db
No fun allowed in user messages
Mar 31, 2016
acbc6a9
Better message for task in terminal state and file not found
Mar 31, 2016
eb0c767
Bug fix
Apr 1, 2016
74c161f
Fix bug that caused file to revert to undefined after running with 'b…
Apr 1, 2016
857c495
Re-fix bug that prevented run now dialog from opening from the pendin…
Apr 1, 2016
40a0895
Revert "Re-fix bug that prevented run now dialog from opening from th…
Apr 1, 2016
3e1e910
This should actually fix the run now button on the scheduled tasks page
Apr 1, 2016
9e22341
Fix bug that was stopping autotailer from loading
Apr 4, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 4 additions & 2 deletions SingularityUI/app/controllers/Controller.coffee
@@ -1,3 +1,5 @@
Utils = require '../utils'

# Base controller to be extended by other classes
class Controller

Expand Down Expand Up @@ -33,9 +35,9 @@ class Controller
document.title = pageTitle + ' - ' + config.title

# e.g. `myModel.fetch().error @ignore404`
ignore404: (response) -> app.caughtError() if response.status is 404
ignore404: Utils.ignore404

# e.g. `myModel.fetch().error @ignore400`
ignore400: (response) -> app.caughtError() if response.status is 400
ignore400: Utils.ignore400

module.exports = Controller
2 changes: 1 addition & 1 deletion SingularityUI/app/handlebarsHelpers.coffee
Expand Up @@ -143,7 +143,7 @@ Handlebars.registerHelper 'substituteTaskId', (value, taskId) ->
value.replace('$TASK_ID', taskId)

Handlebars.registerHelper 'filename', (value) ->
value.substring(value.lastIndexOf('/') + 1)
Utils.fileName(value)

Handlebars.registerHelper 'getLabelClass', (state) ->
Utils.getLabelClassFromTaskState state
Expand Down
229 changes: 118 additions & 111 deletions SingularityUI/app/models/Request.coffee
@@ -1,6 +1,7 @@
Model = require './model'

Racks = require '../collections/Racks'
HistoricalTasks = require '../collections/HistoricalTasks'

pauseTemplate = require '../templates/vex/requestPause'
scaleTemplate = require '../templates/vex/requestScale'
Expand All @@ -13,9 +14,13 @@ exitCooldownTemplate = require '../templates/vex/exitCooldown'
stepDeployTemplate = require '../templates/vex/stepDeploy'
cancelDeployTemplate = require '../templates/vex/cancelDeploy'
TaskHistory = require '../models/TaskHistory'
TaskPoller = require '../views/TaskPoller'

Utils = require '../utils'

vex = require 'vex.dialog'
juration = require 'juration'
uuid = require 'node-uuid'

class Request extends Model

Expand Down Expand Up @@ -91,7 +96,7 @@ class Request extends Model
contentType: 'application/json'
data: JSON.stringify data

run: (cmdLineArgs, message) ->
run: (cmdLineArgs, message, runId) -> #runId will be autogenerated if not provided
options =
url: "#{ @url() }/run"
type: 'POST'
Expand All @@ -100,6 +105,8 @@ class Request extends Model

options.data.commandLineArgs = cmdLineArgs

options.data.runId = runId if runId

if message
options.data.message = message
options.data = JSON.stringify(options.data)
Expand Down Expand Up @@ -427,69 +434,121 @@ class Request extends Model
return unless confirmed
@unpause(confirmed).done callback

promptRun: (callback) =>
try
lastCommands = JSON.parse(localStorage.getItem(@localStorageCommandLineInputKeyPrefix + @id))
catch e
console.error('Could not parse previous commands JSON')
lastCommands = []
vex.dialog.prompt
message: "<h3>Run Task</h3>"
input: runTemplate
id: @get "id"
prefix: @localStorageCommandLineInputKeyPrefix
commands: if lastCommands? then lastCommands[lastCommands.length - 1] else []
getMostRecentlyRunTask: (callback) =>
@commands = []
historicalTasks = new HistoricalTasks [],
params:
requestId: @get "id"
historicalTasksFetch = historicalTasks.fetch()
historicalTasksFetch.success () =>
if historicalTasks.models.length
taskId = historicalTasks.models[0].attributes.taskId.id
task = new TaskHistory {taskId}
taskFetch = task.fetch()
taskFetch.success () =>
@commands = task.attributes.task.taskRequest.pendingTask.cmdLineArgsList
callback()
taskFetch.error () =>
app.caughtError() # Don't actually error if we can't find previous args, just don't populate
callback()
else
callback() # If this is the first task being run for this reqeust
historicalTasksFetch.error () =>
app.caughtError()
callback() # Don't actually error if we can't find previous args, just don't populate

promptRun: (callback, task) => #task is an optional parameter - if it's provided this will rerun it, else this will run a new task
showDialog = () =>
if task
commands = task.attributes.task.taskRequest.pendingTask.cmdLineArgsList
else
commands = @commands
vex.dialog.prompt
message: "<h3>#{if task then 'Rerun' else 'Run'} Task</h3>"
input: runTemplate
id: @get "id"
commands: commands
defaultFileName: Utils.fileName(config.runningTaskLogPath)

buttons: [
$.extend _.clone(vex.dialog.buttons.YES), text: 'Run now'
vex.dialog.buttons.NO
]
buttons: [
$.extend _.clone(vex.dialog.buttons.YES), text: 'Run now'
vex.dialog.buttons.NO
]

beforeClose: =>
return if @data is false

fileName = @data.filename.trim()
message = @data.message

if ((fileName and fileName.length is 0) or not fileName) and @data.autoTail is 'on'
$(window.noFilenameError).removeClass('hide')
return false

else
if @data.commandLineInput?
try
history = JSON.parse(localStorage.getItem(@localStorageCommandLineInputKeyPrefix + @id))
catch e
console.error('Could not parse previous command history')
history = []
if history and @data.commandLineInput != history[-1]
history.push(@data.commandLineInput)
localStorage.setItem(@localStorageCommandLineInputKeyPrefix + @id, JSON.stringify(history))
localStorage.setItem('taskRunRedirectFilename', fileName) if filename?
localStorage.setItem('taskRunAutoTail', @data.autoTail)
@data.id = @get 'id'

@run( @data.commandLineInput, message ).done callback( @data )
return true

afterOpen: =>
$('#filename').val localStorage.getItem('taskRunRedirectFilename')
$('#autoTail').prop 'checked', (localStorage.getItem('taskRunAutoTail') is 'on')
$('#add-cmd-line-arg').on('click', { removeCmdLineArg: @removeCmdLineArg }, @addCmdLineArg)
$('.remove-button').click @removeCmdLineArg
beforeClose: =>
return if @data is false

callback: (data) =>
if data.commandLineInput
if typeof data.commandLineInput is 'string'
if data.commandLineInput != ''
data.commandLineInput = [data.commandLineInput.trim()]
else
fileName = @data.filename.trim()
message = @data.message

if ((fileName and fileName.length is 0) or not fileName) and @data.afterStart is 'autoTail'
# Error - what file are we supposed to tail if none is provided?
$(window.noFilenameError).removeClass('hide')
return false

else
localStorage.setItem('taskRunRedirectFilename', fileName) if filename?
localStorage.setItem('taskRunAfterStart', @data.afterStart)
@data.id = @get 'id'

if @data.afterStart in ['browse-to-sandbox', 'autoTail']
@data.runId = uuid.v4()

doneFn = =>
if @data.afterStart is 'autoTail'
taskPoller = new TaskPoller({
runId: @data.runId
requestId: @id
autoTailFilename: @data.filename
taskPollTimestamp: +new Date()
pollingType: 'autoTail'
})

taskPoller.startTaskPolling()
else if @data.afterStart is 'browse-to-sandbox'
taskPoller = new TaskPoller({
runId: @data.runId
requestId: @id
taskPollTimestamp: +new Date()
pollingType: 'browse-to-sandbox'
})

taskPoller.startTaskPolling()

callback( @data )

@run( @data.commandLineInput, message, @data.runId ).done doneFn
return true

afterOpen: =>
taskRunAfterStart = localStorage.getItem('taskRunAfterStart')
$('#filename').val localStorage.getItem('taskRunRedirectFilename') or Utils.fileName(config.runningTaskLogPath)
$('#autoTail').prop 'checked', (taskRunAfterStart is 'autoTail')
$('#browse-to-sandbox').prop 'checked', (taskRunAfterStart is 'browse-to-sandbox' or not taskRunAfterStart)
$('#stay-on-page').prop 'checked', (taskRunAfterStart is 'stay-on-page')
$('#add-cmd-line-arg').on('click', { removeCmdLineArg: @removeCmdLineArg }, @addCmdLineArg)
$('.remove-button').click @removeCmdLineArg
$('#stay-on-page').on('click', () => $('#filename').addClass('hide'))
$('#browse-to-sandbox').on('click', () => $('#filename').addClass('hide'))
$('#autoTail').on('click', () => $('#filename').removeClass('hide'))
$('#filename').removeClass('hide') if taskRunAfterStart is 'autoTail'

callback: (data) =>
if data.commandLineInput
if typeof data.commandLineInput is 'string'
if data.commandLineInput != ''
data.commandLineInput = [data.commandLineInput.trim()]
else
data.commandLineInput = []
if data.commandLineInput.length == 1 and data.commandLineInput[0] == ''
data.commandLineInput = []
if data.commandLineInput.length == 1 and data.commandLineInput[0] == ''
else
data.commandLineInput = []
else
data.commandLineInput = []
@data = data
@data = data
if task
showDialog()
else
@getMostRecentlyRunTask showDialog

addCmdLineArg: (event) ->
event.preventDefault()
Expand All @@ -506,58 +565,6 @@ class Request extends Model
event.preventDefault()
$(event.currentTarget).parent().remove()

promptRerun: (taskId, callback) =>
task = new TaskHistory {taskId}
task.fetch()
.done =>
commands = task.attributes.task.taskRequest.pendingTask.cmdLineArgsList
vex.dialog.prompt
message: "<h3>Rerun Task</h3>"
input: runTemplate
id: @get "id"
commands: commands
buttons: [
$.extend _.clone(vex.dialog.buttons.YES), text: 'Run now'
vex.dialog.buttons.NO
]

beforeClose: =>
return if @data is false

fileName = @data.filename.trim()
message = @data.message

if ((fileName and fileName.length is 0) or not fileName) and @data.autoTail is 'on'
$(window.noFilenameError).removeClass('hide')
return false

else
localStorage.setItem('taskRunRedirectFilename', fileName) if filename?
localStorage.setItem('taskRunAutoTail', @data.autoTail)
@data.id = @get 'id'

@run( @data.commandLineInput, message ).done callback( @data )
return true

afterOpen: =>
$('#filename').val localStorage.getItem('taskRunRedirectFilename')
$('#autoTail').prop 'checked', (localStorage.getItem('taskRunAutoTail') is 'on')
$('#add-cmd-line-arg').on('click', { removeCmdLineArg: @removeCmdLineArg }, @addCmdLineArg)
$('.remove-button').click @removeCmdLineArg

callback: (data) =>
if data.commandLineInput
if typeof data.commandLineInput is 'string'
if data.commandLineInput != ''
data.commandLineInput = [data.commandLineInput.trim()]
else
data.commandLineInput = []
if data.commandLineInput.length == 1 and data.commandLineInput[0] == ''
data.commandLineInput = []
else
data.commandLineInput = []
@data = data

promptRemove: (callback) =>
vex.dialog.confirm
message: removeTemplate id: @get "id"
Expand Down
7 changes: 6 additions & 1 deletion SingularityUI/app/models/Task.coffee
Expand Up @@ -63,7 +63,12 @@ class Task extends Model

promptRun: (callback) =>
# We tell the Request to run
requestModel = new Request id: @get('request').id
request = @get('request')
if request
id = request.id
else # Pending tasks don't have a request attribute
id = @get('pendingTask').pendingTaskId.requestId
requestModel = new Request id: id
requestModel.promptRun => callback()


Expand Down
14 changes: 13 additions & 1 deletion SingularityUI/app/models/TaskHistoryItem.coffee
Expand Up @@ -6,7 +6,19 @@ class TaskHistoryItem extends Model

ignoreAttributes: ['id', 'canBeRunNow']

initialize: ({ @taskId, @updatedAt, @lastTaskState }) ->
url: () ->
if @requestId and @runId
"#{ config.apiRoot }/history/request/#{ @requestId }/run/#{ @runId }"
else
# Currently the above URL is the ONLY place to fetch this model.
# If you don't have access to request ID and run ID use the TaskHistory
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

once we have the taskId, we can also fetch this using that. I know the main use case with be by runId, but just wanted to clarify

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you fetch by taskId, you get a different model as a response. That gives back a SingularityTaskHistory (which is the TaskHistory model in the UI), wheras searching by runId returns a SingularityTaskIdHistory (Which is this model in the UI). In a roundabout way, you can use Task Search to search out this model by taskId, but then you'd be fetching the collection rather than the model.

screenshot 2016-03-31 09 07 55
screenshot 2016-03-31 09 10 06

# model instead.
throw new Error """
Insufficient data for a meaningful answer.
Cannot fetch individual TaskHistoryItem without #{'requestId' unless @requestId}#{if @requestId or @runId then '' else ' and '}#{if @runId then '' else 'runId'}.
"""

initialize: ({ @taskId, @updatedAt, @lastTaskState, @requestId, @runId }) ->


module.exports = TaskHistoryItem
8 changes: 4 additions & 4 deletions SingularityUI/app/styles/main.styl
Expand Up @@ -99,15 +99,15 @@ code
fieldset
display table-cell

.auto-tail-checklist
.wait-for-checklist
margin-top 10px
.auto-tail-task-start
.wait-for-task-start
font-weight bolder

&.waiting-for-file
.auto-tail-task-start
.wait-for-task-start
font-weight normal
.auto-tail-file-exists
.wait-for-file-exists
font-weight bolder

.vex.vex-theme-default .vex-dialog-form .vex-dialog-input.expiration-input
Expand Down
2 changes: 0 additions & 2 deletions SingularityUI/app/templates/vex/autoTailingFailure.hbs

This file was deleted.

6 changes: 0 additions & 6 deletions SingularityUI/app/templates/vex/autoTailingWaiting.hbs

This file was deleted.

10 changes: 7 additions & 3 deletions SingularityUI/app/templates/vex/requestRun.hbs
Expand Up @@ -24,9 +24,13 @@
<input name="message" id="run-message" type="text" />
</div>
<hr class='border-color-light-gray'>

<p><label class='label-light'><input id="autoTail" name="autoTail" type="checkbox"> Wait for task to start, then start tailing:</label></p>
<strong>After triggering the run:</strong>
<p>
<label class='label-light'><input id="stay-on-page" name="afterStart" type="radio" value="stay-on-page"> Stay on this page</label>
<label class='label-light'><input id="browse-to-sandbox" name="afterStart" type="radio" value="browse-to-sandbox"> Wait for task to start, then browse its sandbox</label>
<label class='label-light'><input id="autoTail" name="afterStart" type="radio" value="autoTail"> Wait for task to start, then start tailing</label>
</p>
<div id="noFilenameError" class="alert alert-warning hide" role="alert">Please enter a filename or uncheck the box</div>
<div class="vex-dialog-input">
<input id='filename' name="filename" type="text" class="vex-dialog-prompt-input" placeholder="e.g. service.log" value="">
<input id='filename' name="filename" type="text" class="vex-dialog-prompt-input hide" placeholder="e.g. {{ defaultFileName }}" value="">
</div>
6 changes: 6 additions & 0 deletions SingularityUI/app/templates/vex/taskPollerWaiting.hbs
@@ -0,0 +1,6 @@
<h3>Launching</h3>
<ol class="task-poller-checklist wait-for-checklist">
<li class="wait-for-task-start">Waiting for task to launch</li>
<li class="wait-for-file-exists">Waiting for <code>{{ autoTailFilename }}</code> to exist</li>
</ol>
<div class='page-loader centered cushy'></div>
2 changes: 2 additions & 0 deletions SingularityUI/app/templates/vex/taskPollingFailure.hbs
@@ -0,0 +1,2 @@
<h3>Failure</h3>
{{#if autoTailFilename}}<code>{{ autoTailFilename }}</code>{{else}}Task{{/if}} did not exist after {{ timeout }} minute(s).