Use Promise Disposer for lock, + cleanup

This commit is contained in:
Pablo Carranza Vélez 2015-08-10 17:46:30 +00:00
parent 685af77007
commit c2496d30c2
4 changed files with 37 additions and 42 deletions

View File

@ -22,7 +22,7 @@ func TestPurge(t *testing.T) {
if err = os.MkdirAll(dataPath, 0755); err != nil { if err = os.MkdirAll(dataPath, 0755); err != nil {
t.Fatal("Could not create test directory for purge") t.Fatal("Could not create test directory for purge")
} else if err = ioutil.WriteFile(dataPath+"/test", []byte("test"), 777); err != nil { } else if err = ioutil.WriteFile(dataPath+"/test", []byte("test"), 0777); err != nil {
t.Fatal("Could not create test file for purge") t.Fatal("Could not create test file for purge")
} }

View File

@ -42,31 +42,29 @@ func TestPurge(t *testing.T) {
appId := config.ApplicationId appId := config.ApplicationId
dataPath := "/resin-data/" + appId dataPath := "/resin-data/" + appId
if err := ioutil.WriteFile(dataPath+"/test", []byte("test"), 777); err != nil { if err := ioutil.WriteFile(dataPath+"/test", []byte("test"), 0777); err != nil {
t.Fatal("Could not create test file for purge") t.Fatal("Could not create test file for purge")
} else if request, err := http.NewRequest("POST", supervisorAddress+"/v1/purge?apikey=bananas", strings.NewReader(`{"appId": "`+appId+`"}`)); err != nil { } else if request, err := http.NewRequest("POST", supervisorAddress+"/v1/purge?apikey=bananas", strings.NewReader(`{"appId": "`+appId+`"}`)); err != nil {
t.Fatal(err) t.Fatal(err)
} else { } else {
request.Header.Set("Content-Type", "application/json") request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if response, err := http.DefaultClient.Do(request); err != nil { defer response.Body.Close()
if err != nil {
t.Fatal(err) t.Fatal(err)
} else if response.StatusCode != http.StatusOK { } else if response.StatusCode != http.StatusOK {
t.Errorf("Expected 200, got %d", response.StatusCode) t.Errorf("Expected 200, got %d", response.StatusCode)
defer response.Body.Close()
if contents, err := ioutil.ReadAll(response.Body); err != nil { if contents, err := ioutil.ReadAll(response.Body); err != nil {
t.Fatal(err) t.Fatal(err)
} else { } else {
t.Fatalf("Response: %s", contents) t.Fatalf("Response: %s", contents)
} }
} else { } else {
defer response.Body.Close()
if contents, err := ioutil.ReadAll(response.Body); err != nil { if contents, err := ioutil.ReadAll(response.Body); err != nil {
t.Fatal(err) t.Fatal(err)
} else if !strings.EqualFold(string(contents), `{"Status":"OK","Error":""}`) { } else if !strings.EqualFold(string(contents), `{"Status":"OK","Error":""}`) {
t.Errorf("Purge response didn't match the expected JSON, got: %s", contents) t.Errorf("Purge response didn't match the expected JSON, got: %s", contents)
} }
if dirContents, err := ioutil.ReadDir(dataPath); err != nil { if dirContents, err := ioutil.ReadDir(dataPath); err != nil {
t.Errorf("Could not read the data path after purge: %s", err) t.Errorf("Could not read the data path after purge: %s", err)
} else if len(dirContents) > 0 { } else if len(dirContents) > 0 {

View File

@ -67,21 +67,20 @@ module.exports = (secret) ->
utils.mixpanelTrack('Purge /data', appId) utils.mixpanelTrack('Purge /data', appId)
if !appId? if !appId?
return res.status(400).send('Missing app id') return res.status(400).send('Missing app id')
app = null
knex('app').select().where({ appId }) knex('app').select().where({ appId })
.then ([ appFromDB ]) -> .then ([ app ]) ->
if !appFromDB? if !app?
throw new Error('App not found') throw new Error('App not found')
app = appFromDB Promise.using application.lockUpdates(), ->
application.lockUpdatesAsync() application.kill(app)
.tap ->
application.kill(app)
.then (release) ->
request.post config.gosuperAddress + '/v1/purge', { json: true, body: applicationId: appId }, ->
application.start(app)
.then -> .then ->
release() new Promise (resolve, reject) ->
.pipe(res) request.post(config.gosuperAddress + '/v1/purge', { json: true, body: applicationId: appId })
.on 'error', reject
.on 'response', -> resolve()
.pipe(res)
.finally ->
application.start(app)
.catch (err) -> .catch (err) ->
res.status(503).send(err?.message or err or 'Unknown error') res.status(503).send(err?.message or err or 'Unknown error')

View File

@ -242,9 +242,10 @@ getEnvironment = do ->
console.error("Failed to get environment for device #{deviceId}, app #{appId}. #{err}") console.error("Failed to get environment for device #{deviceId}, app #{appId}. #{err}")
throw err throw err
lock = new Lock() exports.lockUpdates = lockUpdates = do ->
exports.lockUpdates = lockUpdates = lock.async.writeLock _lock = new Lock()
exports.lockUpdatesAsync = lockUpdatesAsync = Promise.promisify(lockUpdates) _lockUpdates = Promise.promisify(_lock.async.writeLock)
return -> _lockUpdates().disposer (release) -> release()
# 0 - Idle # 0 - Idle
# 1 - Updating # 1 - Updating
@ -325,27 +326,24 @@ exports.update = update = ->
app = remoteApps[imageId] app = remoteApps[imageId]
fetch(app) fetch(app)
.then -> .then ->
lockUpdatesAsync() Promise.using lockUpdates(), ->
.tap -> # Then delete all the ones to remove in one go
# Then delete all the ones to remove in one go Promise.map toBeRemoved, (imageId) ->
Promise.map toBeRemoved, (imageId) -> kill(apps[imageId])
kill(apps[imageId])
.tap ->
# Then install the apps and add each to the db as they succeed
installingPromises = toBeInstalled.map (imageId) ->
app = remoteApps[imageId]
start(app)
# And remove/recreate updated apps and update db as they succeed
updatingPromises = toBeUpdated.map (imageId) ->
localApp = apps[imageId]
app = remoteApps[imageId]
logSystemEvent(logTypes.updateApp, app)
kill(localApp)
.then -> .then ->
start(app) # Then install the apps and add each to the db as they succeed
Promise.all(installingPromises.concat(updatingPromises)) installingPromises = toBeInstalled.map (imageId) ->
.then (release) -> app = remoteApps[imageId]
release() start(app)
# And remove/recreate updated apps and update db as they succeed
updatingPromises = toBeUpdated.map (imageId) ->
localApp = apps[imageId]
app = remoteApps[imageId]
logSystemEvent(logTypes.updateApp, app)
kill(localApp)
.then ->
start(app)
Promise.all(installingPromises.concat(updatingPromises))
.then -> .then ->
failedUpdates = 0 failedUpdates = 0
# We cleanup here as we want a point when we have a consistent apps/images state, rather than potentially at a # We cleanup here as we want a point when we have a consistent apps/images state, rather than potentially at a