Add legacy deploy method back

This mostly reverts the removal of the legacy deploy code that pushed image tars via the builder. It’s needed for users to avoid having to switch between CLI versions in order to push to legacy apps as well.

Note: this pins resin-sdk to 9.0.0-beta14 as I couldn’t get it to install otherwise — npm would always install 9.0.0-beta9 instead.

Change-Type: minor
This commit is contained in:
Akis Kesoglou 2018-03-19 20:58:05 +02:00
parent e4c9defb70
commit 62f006b89a
5 changed files with 206 additions and 11 deletions

View File

@ -7,10 +7,9 @@ compose = require('../utils/compose')
###
Opts must be an object with the following keys:
appName: the name of the app this build is for; optional
app: the app this build is for
arch: the architecture to build for
deviceType: the device type to build for
projectPath: the project root directory; must be absolute
buildEmulated
buildOpts: arguments to forward to docker build command
###
@ -111,18 +110,25 @@ module.exports =
if arch? and deviceType?
[ undefined, arch, deviceType ]
else
helpers.getArchAndDeviceType(application)
Promise.join(
helpers.getApplication(application)
helpers.getArchAndDeviceType(application)
(app, { arch, device_type }) ->
app.arch = arch
app.device_type = device_type
return app
)
.then (app) ->
[ application, app.arch, app.device_type ]
[ app, app.arch, app.device_type ]
.then ([ appName, arch, deviceType ]) ->
.then ([ app, arch, deviceType ]) ->
Promise.join(
dockerUtils.getDocker(options)
dockerUtils.generateBuildOpts(options)
compose.generateOpts(options)
(docker, buildOpts, composeOpts) ->
buildProject(docker, logger, composeOpts, {
appName
app
arch
deviceType
buildEmulated: !!options.emulated

View File

@ -66,6 +66,33 @@ deployProject = (docker, logger, composeOpts, opts) ->
props: {}
}
.then (images) ->
if opts.app.application_type?[0]?.is_legacy
chalk = require('chalk')
legacyDeploy = require('../utils/deploy-legacy')
msg = chalk.yellow('Target application requires legacy deploy method.')
logger.logWarn(msg)
return Promise.join(
docker
logger
sdk.auth.getToken()
sdk.auth.whoami()
sdk.settings.get('resinUrl')
{
appName: opts.app.app_name
imageName: images[0].name
buildLogs: images[0].logs
shouldUploadLogs: opts.shouldUploadLogs
}
legacyDeploy
)
.then (releaseId) ->
sdk.pine.get
resource: 'release'
id: releaseId
options:
$select: [ 'commit' ]
Promise.join(
sdk.auth.getUserId()
sdk.auth.getToken()

View File

@ -0,0 +1,153 @@
Promise = require('bluebird')
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}"
formatImageName = (image) ->
image.split('/').pop()
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
getSpinner = (message) ->
visuals = require('resin-cli-visuals')
return new visuals.Spinner(message)
showPushProgress = (message) ->
visuals = require('resin-cli-visuals')
progressBar = new visuals.Progress(message)
progressBar.update({ percentage: 0 })
return progressBar
forwardStatusMessageFromRemote = (logger, msg) ->
msg.split(/\r\n|\n/).forEach (line) ->
if /^Warning: This endpoint is deprecated/.test(line)
return
logger.logInfo(line)
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
if obj.type?
switch obj.type
when 'error' then reject(new Error("Remote error: #{obj.error}"))
when 'success' then resolve(obj)
when 'status' then forwardStatusMessageFromRemote(logger, obj.message)
else reject(new Error("Received unexpected reply from remote: #{data}"))
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) ->
_ = require('lodash')
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')

View File

@ -138,11 +138,19 @@ export function getApplication(applicationName: string) {
// that off to a special handler (before importing any modules)
const match = /(\w+)\/(\w+)/.exec(applicationName);
const extraOptions = {
$expand: {
application_type: {
$select: [ 'name', 'slug', 'supports_multicontainer', 'is_legacy' ],
},
},
};
if (match) {
return resin.models.application.getAppByOwner(match[2], match[1]);
return resin.models.application.getAppByOwner(match[2], match[1], extraOptions);
}
return resin.models.application.get(applicationName);
return resin.models.application.get(applicationName, extraOptions);
}
// A function to reliably execute a command

View File

@ -83,6 +83,7 @@
},
"dependencies": {
"@resin.io/valid-email": "^0.1.0",
"@types/stream-to-promise": "^2.2.0",
"ansi-escapes": "^2.0.0",
"any-promise": "^1.3.0",
"archiver": "^2.1.0",
@ -129,7 +130,7 @@
"resin-cli-errors": "^1.2.0",
"resin-cli-form": "^1.4.1",
"resin-cli-visuals": "^1.4.0",
"resin-compose-parse": "^1.5.2",
"resin-compose-parse": "^1.8.0",
"resin-config-json": "^1.0.0",
"resin-device-config": "^4.0.0",
"resin-device-init": "^4.0.0",
@ -139,8 +140,8 @@
"resin-image-manager": "^5.0.0",
"resin-multibuild": "^0.5.1",
"resin-preload": "^6.1.2",
"resin-release": "^1.1.1",
"resin-sdk": "^9.0.0-beta7",
"resin-release": "^1.2.0",
"resin-sdk": "9.0.0-beta14",
"resin-sdk-preconfigured": "^6.9.0",
"resin-settings-client": "^3.6.1",
"resin-stream-logger": "^0.1.0",