116 lines
3.5 KiB
JavaScript

'use strict';
const { log } = require('@serverless/utils/log');
const ServerlessError = require('../../../../serverless-error');
module.exports = {
async setServerlessDeploymentBucketName() {
try {
const bucketName = await this.provider.getServerlessDeploymentBucketName();
this.bucketName = bucketName;
} catch (err) {
// If there is a validation error with expected message, it means that logical resource for
// S3 bucket does not exist and we want to proceed with empty `bucketName`
if (
err.providerError.code !== 'ValidationError' ||
!err.message.includes('does not exist for stack')
) {
throw err;
}
}
},
async listObjectsV2() {
this.objectsInBucket = [];
const serviceStage = `${this.serverless.service.service}/${this.provider.getStage()}`;
const result = await this.provider.request('S3', 'listObjectsV2', {
Bucket: this.bucketName,
Prefix: `${this.provider.getDeploymentPrefix()}/${serviceStage}`,
});
if (result) {
result.Contents.forEach((object) => {
this.objectsInBucket.push({
Key: object.Key,
});
});
}
},
async listObjectVersions() {
this.objectsInBucket = [];
const serviceStage = `${this.serverless.service.service}/${this.provider.getStage()}`;
const result = await this.provider.request('S3', 'listObjectVersions', {
Bucket: this.bucketName,
Prefix: `${this.provider.getDeploymentPrefix()}/${serviceStage}`,
});
if (result) {
if (result.Versions) {
result.Versions.forEach((object) => {
this.objectsInBucket.push({
Key: object.Key,
VersionId: object.VersionId,
});
});
}
if (result.DeleteMarkers) {
result.DeleteMarkers.forEach((object) => {
this.objectsInBucket.push({
Key: object.Key,
VersionId: object.VersionId,
});
});
}
}
},
async listObjects() {
const deploymentBucketObject = this.serverless.service.provider.deploymentBucketObject;
return deploymentBucketObject && deploymentBucketObject.versioning
? this.listObjectVersions()
: this.listObjectsV2();
},
async deleteObjects() {
if (this.objectsInBucket.length) {
const data = await this.provider.request('S3', 'deleteObjects', {
Bucket: this.bucketName,
Delete: {
Objects: this.objectsInBucket,
},
});
if (data && data.Errors && data.Errors.length) {
const firstErrorCode = data.Errors[0].Code;
if (firstErrorCode === 'AccessDenied') {
throw new ServerlessError(
`Could not empty the S3 deployment bucket (${this.bucketName}). Make sure that you have permissions that allow S3 objects deletion. First encountered S3 error code: ${firstErrorCode}`,
'CANNOT_DELETE_S3_OBJECTS_ACCESS_DENIED'
);
}
throw new ServerlessError(
`Could not empty the S3 deployment bucket (${this.bucketName}). First encountered S3 error code: ${firstErrorCode}`,
'CANNOT_DELETE_S3_OBJECTS_GENERIC'
);
}
}
},
async emptyS3Bucket() {
await this.setServerlessDeploymentBucketName();
if (this.bucketName && (await this.checkIfBucketExists(this.bucketName))) {
await this.listObjects();
await this.deleteObjects();
} else {
log.info('S3 bucket not found. Skipping S3 bucket objects removal');
}
},
};