Skip to content

add support for passing an AWS.Credentials object #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@ gulp.task('deploy', function() {
timestamp: true, // optional: If set to false, the zip will not have a timestamp
waitForDeploy: true, // optional: if set to false the task will end as soon as it starts deploying
amazon: {
accessKeyId: "< your access key (fyi, the 'short' one) >", // optional
secretAccessKey: "< your secret access key (fyi, the 'long' one) >", // optional
signatureVersion: "v4", // optional
credentials: {
accessKeyId: "< your access key (fyi, the 'short' one) >", // optional
secretAccessKey: "< your secret access key (fyi, the 'long' one) >", // optional
}
config: { // optional
signatureVersion: "v4",
}
region: 'eu-west-1',
bucket: 'elasticbeanstalk-apps',
applicationName: 'MyApplication',
Expand All @@ -38,7 +42,11 @@ gulp.task('deploy', function() {

The code above would work as follows
* Take the files sepcified by `gulp.src` and zip them on a file named `{ version }-{ timestamp }.zip` (i.e: `1.0.0-2016.04.08_13.26.32.zip`)
* If amazon credentials (`accessKeyId`, `secretAccessKey`) are provided in the `amazon` object, set them on the `AWS.config.credentials`. If not provided, the default values from AWS CLI configuration will be used.
* There are multiple ways to provide AWS credentials:
1. as an [AWS.Credentials](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Credentials.html) object or an object of any inheriting class
2. as an object holding parameters to AWS.Credentials or any of the following inheriting classes: [AWS.CognitoIdentityCredentials](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityCredentials.html), [AWS.SharedIniFileCredentials](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/SharedIniFileCredentials.html), [AWS.SAMLCredentials](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/SAMLCredentials.html), [AWS.TemporaryCredentials](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/TemporaryCredentials.html), which gulp-elasticbeanstalk-deploy will then try to autodetect.
3. as a string either holding the path to [AWS.FileSystemCredentials](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/FileSystemCredentials.html) or the prefix for [AWS.EnvironmentCredentials](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/EnvironmentCredentials.html).
2. If no credentials are provided, the default values from AWS CLI configuration will be used.
* Try to upload the zipped file to the bucket specified by `amazon.bucket`. If it fails because the bucket doesn't exist, try to create the bucket and then try to upload the zipped file again
* Uploads the ziped files to the bucket on the path `{{ name }}/{{ filename }}` (i.e: `my-application/1.0.0-2016.04.08_13.26.32.zip`)
* Creates a new version on the Application specified by `applicationName` with VersionLabel `{ version }-{ timestamp }` (i.e: `1.0.0-2016.04.08_13.26.32`)
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
"left-pad": "^1.1.1",
"lodash": "^4.8.2",
"plexer": "^1.0.1",
"through2": "^2.0.1"
"through2": "^2.0.1",
"uuid": "^3.1.0"
},
"devDependencies": {
"babel-cli": "^6.14.0",
Expand Down
11 changes: 8 additions & 3 deletions src/aws.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,18 @@ export class S3File {
*
* @async
* @method S3File#create
* @param {String} [region] Region where the bucket is to be created
* @return {Promise} Resolved once the action has completed
*

* @see [AWS.S3#createBucket]{@link http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#createBucket-property}
*/
async create() {
async create(region) {
return await new Promise((resolve, reject) => {
this.s3bucket.createBucket((err, result) => {
this.s3bucket.createBucket({
CreateBucketConfiguration: {
LocationConstraint: region
}
}, (err, result) => {
if (err) reject(err)
else resolve(result)
})
Expand Down
99 changes: 88 additions & 11 deletions src/plugin.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { readFileSync } from 'fs'
import { readFileSync, existsSync } from 'fs'
import { join } from 'path'
import { omit, isEqual } from 'lodash'
import { log as gulpLog, colors, PluginError } from 'gulp-util'
Expand All @@ -9,6 +9,43 @@ import AWS from 'aws-sdk'
import pad from 'left-pad'
import { S3File, Bean } from './aws'

const credentialProviders = [
{
Ctor: AWS.Credentials,
fields: [
[ 'accessKeyId', 'secretAccessKey' ]
]
},
{
Ctor: AWS.SAMLCredentials,
fields: [
[ 'RoleArn', 'PrincipalArn', 'SAMLAssertion' ]
]
},
{
Ctor: AWS.CognitoIdentityCredentials,
fields: [
[ 'IdentityPoolId' ],
[ 'IdentityId' ]
]
},
{
// we can only detect these if a custom profile is specified
// but that is fine because shared ini file credentials using the default profile
// are used by the AWS SDK when no credentials are specified
Ctor: AWS.SharedIniFileCredentials,
fields: [
[ 'profile' ]
]
},
{
Ctor: AWS.TemporaryCredentials,
fields: [
[ 'SerialNumber', 'TokenCode' ],
[ 'RoleArn' ]
]
}
]
const IS_TEST = process.env['NODE_ENV'] === 'test'
const log = IS_TEST ? () => {} : gulpLog

Expand Down Expand Up @@ -129,7 +166,7 @@ export async function deploy(opts, file, s3file, bean) {
if (e.code !== 'NoSuchBucket')
throw e

await s3file.create()
await s3file.create(opts.region)
await s3file.upload(file)
}

Expand All @@ -152,11 +189,11 @@ export async function deploy(opts, file, s3file, bean) {
* * versionLabel: version. version + currentTimestamp if timestamp was true
* * filename: versionLavel + '.zip'
*
* If the resulting object amazon property contains accessKeyId and
* secretAccessKey, both will be added as `AWS.config.credentials`.
* If the resulting object amazon property contains credentials,
they will be added as `AWS.config.credentials`.
*
* If the resulting object amazon property contains signatureVersion, it will
* be added to `AWS.config`, ese v4 will be used as signatureVersion
* If the resulting object amazon property contains a configuration object,
* it will be applied to `AWS.config`.
*
* @param {Object} opts
* @return {Object}
Expand Down Expand Up @@ -191,17 +228,57 @@ export function buildOptions(opts) {
if (!options.amazon)
throw new PluginError(PLUGIN_NAME, 'No amazon config provided')

// if keys are provided, create new credentials, otherwise defaults will be used
if (options.amazon.accessKeyId && options.amazon.secretAccessKey) {
AWS.config.update(Object.assign({
signatureVersion: 'v4'
}, options.amazon.config || {}))

// legacy support for signatureVersion now covered by config parameter
if (options.amazon.signatureVersion)
AWS.config.signatureVersion = options.amazon.signatureVersion

if (options.amazon.credentials !== undefined) {
const creds = options.amazon.credentials
const credsType = typeof(creds)

if (credsType === 'string') {
// if the credentials are of type string, assume the user is specifying
// an environment variable name prefix
AWS.config.credentials = existsSync(creds) ? new AWS.FileSystemCredentials(creds)
: new AWS.EnvironmentCredentials(creds)
} else if (credsType !== 'object') {
// otherwise the credentials must be an object
throw new PluginError(PLUGIN_NAME, `Amazon credentials must be an object, got a '${typeof(creds)}'.`)
} else if (creds.constructor.name === 'Credentials' ||
typeof(creds.constructor.__super__) === 'function' &&
creds.constructor.__super__.name === 'Credentials') {
// support pre-build objects of or inheriting the AWS.Credentials class
AWS.config.credentials = creds
} else {
// otherwise try to find a matching provider for the supplied credentials object
const provider = credentialProviders.find(prov =>
prov.fields.find(fields =>
fields.every(field => creds[field] !== undefined)
)
)
if (provider === undefined)
throw new PluginError(PLUGIN_NAME, `Could not find a matching AWS credentials provider for the supplied credentials object.`)

try {
AWS.config.credentials = new provider.Ctor(creds)
} catch(err) {
throw new PluginError(PLUGIN_NAME, `An error occured while trying to construct AWS.${provider.Ctor.name} from supplied credentials object: ${err}`)
}
}
} else if (options.amazon.accessKeyId && options.amazon.secretAccessKey) {
// legacy support for the access key id and secret access key
// passed in directly via the options.amazon object
log('options.amazon.accessKeyId and options.amazon.secretAccessKey are deprecated and will be removed in a future version. Use options.amazon.credentials instead.')
AWS.config.credentials = new AWS.Credentials({
accessKeyId: opts.amazon.accessKeyId,
secretAccessKey: opts.amazon.secretAccessKey
})
}

// Set v4 by default
AWS.config.signatureVersion = options.amazon.signatureVersion || 'v4'

return options
}

Expand Down
Loading