Skip to content

Commit

Permalink
fix: wait for lambda function state updates
Browse files Browse the repository at this point in the history
  • Loading branch information
jsetton authored and Shreyas-vgr committed Dec 27, 2021
1 parent b6fb7b9 commit 879377b
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 76 deletions.
115 changes: 84 additions & 31 deletions lib/builtins/deploy-delegates/lambda-deployer/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,28 +189,28 @@ function _createLambdaFunction(reporter, lambdaClient, options, callback) {
});
};
const shouldRetryCondition = retryResponse => retryResponse === RETRY_MESSAGE;
retryUtils.retry(retryConfig, retryCall, shouldRetryCondition, (retryErr, lambdaData) => {
retryUtils.retry(retryConfig, retryCall, shouldRetryCondition, (retryErr, createData) => {
if (retryErr) {
return callback(retryErr);
}
const arn = lambdaData.FunctionArn;
// 2. Grant permissions to use a Lambda function
_addEventPermissions(lambdaClient, skillId, arn, profile, (addErr) => {
if (addErr) {
return callback(addErr);
const functionArn = createData.FunctionArn;
// 2. Wait for created lambda function to be active
_waitForLambdaFunction(lambdaClient, functionArn, (waitErr) => {
if (waitErr) {
return callback(waitErr);
}
// 3. Get the latest revisionId from getFunction API
lambdaClient.getFunction(arn, (revisionErr, revisionData) => {
if (revisionErr) {
return callback(revisionErr);
// 3. Grant permissions to use a Lambda function
_addEventPermissions(lambdaClient, skillId, functionArn, profile, (permErr, lambdaData) => {
if (permErr) {
return callback(permErr);
}
callback(null, {
isAllStepSuccess: true,
isCodeDeployed: true,
lambdaResponse: {
arn,
arn: functionArn,
lastModified: lambdaData.LastModified,
revisionId: revisionData.Configuration.RevisionId
revisionId: lambdaData.RevisionId
}
});
});
Expand All @@ -222,32 +222,23 @@ function _addEventPermissions(lambdaClient, skillId, functionArn, profile, callb
const targetEndpoints = ResourcesConfig.getInstance().getTargetEndpoints(profile);
// for backward compatibility, defaulting to api from skill manifest if targetEndpoints is not defined
const domains = targetEndpoints.length ? targetEndpoints : Object.keys(Manifest.getInstance().getApis());
async.forEach(domains, (domain, addCallback) => {
lambdaClient.addAlexaPermissionByDomain(domain, skillId, functionArn, (err) => {
if (err) {
return addCallback(err);
}
addCallback();
});
}, (error) => {
callback(error);
async.forEach(domains, (domain, permCallback) => {
lambdaClient.addAlexaPermissionByDomain(domain, skillId, functionArn, permCallback);
}, (permErr) => {
if (permErr) {
return callback(permErr);
}
_waitForLambdaFunction(lambdaClient, functionArn, callback);
});
}

function _updateLambdaFunction(reporter, lambdaClient, options, callback) {
const { zipFilePath, userConfig, deployState } = options;
const zipFile = fs.readFileSync(zipFilePath);
const functionName = deployState.lambda.arn;
let { revisionId } = deployState.lambda;
lambdaClient.updateFunctionCode(zipFile, functionName, revisionId, (codeErr, codeData) => {
_updateLambdaFunctionCode(reporter, lambdaClient, options, (codeErr, codeData) => {
if (codeErr) {
return callback(codeErr);
}
reporter.updateStatus(`Update a lambda function (${functionName}) in progress...`);
const { runtime } = userConfig;
const { handler } = userConfig;
revisionId = codeData.RevisionId;
lambdaClient.updateFunctionConfiguration(functionName, runtime, handler, revisionId, (configErr, configData) => {

_updateLambdaFunctionConfig(reporter, lambdaClient, codeData, (configErr, configData) => {
if (configErr) {
return callback(null, {
isAllStepSuccess: false,
Expand All @@ -272,3 +263,65 @@ function _updateLambdaFunction(reporter, lambdaClient, options, callback) {
});
});
}

function _updateLambdaFunctionCode(reporter, lambdaClient, options, callback) {
const { zipFilePath, deployState } = options;
const zipFile = fs.readFileSync(zipFilePath);
const functionName = deployState.lambda.arn;
const { revisionId } = deployState.lambda;

lambdaClient.updateFunctionCode(zipFile, functionName, revisionId, (err) => {
if (err) {
return callback(err);
}

reporter.updateStatus(`Update a lambda function code (${functionName}) in progress...`);

_waitForLambdaFunction(lambdaClient, functionName, callback);
});
}

function _updateLambdaFunctionConfig(reporter, lambdaClient, lambdaConfig, callback) {
const { FunctionName: functionName, Runtime: runtime, Handler: handler, RevisionId: revisionId } = lambdaConfig;

lambdaClient.updateFunctionConfiguration(functionName, runtime, handler, revisionId, (err) => {
if (err) {
return callback(err);
}

reporter.updateStatus(`Update a lambda function configuration (${functionName}) in progress...`);

_waitForLambdaFunction(lambdaClient, functionName, callback);
});
}

function _waitForLambdaFunction(lambdaClient, functionName, callback) {
const retryConfig = {
base: CONSTANTS.CONFIGURATION.RETRY.WAIT_LAMBDA_FUNCTION.BASE,
factor: CONSTANTS.CONFIGURATION.RETRY.WAIT_LAMBDA_FUNCTION.FACTOR,
maxRetry: CONSTANTS.CONFIGURATION.RETRY.WAIT_LAMBDA_FUNCTION.MAXRETRY
};
const retryCall = (loopCallback) => {
lambdaClient.getFunction(functionName, (err, data) => {
if (err) {
return loopCallback(err);
}
loopCallback(null, data.Configuration);
});
};
const shouldRetryCondition = (retryResponse) => retryResponse.State === CONSTANTS.LAMBDA.FUNCTION_STATE.PENDING
|| retryResponse.LastUpdateStatus === CONSTANTS.LAMBDA.LAST_UPDATE_STATUS.IN_PROGRESS;

retryUtils.retry(retryConfig, retryCall, shouldRetryCondition, (retryErr, lambdaData) => {
if (retryErr) {
return callback(retryErr);
}
if (lambdaData.State !== CONSTANTS.LAMBDA.FUNCTION_STATE.ACTIVE) {
return callback(`Function [${functionName}] state is ${lambdaData.State}.`);
}
if (lambdaData.LastUpdateStatus !== CONSTANTS.LAMBDA.LAST_UPDATE_STATUS.SUCCESSFUL) {
return callback(`Function [${functionName}] last update status is ${lambdaData.LastUpdateStatus}.`);
}
callback(null, lambdaData);
});
}
19 changes: 19 additions & 0 deletions lib/utils/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ module.exports.CONFIGURATION = {
BASE: 5000,
FACTOR: 1.5,
MAXRETRY: 3
},
WAIT_LAMBDA_FUNCTION: {
BASE: 1000,
FACTOR: 1.2,
MAXRETRY: 10
}
},
S3: {
Expand Down Expand Up @@ -296,6 +301,20 @@ module.exports.AWS = {
}
};

module.exports.LAMBDA = {
FUNCTION_STATE: {
ACTIVE: 'Active',
INACTIVE: 'Inactive',
PENDING: 'Pending',
FAILED: 'Failed'
},
LAST_UPDATE_STATUS: {
SUCCESSFUL: 'Successful',
FAILED: 'Failed',
IN_PROGRESS: 'InProgress'
}
};

module.exports.PLACEHOLDER = {
ENVIRONMENT_VAR: {
AWS_CREDENTIALS: '__AWS_CREDENTIALS_IN_ENVIRONMENT_VARIABLE__',
Expand Down
Loading

0 comments on commit 879377b

Please sign in to comment.