cfer 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/README.md +39 -1
- data/Rakefile +1 -56
- data/cfer.gemspec +13 -14
- data/examples/common/instance_deps.rb +3 -3
- data/examples/instance.rb +15 -21
- data/lib/cfer.rb +83 -7
- data/lib/cfer/block.rb +42 -0
- data/lib/cfer/cfn/cfer_credentials_provider.rb +72 -0
- data/lib/cfer/cfn/client.rb +121 -18
- data/lib/cfer/cli.rb +40 -1
- data/lib/cfer/core/client.rb +11 -0
- data/lib/cfer/core/resource.rb +16 -18
- data/lib/cfer/core/stack.rb +85 -14
- data/lib/cfer/util/error.rb +8 -0
- data/lib/cfer/version.rb +7 -1
- data/lib/cferext/aws/auto_scaling/auto_scaling_group.rb +6 -0
- data/lib/cferext/aws/iam/policy.rb +30 -0
- data/lib/cferext/aws/iam/policy_generator.rb +54 -0
- metadata +84 -58
- data/examples/chef_instance.rb +0 -56
- data/lib/cferext/aws/auto_scaling/launch_configuration.rb +0 -15
- data/lib/cferext/aws/ec2/instance.rb +0 -15
- data/lib/cferext/provisioning.rb +0 -16
- data/lib/cferext/provisioning/cfn-bootstrap.rb +0 -186
- data/lib/cferext/provisioning/chef.rb +0 -75
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9e97e0ff7a93e2064e001ebe5d02275d10d652b4
|
4
|
+
data.tar.gz: 6814fbea31c860591f6034facf0db09aff48bd6e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c39189c5fd1dea4a81cd95837d512e71342d60361aa310e7619af2c0abbb8c5f68525263048734864775199a1a25316ede965c01aad1ece07722e02151c00ce7
|
7
|
+
data.tar.gz: dcac2d43384fc8cde476c1f3ad92a4d2153fd66fb48da3da73aa00872ee7d22367a235a903ac2b7fbfd7640ae6713e47aa92d5f54492b7eb83cd3e70bd2d84a8
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -15,7 +15,9 @@ Read about Cfer [here](http://tilmonedwards.com/2015/07/28/cfer.html).
|
|
15
15
|
|
16
16
|
Cfer is pre-1.0 software, and may contain bugs or incomplete features. Please see the [license](https://github.com/seanedwards/cfer/blob/master/LICENSE.txt) for disclaimers.
|
17
17
|
|
18
|
-
If you would like support or guidance on Cfer, or CloudFormation in general, I offer DevOps consulting services. Please [Contact
|
18
|
+
If you would like support or guidance on Cfer, or CloudFormation in general, I offer DevOps consulting services. Please [Contact me](mailto:stedwards87+cfer@gmail.com) and I'll be happy to discuss your needs.
|
19
|
+
|
20
|
+
You can also find me at [@tilmonedwards](https://twitter.com/tilmonedwards). If you use Cfer, or are considering it, I'd love to hear from you.
|
19
21
|
|
20
22
|
## Installation
|
21
23
|
|
@@ -77,9 +79,14 @@ The following options may be used with the `converge` command:
|
|
77
79
|
* `--follow` (`-f`): Follows stack events on standard output as the create/update process takes place.
|
78
80
|
* `--stack-file <template.rb>`: Reads this file from the filesystem, rather than the default `<stack-name>.rb`
|
79
81
|
* `--parameters <Key1>:<Value1> <Key2>:<Value2> ...`: Specifies input parameters, which will be available to Ruby in the `parameters` hash, or to CloudFormation by using the `Fn::ref` function
|
82
|
+
* `--parameter-file <params_file.[yaml|json]`: Specifies input parameters from a YAML or JSON file
|
83
|
+
* `--parameter-environment <env_name>`: Requires `--parameter-file`. Merges the specified key in the YAML or JSON file into the root of the parameter file before passing it into the Cfer stack, i.e. to provide different constants for different AWS environments. The priority for parameters is, in ascending order, **stack default**, **file**, **environment**, and **command line**.
|
80
84
|
* `--on-failure <DELETE|ROLLBACK|DO_NOTHING>`: Specifies the action to take when a stack creation fails. Has no effect if the stack already exists and is being updated.
|
81
85
|
* `--stack-policy <filename|URL|JSON string>` (`-s`): Stack policy to apply to the stack in order to control updates; takes a local filename containing the policy, a URL to an S3 object, or a raw JSON string.
|
82
86
|
* `--stack-policy-during-update <filename|URL|JSON string>` (`-u`): Stack policy as in `--stack-policy` option above, but applied as a temporary override to the permanent policy during stack update.
|
87
|
+
* `--s3-path <S3_PATH>`: Path to an S3 bucket location where the template will be stored. This is required if the template output exceeds [51,200 bytes](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html).
|
88
|
+
* `--force-s3`: Forces Cfer to upload the template to S3, even if it's small enough to be uploaded directly to the cloudformation API.
|
89
|
+
* `--change <CHANGE_NAME>`: Creates a [CloudFormation Change Set](https://aws.amazon.com/blogs/aws/new-change-sets-for-aws-cloudformation/), rather than immediately updating the stack.
|
83
90
|
|
84
91
|
#### `tail <stack-name>`
|
85
92
|
|
@@ -90,6 +97,19 @@ The following options may be used with the `tail` command:
|
|
90
97
|
* `--follow` (`-f`): Follows stack events on standard output as the create/update process takes place.
|
91
98
|
* `--number` (`-n`): Print the last `n` stack events.
|
92
99
|
|
100
|
+
#### `remove <stack-name>`
|
101
|
+
|
102
|
+
Removes or deletes an existing CloudFormation stack.
|
103
|
+
|
104
|
+
```bash
|
105
|
+
cfer remove vpc --profile [YOUR-PROFILE] --region [YOUR-REGION]
|
106
|
+
```
|
107
|
+
|
108
|
+
This can also be done in the following way with the awscli tools, but now eliminates the need to install that package.
|
109
|
+
```bash
|
110
|
+
aws cloudformation delete-stack --stack-name vpc
|
111
|
+
```
|
112
|
+
|
93
113
|
### Template Anatomy
|
94
114
|
|
95
115
|
See the `examples` directory for some examples of complete templates.
|
@@ -278,6 +298,24 @@ This project also contains a [Code of Conduct](https://github.com/seanedwards/cf
|
|
278
298
|
|
279
299
|
# Release Notes
|
280
300
|
|
301
|
+
## 0.4.0
|
302
|
+
|
303
|
+
### **BREAKING CHANGES**
|
304
|
+
* Provisioning is removed from Cfer core and moved to [cfer-provisioning](https://github.com/seanedwards/cfer-provisioning)
|
305
|
+
|
306
|
+
### Enhancements
|
307
|
+
* Adds support for assume-role authentication with MFA (see: https://docs.aws.amazon.com/cli/latest/userguide/cli-roles.html)
|
308
|
+
* Adds support for yml-format parameter files with environment-specific sections.
|
309
|
+
* Adds a DSL for IAM policies.
|
310
|
+
* Adds `cfer estimate` command to estimate the cost of a template using the AWS CloudFormation cost estimation API.
|
311
|
+
* Enhancements to chef provisioner to allow for references in chef attributes. (Thanks to @eropple)
|
312
|
+
* Adds continue/rollback/quit selection when `^C` is caught during a converge.
|
313
|
+
* Stores Cfer version and Git repo information in the Repo metadata.
|
314
|
+
* Added support for uploading templates to S3 with the `--s3-path` and `--force-s3` options.
|
315
|
+
* Added new way of extending resources, making plugins easier.
|
316
|
+
|
317
|
+
### Bugfixes
|
318
|
+
|
281
319
|
## 0.3.0
|
282
320
|
|
283
321
|
### Enhancements:
|
data/Rakefile
CHANGED
@@ -1,56 +1 @@
|
|
1
|
-
|
2
|
-
gem 'cfer'
|
3
|
-
require 'cfer'
|
4
|
-
require 'highline'
|
5
|
-
|
6
|
-
task :default => [:spec]
|
7
|
-
|
8
|
-
task :config_aws, [:profile] do |t, args|
|
9
|
-
Aws.config.update region: ENV['AWS_REGION'] || ask('AWS Region?') { |q| q.default = 'us-east-1' },
|
10
|
-
credentials: Aws::SharedCredentials.new(profile_name: ENV['AWS_PROFILE'] || ask('AWS Profile?') { |q| q.default = 'default' })
|
11
|
-
end
|
12
|
-
|
13
|
-
task :vpc => :config_aws do |t, args|
|
14
|
-
Cfer.converge! 'vpc',
|
15
|
-
template: 'examples/vpc.rb',
|
16
|
-
follow: true
|
17
|
-
end
|
18
|
-
|
19
|
-
task :describe_vpc => :config_aws do
|
20
|
-
Cfer.describe! 'vpc'
|
21
|
-
end
|
22
|
-
|
23
|
-
task :instance => :vpc do |t, args|
|
24
|
-
key_pair = ask("Enter your EC2 KeyPair name: ")
|
25
|
-
|
26
|
-
Cfer.converge! 'instance',
|
27
|
-
template: 'examples/instance.rb',
|
28
|
-
parameters: {
|
29
|
-
:KeyName => key_pair
|
30
|
-
},
|
31
|
-
follow: true
|
32
|
-
end
|
33
|
-
|
34
|
-
task :describe_instance => :config_aws do
|
35
|
-
Cfer.describe! 'instance'
|
36
|
-
end
|
37
|
-
|
38
|
-
task :converge => [:vpc, :instance]
|
39
|
-
|
40
|
-
|
41
|
-
########################
|
42
|
-
##### END OF DEMO ######
|
43
|
-
########################
|
44
|
-
|
45
|
-
|
46
|
-
# This task isn't really part of Cfer.
|
47
|
-
# It just makes it easier for me to release new versions.
|
48
|
-
task :release do
|
49
|
-
`git checkout master`
|
50
|
-
`git merge develop --no-ff -m 'Merge from develop for release'`
|
51
|
-
|
52
|
-
require_relative 'lib/cfer/version.rb'
|
53
|
-
|
54
|
-
`git tag -m "Release v#{Cfer::VERSION}" #{Cfer::VERSION}`
|
55
|
-
end
|
56
|
-
|
1
|
+
require "bundler/gem_tasks"
|
data/cfer.gemspec
CHANGED
@@ -19,19 +19,18 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = 'cfer'
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
-
spec.add_runtime_dependency 'docile'
|
23
|
-
spec.add_runtime_dependency 'thor'
|
24
|
-
spec.add_runtime_dependency 'activesupport'
|
25
|
-
spec.add_runtime_dependency 'aws-sdk'
|
26
|
-
spec.add_runtime_dependency 'aws-sdk-resources'
|
27
|
-
spec.add_runtime_dependency 'preconditions'
|
28
|
-
spec.add_runtime_dependency 'semantic'
|
29
|
-
spec.add_runtime_dependency 'rainbow'
|
30
|
-
spec.add_runtime_dependency 'highline'
|
31
|
-
spec.add_runtime_dependency 'table_print'
|
32
|
-
spec.add_runtime_dependency "
|
33
|
-
spec.add_runtime_dependency "
|
22
|
+
spec.add_runtime_dependency 'docile', '~> 1.1', '>= 1.1.5'
|
23
|
+
spec.add_runtime_dependency 'thor', '~> 0.19.1'
|
24
|
+
spec.add_runtime_dependency 'activesupport', '~> 4.2', '>= 4.2.6'
|
25
|
+
spec.add_runtime_dependency 'aws-sdk', '~> 2.2', '>= 2.2.33'
|
26
|
+
spec.add_runtime_dependency 'aws-sdk-resources', '~> 2.2', '>= 2.2.33'
|
27
|
+
spec.add_runtime_dependency 'preconditions', '~> 0.3.0'
|
28
|
+
spec.add_runtime_dependency 'semantic', '~> 1.4', '>= 1.4.1'
|
29
|
+
spec.add_runtime_dependency 'rainbow', '~> 2.1'
|
30
|
+
spec.add_runtime_dependency 'highline', '~> 1.7', '>= 1.7.8'
|
31
|
+
spec.add_runtime_dependency 'table_print', '~> 1.5', '>= 1.5.6'
|
32
|
+
spec.add_runtime_dependency "git", '~> 1.3'
|
33
|
+
spec.add_runtime_dependency "bundler"
|
34
34
|
|
35
|
-
spec.add_development_dependency "
|
36
|
-
spec.add_development_dependency "yard"
|
35
|
+
spec.add_development_dependency "yard", '~> 0.8.7.6'
|
37
36
|
end
|
@@ -8,12 +8,12 @@ parameter :KeyName
|
|
8
8
|
# If you created the VPC stack with a different name, you can overwrite these default values
|
9
9
|
# by adding `Vpc:<vpc_stack_name> to your `--parameters` option
|
10
10
|
parameter :Vpc, default: 'vpc'
|
11
|
-
parameter :VpcId, default: lookup_output(parameters[:Vpc], 'vpcid')
|
12
|
-
parameter :SubnetId, default: lookup_output(parameters[:Vpc], 'subnetid1')
|
11
|
+
parameter :VpcId, default: (lookup_output(parameters[:Vpc], 'vpcid') rescue nil)
|
12
|
+
parameter :SubnetId, default: (lookup_output(parameters[:Vpc], 'subnetid1') rescue nil)
|
13
13
|
|
14
14
|
# This is the Ubuntu 14.04 LTS HVM AMI provided by Amazon.
|
15
15
|
parameter :ImageId, default: 'ami-fce3c696'
|
16
|
-
parameter :InstanceType, default: 't2.
|
16
|
+
parameter :InstanceType, default: 't2.micro'
|
17
17
|
|
18
18
|
# Define a security group to be applied to an instance.
|
19
19
|
# This one will allow SSH access from anywhere, and no other inbound traffic.
|
data/examples/instance.rb
CHANGED
@@ -2,30 +2,24 @@ description 'Example stack template for a small EC2 instance'
|
|
2
2
|
|
3
3
|
# NOTE: This template depends on vpc.rb
|
4
4
|
|
5
|
-
#
|
5
|
+
# You can use the `include_template` function to include other ruby files into this Cloudformation template.
|
6
6
|
include_template 'common/instance_deps.rb'
|
7
7
|
|
8
|
-
# We can define
|
8
|
+
# We can define extensions to resources, which extend the basic JSON-building
|
9
9
|
# functionality of Cfer. Cfer provides a few of these, but you're free
|
10
|
-
# to define your own by
|
11
|
-
# CloudFormation resource type,
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
#!/bin/bash
|
24
|
-
#{data}
|
25
|
-
EOS
|
26
|
-
|
27
|
-
user_data Base64.encode64(script)
|
28
|
-
end
|
10
|
+
# to define your own by using `Cfer::Core::Resource.extend_resource` and specifying a
|
11
|
+
# CloudFormation resource type. Inside the block, define any methods you'd like to use:
|
12
|
+
Cfer::Core::Resource.extend_resource "AWS::EC2::Instance" do
|
13
|
+
def boot_script(data)
|
14
|
+
# This function simply wraps a bash script in the little bit of extra
|
15
|
+
# sugar (hashbang + base64 encoding) that EC2 requires for userdata boot scripts.
|
16
|
+
# See the AWS docs here: http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html
|
17
|
+
script = <<-EOS.strip_heredoc
|
18
|
+
#!/bin/bash
|
19
|
+
#{data}
|
20
|
+
EOS
|
21
|
+
|
22
|
+
user_data Base64.encode64(script)
|
29
23
|
end
|
30
24
|
end
|
31
25
|
|
data/lib/cfer.rb
CHANGED
@@ -56,12 +56,34 @@ module Cfer
|
|
56
56
|
cfn = options[:aws_options] || {}
|
57
57
|
|
58
58
|
cfn_stack = options[:cfer_client] || Cfer::Cfn::Client.new(cfn.merge(stack_name: stack_name))
|
59
|
-
stack = options[:cfer_stack] ||
|
59
|
+
stack = options[:cfer_stack] ||
|
60
|
+
Cfer::stack_from_file(tmpl,
|
61
|
+
options.merge(
|
62
|
+
client: cfn_stack,
|
63
|
+
parameters: generate_final_parameters(options)
|
64
|
+
)
|
65
|
+
)
|
60
66
|
|
61
67
|
begin
|
62
|
-
|
63
|
-
if options[:follow]
|
64
|
-
|
68
|
+
operation = stack.converge!(options)
|
69
|
+
if options[:follow] && !options[:change]
|
70
|
+
begin
|
71
|
+
tail! stack_name, options.merge(cfer_client: cfn_stack)
|
72
|
+
rescue Interrupt
|
73
|
+
puts "Caught interrupt. What would you like to do?"
|
74
|
+
case HighLine.new($stdin, $stderr).choose('Continue', 'Quit', 'Rollback')
|
75
|
+
when 'Continue'
|
76
|
+
retry
|
77
|
+
when 'Rollback'
|
78
|
+
case operation
|
79
|
+
when :created
|
80
|
+
cfn_stack.delete_stack stack_name: stack_name
|
81
|
+
when :updated
|
82
|
+
cfn_stack.cancel_update_stack stack_name: stack_name
|
83
|
+
end
|
84
|
+
retry
|
85
|
+
end
|
86
|
+
end
|
65
87
|
end
|
66
88
|
rescue Aws::CloudFormation::Errors::ValidationError => e
|
67
89
|
Cfer::LOGGER.info "CFN validation error: #{e.message}"
|
@@ -118,11 +140,31 @@ module Cfer
|
|
118
140
|
|
119
141
|
def generate!(tmpl, options = {})
|
120
142
|
config(options)
|
121
|
-
|
122
|
-
|
143
|
+
cfn = options[:aws_options] || {}
|
144
|
+
|
145
|
+
cfn_stack = options[:cfer_client] || Cfer::Cfn::Client.new(cfn)
|
146
|
+
stack = options[:cfer_stack] || Cfer::stack_from_file(tmpl,
|
147
|
+
options.merge(client: cfn_stack, parameters: generate_final_parameters(options))).to_h
|
123
148
|
puts render_json(stack, options)
|
124
149
|
end
|
125
150
|
|
151
|
+
def estimate!(tmpl, options = {})
|
152
|
+
config(options)
|
153
|
+
cfn = options[:aws_options] || {}
|
154
|
+
|
155
|
+
cfn_stack = options[:cfer_client] || Cfer::Cfn::Client.new(cfn)
|
156
|
+
stack = options[:cfer_stack] || Cfer::stack_from_file(tmpl,
|
157
|
+
options.merge(client: cfn_stack, parameters: generate_final_parameters(options)))
|
158
|
+
puts cfn_stack.estimate(stack)
|
159
|
+
end
|
160
|
+
|
161
|
+
def delete!(stack_name, options = {})
|
162
|
+
config(options)
|
163
|
+
cfn = options[:aws_options] || {}
|
164
|
+
cfn_stack = options[:cfer_client] || cfn_stack = Cfer::Cfn::Client.new(cfn.merge(stack_name: stack_name))
|
165
|
+
cfn_stack.delete_stack(stack_name)
|
166
|
+
end
|
167
|
+
|
126
168
|
# Builds a Cfer::Core::Stack from a Ruby block
|
127
169
|
#
|
128
170
|
# @param options [Hash] The stack options
|
@@ -156,8 +198,40 @@ module Cfer
|
|
156
198
|
Cfer::LOGGER.debug "Options: #{options}"
|
157
199
|
Cfer::LOGGER.level = Logger::DEBUG if options[:verbose]
|
158
200
|
|
201
|
+
require 'rubygems'
|
202
|
+
require 'bundler/setup'
|
203
|
+
|
159
204
|
Aws.config.update region: options[:region] if options[:region]
|
160
|
-
Aws.config.update credentials:
|
205
|
+
Aws.config.update credentials: Cfer::Cfn::CferCredentialsProvider.new(profile_name: options[:profile]) if options[:profile]
|
206
|
+
end
|
207
|
+
|
208
|
+
def generate_final_parameters(options)
|
209
|
+
raise "parameter-environment set but parameter_file not set" \
|
210
|
+
if options[:parameter_environment] && options[:parameter_file].nil?
|
211
|
+
|
212
|
+
file_params =
|
213
|
+
if options[:parameter_file]
|
214
|
+
case File.extname(options[:parameter_file])
|
215
|
+
when '.yaml'
|
216
|
+
require 'yaml'
|
217
|
+
YAML.load_file(options[:parameter_file])
|
218
|
+
when '.json'
|
219
|
+
JSON.parse(IO.read(options[:parameter_file]))
|
220
|
+
else
|
221
|
+
raise "Unrecognized parameter file format: #{File.extname(options[:parameter_file])}"
|
222
|
+
end
|
223
|
+
else
|
224
|
+
{}
|
225
|
+
end
|
226
|
+
|
227
|
+
if options[:parameter_environment]
|
228
|
+
raise "no key '#{options[:parameter_environment]}' found in parameters file." \
|
229
|
+
unless file_params.key?(options[:parameter_environment])
|
230
|
+
|
231
|
+
file_params = file_params.deep_merge(file_params[options[:parameter_environment]])
|
232
|
+
end
|
233
|
+
|
234
|
+
file_params.deep_merge(options[:parameters] || {})
|
161
235
|
end
|
162
236
|
|
163
237
|
def render_json(obj, options = {})
|
@@ -170,6 +244,8 @@ module Cfer
|
|
170
244
|
|
171
245
|
def templatize_errors(base_loc)
|
172
246
|
yield
|
247
|
+
rescue Cfer::Util::CferError => e
|
248
|
+
raise e
|
173
249
|
rescue SyntaxError => e
|
174
250
|
raise Cfer::Util::TemplateError.new([]), e.message
|
175
251
|
rescue StandardError => e
|
data/lib/cfer/block.rb
CHANGED
@@ -30,4 +30,46 @@ module Cfer
|
|
30
30
|
def post_block
|
31
31
|
end
|
32
32
|
end
|
33
|
+
|
34
|
+
class BlockHash < Block
|
35
|
+
NON_PROXIED_METHODS = [:parameters, :options, :lookup_output]
|
36
|
+
|
37
|
+
def properties(keyvals = {})
|
38
|
+
self.merge!(keyvals)
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_property(key)
|
42
|
+
self.fetch key
|
43
|
+
end
|
44
|
+
|
45
|
+
def respond_to?(method_sym)
|
46
|
+
!non_proxied_methods.include?(method_sym)
|
47
|
+
end
|
48
|
+
|
49
|
+
def method_missing(method_sym, *arguments, &block)
|
50
|
+
key = camelize_property(method_sym)
|
51
|
+
properties key =>
|
52
|
+
case arguments.size
|
53
|
+
when 0
|
54
|
+
if block
|
55
|
+
BlockHash.new.build_from_block(&block)
|
56
|
+
else
|
57
|
+
raise "Expected a value or block when setting property #{key}"
|
58
|
+
end
|
59
|
+
when 1
|
60
|
+
arguments.first
|
61
|
+
else
|
62
|
+
arguments
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
def non_proxied_methods
|
68
|
+
NON_PROXIED_METHODS
|
69
|
+
end
|
70
|
+
|
71
|
+
def camelize_property(sym)
|
72
|
+
sym.to_s.camelize.to_sym
|
73
|
+
end
|
74
|
+
end
|
33
75
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Cfer
|
4
|
+
module Cfn
|
5
|
+
class CferCredentialsProvider < Aws::SharedCredentials
|
6
|
+
private
|
7
|
+
|
8
|
+
def load_from_path
|
9
|
+
profile = load_profile
|
10
|
+
credentials = Aws::Credentials.new(
|
11
|
+
profile['aws_access_key_id'],
|
12
|
+
profile['aws_secret_access_key'],
|
13
|
+
profile['aws_session_token']
|
14
|
+
)
|
15
|
+
@credentials =
|
16
|
+
if role_arn = profile['role_arn']
|
17
|
+
role_creds =
|
18
|
+
begin
|
19
|
+
YAML::load_file('.cfer-role')
|
20
|
+
rescue
|
21
|
+
{}
|
22
|
+
end
|
23
|
+
|
24
|
+
if stored_creds = role_creds[profile_name]
|
25
|
+
if (Time.now.to_i + 5 * 60) > stored_creds[:expiration].to_i
|
26
|
+
stored_creds = nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
if stored_creds == nil
|
31
|
+
role_credentials_options = {
|
32
|
+
role_session_name: [*('A'..'Z')].sample(16).join,
|
33
|
+
role_arn: role_arn,
|
34
|
+
credentials: credentials
|
35
|
+
}
|
36
|
+
|
37
|
+
if profile['mfa_serial']
|
38
|
+
role_credentials_options[:serial_number] ||= profile['mfa_serial']
|
39
|
+
role_credentials_options[:token_code] ||= HighLine.new($stdin, $stderr).ask('Enter MFA Code:')
|
40
|
+
end
|
41
|
+
|
42
|
+
creds = Aws::AssumeRoleCredentials.new(role_credentials_options)
|
43
|
+
stored_creds = {
|
44
|
+
expiration: creds.expiration,
|
45
|
+
credentials: creds.credentials
|
46
|
+
}
|
47
|
+
role_creds[profile_name] = stored_creds
|
48
|
+
end
|
49
|
+
|
50
|
+
IO.write('.cfer-role', YAML.dump(role_creds))
|
51
|
+
stored_creds[:credentials]
|
52
|
+
else
|
53
|
+
credentials
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def load_profile
|
58
|
+
if profile = profiles[profile_name]
|
59
|
+
# Add all options from source profile
|
60
|
+
if source = profile.delete('source_profile')
|
61
|
+
profiles[source].merge(profile)
|
62
|
+
else
|
63
|
+
profile
|
64
|
+
end
|
65
|
+
else
|
66
|
+
msg = "Profile `#{profile_name}' not found in #{path}"
|
67
|
+
raise Aws::Errors::NoSuchProfileError, msg
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|