2017-03-29 11:03:40 +00:00
|
|
|
Promise = require('bluebird')
|
2017-04-24 19:05:18 +00:00
|
|
|
dockerUtils = require('../utils/docker')
|
2017-03-29 11:03:40 +00:00
|
|
|
|
|
|
|
getBuilderPushEndpoint = (baseUrl, owner, app) ->
|
|
|
|
escOwner = encodeURIComponent(owner)
|
|
|
|
escApp = encodeURIComponent(app)
|
|
|
|
"https://builder.#{baseUrl}/v1/push?owner=#{escOwner}&app=#{escApp}"
|
|
|
|
|
|
|
|
formatImageName = (image) ->
|
|
|
|
image.split('/').pop()
|
|
|
|
|
|
|
|
parseInput = Promise.method (params, options) ->
|
|
|
|
if not params.appName?
|
|
|
|
throw new Error('Need an application to deploy to!')
|
|
|
|
appName = params.appName
|
|
|
|
image = undefined
|
|
|
|
if params.image?
|
|
|
|
if options.build or options.source?
|
|
|
|
throw new Error('Build and source parameters are not applicable when specifying an image')
|
|
|
|
options.build = false
|
|
|
|
image = params.image
|
|
|
|
else if options.build
|
2017-04-24 14:55:54 +00:00
|
|
|
source = options.source || '.'
|
2017-03-29 11:03:40 +00:00
|
|
|
else
|
|
|
|
throw new Error('Need either an image or a build flag!')
|
|
|
|
|
2017-04-24 14:55:54 +00:00
|
|
|
return [appName, options.build, source, image]
|
2017-03-29 11:03:40 +00:00
|
|
|
|
2017-04-24 12:48:43 +00:00
|
|
|
pushProgress = (imageSize, request, logStreams, timeout = 250) ->
|
|
|
|
logging = require('../utils/logging')
|
|
|
|
ansiEscapes = require('ansi-escapes')
|
|
|
|
|
|
|
|
logging.logInfo(logStreams, 'Initialising...')
|
2017-03-29 11:03:40 +00:00
|
|
|
progressReporter = setInterval ->
|
|
|
|
sent = request.req.connection._bytesDispatched
|
|
|
|
percent = (sent / imageSize) * 100
|
|
|
|
if percent >= 100
|
|
|
|
clearInterval(progressReporter)
|
|
|
|
percent = 100
|
2017-04-24 12:48:43 +00:00
|
|
|
process.stdout.write(ansiEscapes.cursorUp(1))
|
2017-03-29 11:03:40 +00:00
|
|
|
process.stdout.clearLine()
|
|
|
|
process.stdout.cursorTo(0)
|
2017-04-24 12:48:43 +00:00
|
|
|
logging.logInfo(logStreams, "Uploaded #{percent.toFixed(1)}%")
|
2017-03-29 11:03:40 +00:00
|
|
|
, timeout
|
|
|
|
|
|
|
|
getBundleInfo = (options) ->
|
|
|
|
helpers = require('../utils/helpers')
|
|
|
|
|
|
|
|
helpers.getAppInfo(options.appName)
|
|
|
|
.then (app) ->
|
|
|
|
[app.arch, app.device_type]
|
|
|
|
|
2017-04-24 12:48:43 +00:00
|
|
|
performUpload = (image, token, username, url, size, appName, logStreams) ->
|
2017-03-29 11:03:40 +00:00
|
|
|
request = require('request')
|
|
|
|
url = url || process.env.RESINRC_RESIN_URL
|
|
|
|
post = request.post
|
|
|
|
url: getBuilderPushEndpoint(url, username, appName)
|
|
|
|
auth:
|
|
|
|
bearer: token
|
|
|
|
body: image
|
|
|
|
|
2017-04-24 12:48:43 +00:00
|
|
|
uploadToPromise(post, size, logStreams)
|
2017-03-29 11:03:40 +00:00
|
|
|
|
2017-04-24 12:48:43 +00:00
|
|
|
uploadToPromise = (request, size, logStreams) ->
|
|
|
|
logging = require('../utils/logging')
|
2017-03-29 11:03:40 +00:00
|
|
|
new Promise (resolve, reject) ->
|
|
|
|
|
|
|
|
handleMessage = (data) ->
|
|
|
|
data = data.toString()
|
2017-04-24 12:48:43 +00:00
|
|
|
logging.logDebug(logStreams, "Received data: #{data}")
|
2017-03-29 11:03:40 +00:00
|
|
|
|
|
|
|
obj = JSON.parse(data)
|
|
|
|
if obj.type?
|
|
|
|
switch obj.type
|
|
|
|
when 'error' then reject(new Error("Remote error: #{obj.error}"))
|
|
|
|
when 'success' then resolve(obj.image)
|
2017-04-24 12:48:43 +00:00
|
|
|
when 'status' then logging.logInfo(logStreams, "Remote: #{obj.message}")
|
2017-03-29 11:03:40 +00:00
|
|
|
else reject(new Error("Received unexpected reply from remote: #{data}"))
|
|
|
|
else
|
|
|
|
reject(new Error("Received unexpected reply from remote: #{data}"))
|
|
|
|
|
|
|
|
request
|
|
|
|
.on('error', reject)
|
|
|
|
.on('data', handleMessage)
|
|
|
|
|
|
|
|
# Set up upload reporting
|
2017-04-24 12:48:43 +00:00
|
|
|
pushProgress(size, request, logStreams)
|
2017-03-29 11:03:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
module.exports =
|
|
|
|
signature: 'deploy <appName> [image]'
|
|
|
|
description: 'Deploy a container to a resin.io application'
|
|
|
|
help: '''
|
|
|
|
Use this command to deploy and optionally build an image to an application.
|
|
|
|
|
|
|
|
Usage: deploy <appName> ([image] | --build [--source build-dir])
|
|
|
|
|
|
|
|
Note: If building with this command, all options supported by `resin build`
|
2017-04-26 12:34:40 +00:00
|
|
|
are also supported with this command.
|
2017-03-29 11:03:40 +00:00
|
|
|
|
|
|
|
Examples:
|
2017-04-26 12:34:40 +00:00
|
|
|
$ resin deploy myApp --build --source myBuildDir/
|
|
|
|
$ resin deploy myApp myApp/myImage
|
2017-03-29 11:03:40 +00:00
|
|
|
'''
|
|
|
|
permission: 'user'
|
2017-04-24 19:05:18 +00:00
|
|
|
options: dockerUtils.appendOptions [
|
2017-03-29 11:03:40 +00:00
|
|
|
{
|
|
|
|
signature: 'build'
|
|
|
|
boolean: true
|
|
|
|
description: 'Build image then deploy'
|
|
|
|
alias: 'b'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
signature: 'source'
|
|
|
|
parameter: 'source'
|
|
|
|
description: 'The source directory to use when building the image'
|
|
|
|
alias: 's'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
action: (params, options, done) ->
|
|
|
|
_ = require('lodash')
|
|
|
|
tmp = require('tmp')
|
|
|
|
tmpNameAsync = Promise.promisify(tmp.tmpName)
|
|
|
|
resin = require('resin-sdk-preconfigured')
|
|
|
|
|
2017-04-24 12:48:43 +00:00
|
|
|
logging = require('../utils/logging')
|
|
|
|
|
|
|
|
logStreams = logging.getLogStreams()
|
|
|
|
|
2017-03-29 11:03:40 +00:00
|
|
|
# Ensure the tmp files gets deleted
|
|
|
|
tmp.setGracefulCleanup()
|
|
|
|
|
|
|
|
docker = dockerUtils.getDocker(options)
|
|
|
|
# Check input parameters
|
|
|
|
parseInput(params, options)
|
2017-04-24 14:55:54 +00:00
|
|
|
.then ([appName, build, source, imageName]) ->
|
2017-03-29 11:03:40 +00:00
|
|
|
tmpNameAsync()
|
|
|
|
.then (tmpPath) ->
|
|
|
|
|
|
|
|
# Setup the build args for how the build routine expects them
|
|
|
|
options = _.assign({}, options, { appName })
|
2017-04-24 14:55:54 +00:00
|
|
|
params = _.assign({}, params, { source })
|
2017-03-29 11:03:40 +00:00
|
|
|
|
|
|
|
Promise.try ->
|
|
|
|
if build
|
2017-04-24 12:48:43 +00:00
|
|
|
dockerUtils.runBuild(params, options, getBundleInfo, logStreams)
|
2017-03-29 11:03:40 +00:00
|
|
|
else
|
|
|
|
imageName
|
|
|
|
.then (imageName) ->
|
|
|
|
Promise.join(
|
|
|
|
dockerUtils.bufferImage(docker, imageName, tmpPath)
|
|
|
|
resin.auth.getToken()
|
|
|
|
resin.auth.whoami()
|
|
|
|
resin.settings.get('resinUrl')
|
|
|
|
dockerUtils.getImageSize(docker, imageName)
|
|
|
|
params.appName
|
2017-04-24 12:48:43 +00:00
|
|
|
logStreams
|
2017-03-29 11:03:40 +00:00
|
|
|
performUpload
|
|
|
|
)
|
|
|
|
.finally ->
|
|
|
|
require('fs').unlink(tmpPath)
|
|
|
|
.then (imageName) ->
|
2017-04-24 12:48:43 +00:00
|
|
|
logging.logSuccess(logStreams, "Successfully deployed image: #{formatImageName(imageName)}")
|
2017-03-29 11:03:40 +00:00
|
|
|
.asCallback(done)
|