Skip to content

Commit

Permalink
Auto-merge for PR #818 via VersionBot
Browse files Browse the repository at this point in the history
Restore legacy deployment method
  • Loading branch information
resin-io-versionbot[bot] committed Mar 22, 2018
2 parents e4c9def + e011502 commit b388ccb
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 12 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file
automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY!
This project adheres to [Semantic Versioning](http://semver.org/).

## v7.1.0 - 2018-03-22

* Warn early if deploying a multicontainer project to an incompatible app #818 [Akis Kesoglou]
* Add legacy deploy method back #818 [Akis Kesoglou]

## v7.0.7 - 2018-03-20

* Update resin-preload to 6.1.2 #821 [Alexis Svinartchouk]
Expand Down
24 changes: 18 additions & 6 deletions lib/actions/build.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -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
###
Expand All @@ -21,6 +20,12 @@ buildProject = (docker, logger, composeOpts, opts) ->
composeOpts.projectName
)
.then (project) ->
if project.descriptors.length > 1 and not opts.app.application_type?[0]?.supports_multicontainer
logger.logWarn(
'Target application does not support multiple containers.\n' +
'Continuing with build, but you will not be able to deploy.'
)

compose.buildProject(
docker
logger
Expand Down Expand Up @@ -111,18 +116,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
Expand Down
26 changes: 26 additions & 0 deletions lib/actions/deploy.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ deployProject = (docker, logger, composeOpts, opts) ->
opts.image
)
.then (project) ->
if project.descriptors.length > 1 and !opts.app.application_type?[0]?.supports_multicontainer
throw new Error('Target application does not support multiple containers. Aborting!')

# find which services use images that already exist locally
Promise.map project.descriptors, (d) ->
# unconditionally build (or pull) if explicitly requested
Expand Down Expand Up @@ -66,6 +69,29 @@ 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.models.release.get(releaseId, $select: [ 'commit' ])
Promise.join(
sdk.auth.getUserId()
sdk.auth.getToken()
Expand Down
136 changes: 136 additions & 0 deletions lib/utils/deploy-legacy.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
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}"

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 = require('resin-cli-visuals')
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')
16 changes: 14 additions & 2 deletions lib/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,23 @@ 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
Expand Down
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "resin-cli",
"version": "7.0.7",
"version": "7.1.0",
"description": "The official resin.io CLI tool",
"main": "./build/actions/index.js",
"homepage": "https://github.com/resin-io/resin-cli",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand Down

0 comments on commit b388ccb

Please sign in to comment.