Merge pull request from balena-io/js-lib-utils-deploy-legacy

Convert lib/utils/deploy-legacy.coffee to javascript
This commit is contained in:
Page- 2020-04-30 13:22:03 +01:00 committed by GitHub
commit 5fef98bdf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 221 additions and 139 deletions

View File

@ -89,7 +89,7 @@ deployProject = (docker, logger, composeOpts, opts) ->
}
.then (images) ->
if opts.app.application_type?[0]?.is_legacy
legacyDeploy = require('../utils/deploy-legacy')
{ deployLegacy } = require('../utils/deploy-legacy')
msg = getChalk().yellow('Target application requires legacy deploy method.')
logger.logWarn(msg)
@ -107,7 +107,7 @@ deployProject = (docker, logger, composeOpts, opts) ->
buildLogs: images[0].logs
shouldUploadLogs: opts.shouldUploadLogs
}
legacyDeploy
deployLegacy
)
.then (releaseId) ->
sdk.models.release.get(releaseId, $select: [ 'commit' ])

View File

@ -1,137 +0,0 @@
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')

209
lib/utils/deploy-legacy.js Normal file
View File

@ -0,0 +1,209 @@
/**
* @license
* Copyright 2020 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as Promise from 'bluebird';
import { getVisuals } from './lazy';
const getBuilderPushEndpoint = function(baseUrl, owner, app) {
const querystring = require('querystring');
const args = querystring.stringify({ owner, app });
return `https://builder.${baseUrl}/v1/push?${args}`;
};
const getBuilderLogPushEndpoint = function(baseUrl, buildId, owner, app) {
const querystring = require('querystring');
const args = querystring.stringify({ owner, app, buildId });
return `https://builder.${baseUrl}/v1/pushLogs?${args}`;
};
const bufferImage = function(docker, imageId, bufferFile) {
const streamUtils = require('./streams');
const image = docker.getImage(imageId);
const imageMetadata = image.inspect();
return Promise.join(
image.get(),
imageMetadata.get('Size'),
(imageStream, imageSize) =>
streamUtils.buffer(imageStream, bufferFile).tap(bufferedStream => {
// @ts-ignore adding an extra property
bufferedStream.length = imageSize;
}),
);
};
const showPushProgress = function(message) {
const visuals = getVisuals();
const progressBar = new visuals.Progress(message);
progressBar.update({ percentage: 0 });
return progressBar;
};
const uploadToPromise = (uploadRequest, logger) =>
new Promise(function(resolve, reject) {
const handleMessage = function(data) {
let obj;
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) {
case 'error':
reject(new Error(`Remote error: ${obj.error}`));
break;
case 'success':
resolve(obj);
break;
case 'status':
logger.logInfo(obj.message);
break;
default:
reject(new Error(`Received unexpected reply from remote: ${data}`));
}
};
uploadRequest.on('error', reject).on('data', handleMessage);
});
const uploadImage = function(
imageStream,
token,
username,
url,
appName,
logger,
) {
const request = require('request');
const progressStream = require('progress-stream');
const zlib = require('zlib');
// Need to strip off the newline
const progressMessage = logger
.formatMessage('info', 'Uploading')
.slice(0, -1);
const progressBar = showPushProgress(progressMessage);
const streamWithProgress = imageStream.pipe(
progressStream(
{
time: 500,
length: imageStream.length,
},
({ percentage, eta }) =>
progressBar.update({
percentage: Math.min(percentage, 100),
eta,
}),
),
);
const uploadRequest = request.post({
url: getBuilderPushEndpoint(url, username, appName),
headers: {
'Content-Encoding': 'gzip',
},
auth: {
bearer: token,
},
body: streamWithProgress.pipe(
zlib.createGzip({
level: 6,
}),
),
});
return uploadToPromise(uploadRequest, logger);
};
const uploadLogs = function(logs, token, url, buildId, username, appName) {
const request = require('request');
return 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
*/
export const deployLegacy = function(
docker,
logger,
token,
username,
url,
opts,
) {
const tmp = require('tmp');
const tmpNameAsync = Promise.promisify(tmp.tmpName);
// Ensure the tmp files gets deleted
tmp.setGracefulCleanup();
const { appName, imageName, buildLogs, shouldUploadLogs } = opts;
const logs = buildLogs;
return tmpNameAsync()
.then(function(bufferFile) {
logger.logInfo('Initializing deploy...');
return 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(
undefined,
),
);
})
.tap(function({ buildId }) {
if (!shouldUploadLogs) {
return;
}
logger.logInfo('Uploading logs...');
return Promise.join(
logs,
token,
url,
buildId,
username,
appName,
uploadLogs,
);
})
.get('buildId');
};

9
npm-shrinkwrap.json generated
View File

@ -1217,6 +1217,15 @@
"integrity": "sha512-Zu4jAKE46yc6R8JrVkCBWbXhs18dUgI/JlbID4jziFgUBgEdAHxFekR5TlEnk9phHdGE80QlCznRBaxlk0rl7w==",
"dev": true
},
"@types/progress-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/progress-stream/-/progress-stream-2.0.0.tgz",
"integrity": "sha512-KKboL4BvIezEvnd2bdbIfIIkr3Th1p0AJOBaPdKMWm69uMoisOGd4mynzfB/iia2z0k3nwAJCmixESHfVjsrgw==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/range-parser": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",

View File

@ -129,6 +129,7 @@
"@types/node": "^10.17.20",
"@types/node-cleanup": "^2.1.1",
"@types/prettyjson": "0.0.29",
"@types/progress-stream": "^2.0.0",
"@types/request": "^2.48.4",
"@types/rewire": "^2.5.28",
"@types/rimraf": "^2.0.4",