balena-cli/lib/utils/deploy-legacy.coffee
Pagan Gazzard 7c62e34455 Simplify lazy-loading of resin-cli-visuals with a shared function
This also avoids current unnecessary requiring of resin-cli-visuals
for every command

Change-type: patch
2020-02-28 15:29:07 +00:00

138 lines
3.7 KiB
CoffeeScript

Promise = require('bluebird')
{ getVisuals } = require('./lazy')
getBuilderPushEndpoint = (baseUrl, owner, app) ->
querystring = require('querystring')
args = querystring.stringify({ owner, app })
"https://builder.#{baseUrl}/v1/push?#{args}"
getBuilderLogPushEndpoint = (baseUrl, buildId, owner, app) ->
querystring = require('querystring')
args = querystring.stringify({ owner, app, buildId })
"https://builder.#{baseUrl}/v1/pushLogs?#{args}"
bufferImage = (docker, imageId, bufferFile) ->
Promise = require('bluebird')
streamUtils = require('./streams')
image = docker.getImage(imageId)
imageMetadata = image.inspect()
Promise.join image.get(), imageMetadata.get('Size'), (imageStream, imageSize) ->
streamUtils.buffer(imageStream, bufferFile)
.tap (bufferedStream) ->
bufferedStream.length = imageSize
showPushProgress = (message) ->
visuals = getVisuals()
progressBar = new visuals.Progress(message)
progressBar.update({ percentage: 0 })
return progressBar
uploadToPromise = (uploadRequest, logger) ->
new Promise (resolve, reject) ->
handleMessage = (data) ->
data = data.toString()
logger.logDebug("Received data: #{data}")
try
obj = JSON.parse(data)
catch e
logger.logError('Error parsing reply from remote side')
reject(e)
return
switch obj.type
when 'error' then reject(new Error("Remote error: #{obj.error}"))
when 'success' then resolve(obj)
when 'status' then logger.logInfo(obj.message)
else reject(new Error("Received unexpected reply from remote: #{data}"))
uploadRequest
.on('error', reject)
.on('data', handleMessage)
uploadImage = (imageStream, token, username, url, appName, logger) ->
request = require('request')
progressStream = require('progress-stream')
zlib = require('zlib')
# Need to strip off the newline
progressMessage = logger.formatMessage('info', 'Uploading').slice(0, -1)
progressBar = showPushProgress(progressMessage)
streamWithProgress = imageStream.pipe progressStream
time: 500,
length: imageStream.length
, ({ percentage, eta }) ->
progressBar.update
percentage: Math.min(percentage, 100)
eta: eta
uploadRequest = request.post
url: getBuilderPushEndpoint(url, username, appName)
headers:
'Content-Encoding': 'gzip'
auth:
bearer: token
body: streamWithProgress.pipe(zlib.createGzip({
level: 6
}))
uploadToPromise(uploadRequest, logger)
uploadLogs = (logs, token, url, buildId, username, appName) ->
request = require('request')
request.post
json: true
url: getBuilderLogPushEndpoint(url, buildId, username, appName)
auth:
bearer: token
body: Buffer.from(logs)
###
opts must be a hash with the following keys:
- appName: the name of the app to deploy to
- imageName: the name of the image to deploy
- buildLogs: a string with build output
- shouldUploadLogs
###
module.exports = (docker, logger, token, username, url, opts) ->
tmp = require('tmp')
tmpNameAsync = Promise.promisify(tmp.tmpName)
# Ensure the tmp files gets deleted
tmp.setGracefulCleanup()
{ appName, imageName, buildLogs, shouldUploadLogs } = opts
logs = buildLogs
tmpNameAsync()
.then (bufferFile) ->
logger.logInfo('Initializing deploy...')
bufferImage(docker, imageName, bufferFile)
.then (stream) ->
uploadImage(stream, token, username, url, appName, logger)
.finally ->
# If the file was never written to (for instance because an error
# has occured before any data was written) this call will throw an
# ugly error, just suppress it
Promise.try ->
require('mz/fs').unlink(bufferFile)
.catchReturn()
.tap ({ buildId }) ->
return if not shouldUploadLogs
logger.logInfo('Uploading logs...')
Promise.join(
logs
token
url
buildId
username
appName
uploadLogs
)
.get('buildId')