cfer 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,11 +1,24 @@
1
1
  language: ruby
2
2
  install: bundle install --without debug --jobs=3 --retry=3
3
3
  rvm:
4
- - 2.1.3
4
+ - 2.1
5
+ - 2.2
6
+ - 2.3.0
5
7
  - ruby-head
8
+ matrix:
9
+ allow_failures:
10
+ - rvm: ruby-head
11
+ branches:
12
+ only:
13
+ - master
14
+ - develop
6
15
  script:
7
16
  - bundle exec rspec
8
17
  - bundle exec yard
18
+ addons:
19
+ code_climate:
20
+ repo_token:
21
+ secure: yokaltZRMTADMBq/htRjXYxchq4e7e66Zlvv0DrLs3yppy3bpCKbXkmtZdUm4XoxOA8fcoeFB0/VVuijmkdpJd2fAj1dc8RDb5uEdwA6ifI2ni1wKZHg15YheL4VwvDK1Nhb9Gr06uXReQ1Fy6jYcXDktyirbX7/AN/csD83IEQ29U58DVCwPxtG1k3h8ji6r/BMayzAd3T/bAeNkLd6t7YIdUAQHqIQsHsER/wQEYRTBVrx+D9b5rj2UluctfJDmpM61lvQnMt6VfgewzGXZCKNdY2DAvySaNg4+9/saZt3Ygpnt7Ef7uBMqgWTayNQ0Wk2VZSUvvLDRAKg+3WLBXTUnbFKK4/GT2T/37Rw9KQGDBJATjDLvNkS1V6Ikzg/4eNtni9RsV/bIwEaYg4CjhVEccj9fa1iPl+2Nzyw7q15UUT89TYJR3L00MQ+M7j5BDgg48kmg2tXBL62kIj/mFzEGuwNLn5nuVkwlnciQ5KCNDrQmN7XL3sPSDyIQ1dAezuj/CAqrRu43KukNZPn9TK9z/NPLUfHvco2E10Mlsw4gXiSp8j1WOmgcenivAoP2x4z5isZ2FShXkxLGq/sT0R4Ko+oGTnD/ghIHuhY3dUDfBhpOkq1wrFB73GjnUh5cYetlS3MvOj0dxT0iUK4oNp1/ds2Bn8in6juiOkN/Zc=
9
22
  deploy:
10
23
  provider: rubygems
11
24
  api_key:
data/Gemfile CHANGED
@@ -8,6 +8,7 @@ group :test do
8
8
  gem 'rspec-mocks'
9
9
  gem 'guard-rspec'
10
10
  gem 'coveralls', require: false
11
+ gem "codeclimate-test-reporter", require: nil
11
12
  end
12
13
 
13
14
  group :debug do
@@ -15,4 +16,5 @@ group :debug do
15
16
  gem 'pry-byebug'
16
17
  gem 'pry-rescue'
17
18
  gem 'pry-stack_explorer'
19
+ gem 'travis'
18
20
  end
data/README.md CHANGED
@@ -1,13 +1,22 @@
1
1
  # Cfer
2
2
 
3
3
  [![Build Status](https://travis-ci.org/seanedwards/cfer.svg?branch=master)](https://travis-ci.org/seanedwards/cfer)
4
- [![Coverage Status](https://coveralls.io/repos/seanedwards/cfer/badge.svg)](https://coveralls.io/r/seanedwards/cfer)
5
4
  [![Gem Version](https://badge.fury.io/rb/cfer.svg)](http://badge.fury.io/rb/cfer)
5
+ [![Code Climate](https://codeclimate.com/github/seanedwards/cfer/badges/gpa.svg)](https://codeclimate.com/github/seanedwards/cfer)
6
+ [![Test Coverage](https://codeclimate.com/github/seanedwards/cfer/badges/coverage.svg)](https://codeclimate.com/github/seanedwards/cfer/coverage)
7
+ [![Issue Count](https://codeclimate.com/github/seanedwards/cfer/badges/issue_count.svg)](https://codeclimate.com/github/seanedwards/cfer)
8
+
6
9
 
7
10
  Cfer is a lightweight toolkit for managing CloudFormation templates.
8
11
 
9
12
  Read about Cfer [here](http://tilmonedwards.com/2015/07/28/cfer.html).
10
13
 
14
+ ## Support
15
+
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
+
18
+ If you would like support or guidance on Cfer, or CloudFormation in general, I offer DevOps consulting services. Please [Contact Bitlancer](http://www.bitlancer.com/contact-us/) and we'll be happy to discuss your needs.
19
+
11
20
  ## Installation
12
21
 
13
22
  Add this line to your application's Gemfile:
@@ -35,12 +44,12 @@ cfer converge instance -t examples/instance.rb --profile [YOUR-PROFILE] --region
35
44
 
36
45
  You should see something like this:
37
46
 
38
- ![Demo](cfer-demo.gif)
47
+ ![Demo](https://raw.githubusercontent.com/seanedwards/cfer/master/doc/cfer-demo.gif)
39
48
 
40
49
  ### Command line
41
50
 
42
51
  Commands:
43
- cfer converge [OPTIONS] <stack-name> # Converges a cloudformation stack according to the template
52
+ cfer converge [OPTIONS] <stack-name> # Creates or updates a cloudformation stack according to the template
44
53
  cfer generate [OPTIONS] <template.rb> # Generates a CloudFormation template by evaluating a Cfer template
45
54
  cfer help [COMMAND] # Describe available commands or one specific command
46
55
  cfer tail <stack-name> # Follows stack events on standard output as they occur
@@ -65,10 +74,12 @@ Creates or updates a CloudFormation stack according to the specified template.
65
74
 
66
75
  The following options may be used with the `converge` command:
67
76
 
68
- * `--follow` (`-f`): Follows stack events on standard output as the create/update process takes place.
77
+ * `--follow` (`-f`): Follows stack events on standard output as the create/update process takes place.
69
78
  * `--stack-file <template.rb>`: Reads this file from the filesystem, rather than the default `<stack-name>.rb`
70
79
  * `--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
71
80
  * `--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
+ * `--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
+ * `--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.
72
83
 
73
84
  #### `tail <stack-name>`
74
85
 
@@ -76,7 +87,7 @@ Prints the latest `n` stack events, and optionally follows events while a stack
76
87
 
77
88
  The following options may be used with the `tail` command:
78
89
 
79
- * `--follow` (`-f`): Follows stack events on standard output as the create/update process takes place.
90
+ * `--follow` (`-f`): Follows stack events on standard output as the create/update process takes place.
80
91
  * `--number` (`-n`): Print the last `n` stack events.
81
92
 
82
93
  ### Template Anatomy
@@ -93,8 +104,6 @@ parameter :ParameterName,
93
104
  default: 'ParameterValue'
94
105
  ```
95
106
 
96
- A parameter's value may have the form `@stack.output` to look up output values from other stacks in the same account and region. This works anywhere a parameter value is specified, including defaults and inputs. (See the SDK section on [Cfer Stacks](#cfer-stacks) for caveats.)
97
-
98
107
  Any parameter can be referenced either in Ruby by using the `parameters` hash:
99
108
 
100
109
  ```ruby
@@ -139,6 +148,12 @@ Outputs may be defined using the `output` function:
139
148
  output :OutputName, Fn::ref(:ResourceName)
140
149
  ```
141
150
 
151
+ Outputs may be retireved from other stacks anywhere in a template by using the `lookup_output` function.
152
+
153
+ ```ruby
154
+ lookup_output('stack_name', 'output_name')
155
+ ```
156
+
142
157
  #### Including code from multiple files
143
158
 
144
159
  Templates can get pretty large, and splitting template code into multiple
@@ -229,15 +244,13 @@ stack = Cfer::stack_from_block(client: <client>) do
229
244
  end
230
245
  ```
231
246
 
232
- Note: Specifying a client is optional, but if no client is specified, parameter mappings will not occur.
233
-
234
247
  ## Contributing
235
248
 
236
249
  This project uses [git-flow](http://nvie.com/posts/a-successful-git-branching-model/). Please name branches and pull requests according to that convention.
237
250
 
238
251
  Always use `--no-ff` when merging into `develop` or `master`.
239
252
 
240
- This project also contains a [Code of Conduct](CODE_OF_CONDUCT.md), which should be followed when submitting feedback or contributions to this project.
253
+ This project also contains a [Code of Conduct](https://github.com/seanedwards/cfer/blob/master/CODE_OF_CONDUCT.md), which should be followed when submitting feedback or contributions to this project.
241
254
 
242
255
  ### New features
243
256
 
@@ -263,3 +276,28 @@ This project also contains a [Code of Conduct](CODE_OF_CONDUCT.md), which should
263
276
  * Merge into `develop` and `master`
264
277
  * Name branch `release/<major.minor>`
265
278
 
279
+ # Release Notes
280
+
281
+ ## 0.3.0
282
+
283
+ ### Enhancements:
284
+ * `parameters` hash now includes parameters that are set on the existing stack, but not passed in via CLI during a stack update.
285
+ * `parameters` hash now includes defaults for parameters that were not passed on the CLI during a stack creation.
286
+ * Adds a `lookup_output` function, for looking up outputs of stacks in the same account+region. (See #8)
287
+ * Adds provisioning for cfn-init and chef-solo, including resource signaling.
288
+ * Adds support for stack policies.
289
+ * Cfer no longer validates parameters itself. CloudFormation will throw an error if something is wrong.
290
+ * Adds release notes to the README.
291
+
292
+ ### Bugfixes:
293
+ * Removes automatic parameter mapping in favor of an explicit function available to resources. (Fixes Issue #8)
294
+ * No more double-printing the stack summary when converging a stack with tailing enabled.
295
+ * Update demo to only use 2 AZs, since us-west-1 only has two.
296
+ * `AllowedValues` attribute on parameters is now an array, not a CSV string. (Thanks to @rlister)
297
+
298
+ ## 0.2.0
299
+
300
+ ### Enhancements:
301
+ * Adds support for including other files via `include_template` function.
302
+ * Adds basic Dockerfile
303
+
data/Rakefile CHANGED
@@ -3,13 +3,11 @@ gem 'cfer'
3
3
  require 'cfer'
4
4
  require 'highline'
5
5
 
6
- Cfer::LOGGER.level = Logger::DEBUG
7
-
8
6
  task :default => [:spec]
9
7
 
10
8
  task :config_aws, [:profile] do |t, args|
11
- Aws.config.update region: ENV['AWS_REGION'] || 'us-east-1',
12
- credentials: Aws::SharedCredentials.new(profile_name: ENV['AWS_PROFILE'] || 'default')
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' })
13
11
  end
14
12
 
15
13
  task :vpc => :config_aws do |t, args|
@@ -22,7 +20,7 @@ task :describe_vpc => :config_aws do
22
20
  Cfer.describe! 'vpc'
23
21
  end
24
22
 
25
- task :instance => :config_aws do |t, args|
23
+ task :instance => :vpc do |t, args|
26
24
  key_pair = ask("Enter your EC2 KeyPair name: ")
27
25
 
28
26
  Cfer.converge! 'instance',
@@ -48,10 +46,11 @@ task :converge => [:vpc, :instance]
48
46
  # This task isn't really part of Cfer.
49
47
  # It just makes it easier for me to release new versions.
50
48
  task :release do
49
+ `git checkout master`
50
+ `git merge develop --no-ff -m 'Merge from develop for release'`
51
+
51
52
  require_relative 'lib/cfer/version.rb'
52
53
 
53
- `git checkout master`
54
- `git merge develop --no-ff -m 'Merge from develop for release v#{Cfer::VERSION}'`
55
54
  `git tag -m "Release v#{Cfer::VERSION}" #{Cfer::VERSION}`
56
55
  end
57
56
 
@@ -7,7 +7,7 @@ Gem::Specification.new do |spec|
7
7
  spec.name = "cfer"
8
8
  spec.version = Cfer::VERSION
9
9
  spec.authors = ["Sean Edwards"]
10
- spec.email = ["stedwards87+git@gmail.com"]
10
+ spec.email = ["stedwards87+cfer@gmail.com"]
11
11
 
12
12
  spec.summary = %q{Toolkit for automating infrastructure using AWS CloudFormation}
13
13
  spec.description = spec.summary
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.add_runtime_dependency 'highline'
31
31
  spec.add_runtime_dependency 'table_print'
32
32
  spec.add_runtime_dependency "rake"
33
+ spec.add_runtime_dependency "erubis"
33
34
 
34
35
  spec.add_development_dependency "bundler"
35
36
  spec.add_development_dependency "yard"
@@ -0,0 +1,56 @@
1
+ description 'Example stack template for a small EC2 instance'
2
+
3
+ # NOTE: This template depends on vpc.rb
4
+
5
+ # Include common template code that will be used for examples that create EC2 instances.
6
+ include_template 'common/instance_deps.rb'
7
+
8
+ resource :instance, "AWS::EC2::Instance",
9
+ # Set a creation policy so that the stack will wait for
10
+ # on-instance provisioning to complete before marking the instance
11
+ # as done.
12
+ :CreationPolicy => {
13
+ :ResourceSignal => {
14
+ :Count => 1
15
+ }
16
+ } do
17
+ # Chef provisioning depends on cfn-init, so set that up first.
18
+ # We will have the initial provisioning set up cfn-hup, install chef, and run our cookbooks.
19
+ # Cfn-hup will only rerun chef when the metadata changes.
20
+ cfn_init_setup signal: :instance,
21
+ cfn_init_config_set: [ :cfn_hup, :install_chef, :run_chef],
22
+ cfn_hup_config_set: [ :cfn_hup, :run_chef]
23
+
24
+ # Configure chef to generate a Berksfile that will download the AWS cookbook from the Chef supermarket.
25
+ # Set the run list to run the AWS cookbook, so our instance will have the AWS SDK available.
26
+ chef_solo version: 'latest',
27
+ node: {
28
+ cfer: {
29
+ demo: {
30
+ welcome: "Welcome to Cfer!"
31
+ }
32
+ },
33
+ run_list: 'recipe[ec2-demo]'
34
+ },
35
+ # We specify a berksfile inline, but you could read this from somewhere else in your repo too.
36
+ # This uses a simple cookbook to write a file, similar to the instance.rb example.
37
+ # Review this cookbook here: https://github.com/seanedwards/cfer-cookbook-demo
38
+ berksfile: <<-EOF
39
+ source "https://supermarket.chef.io"
40
+ cookbook 'ec2-demo', github: 'seanedwards/cfer-cookbook-demo', branch: 'master'
41
+ EOF
42
+
43
+ image_id Fn::ref(:ImageId)
44
+ instance_type Fn::ref(:InstanceType)
45
+ key_name Fn::ref(:KeyName)
46
+
47
+ network_interfaces [ {
48
+ AssociatePublicIpAddress: "true",
49
+ DeviceIndex: "0",
50
+ GroupSet: [ Fn::ref(:instancesg) ],
51
+ SubnetId: Fn::ref(:SubnetId)
52
+ } ]
53
+ end
54
+
55
+ output :instance, Fn::ref(:instance)
56
+ output :instanceip, Fn::get_att(:instance, :PublicIp)
@@ -0,0 +1,34 @@
1
+ # By not specifying a default value, a parameter becomes required.
2
+ # Specify this parameter by adding `--parameters KeyName:<ec2-keyname>` to your CLI options.
3
+ parameter :KeyName
4
+
5
+ # We define some more parameters the same way we did in the VPC template.
6
+ # Cfer will fetch the output values from the `vpc` stack we created earlier.
7
+ #
8
+ # If you created the VPC stack with a different name, you can overwrite these default values
9
+ # by adding `Vpc:<vpc_stack_name> to your `--parameters` option
10
+ parameter :Vpc, default: 'vpc'
11
+ parameter :VpcId, default: lookup_output(parameters[:Vpc], 'vpcid')
12
+ parameter :SubnetId, default: lookup_output(parameters[:Vpc], 'subnetid1')
13
+
14
+ # This is the Ubuntu 14.04 LTS HVM AMI provided by Amazon.
15
+ parameter :ImageId, default: 'ami-fce3c696'
16
+ parameter :InstanceType, default: 't2.nano'
17
+
18
+ # Define a security group to be applied to an instance.
19
+ # This one will allow SSH access from anywhere, and no other inbound traffic.
20
+ resource :instancesg, "AWS::EC2::SecurityGroup" do
21
+ group_description 'Wide-open SSH'
22
+ vpc_id Fn::ref(:VpcId)
23
+
24
+ # Parameter values can be Ruby arrays and hashes. These will be transformed to JSON.
25
+ # You could write your own functions to make stuff like this easier, too.
26
+ security_group_ingress [
27
+ {
28
+ CidrIp: '0.0.0.0/0',
29
+ IpProtocol: 'tcp',
30
+ FromPort: 22,
31
+ ToPort: 22
32
+ }
33
+ ]
34
+ end
@@ -2,42 +2,8 @@ description 'Example stack template for a small EC2 instance'
2
2
 
3
3
  # NOTE: This template depends on vpc.rb
4
4
 
5
-
6
- # By not specifying a default value, a parameter becomes required.
7
- # Specify this parameter by adding `--parameters KeyName:<ec2-keyname>` to your CLI options.
8
- parameter :KeyName
9
-
10
- # We define some more parameters the same way we did in the VPC template.
11
- # Cfer will interpret the default value by looking up the stack output named `vpcid`
12
- # on the stack named `vpc`.
13
- #
14
- # If you created the VPC stack with a different name, you can overwrite these default values
15
- # by adding `VpcId:@<vpc-stack-name>.vpcid SubnetId:@<vpc-stack-name>.subnetid1`
16
- # to your `--parameters` option
17
- parameter :VpcId, default: '@vpc.vpcid'
18
- parameter :SubnetId, default: '@vpc.subnetid1'
19
-
20
- # This is the Ubuntu 14.04 LTS HVM AMI provided by Amazon.
21
- parameter :ImageId, default: 'ami-d05e75b8'
22
- parameter :InstanceType, default: 't2.medium'
23
-
24
- # Define a security group to be applied to an instance.
25
- # This one will allow SSH access from anywhere, and no other inbound traffic.
26
- resource :instancesg, "AWS::EC2::SecurityGroup" do
27
- group_description 'Wide-open SSH'
28
- vpc_id Fn::ref(:VpcId)
29
-
30
- # Parameter values can be Ruby arrays and hashes. These will be transformed to JSON.
31
- # You could write your own functions to make stuff like this easier, too.
32
- security_group_ingress [
33
- {
34
- CidrIp: '0.0.0.0/0',
35
- IpProtocol: 'tcp',
36
- FromPort: 22,
37
- ToPort: 22
38
- }
39
- ]
40
- end
5
+ # Include common template code that will be used for examples that create EC2 instances.
6
+ include_template 'common/instance_deps.rb'
41
7
 
42
8
  # We can define extension objects, which extend the basic JSON-building
43
9
  # functionality of Cfer. Cfer provides a few of these, but you're free
@@ -82,3 +48,4 @@ resource :instance, "AWS::EC2::Instance" do
82
48
  end
83
49
 
84
50
  output :instance, Fn::ref(:instance)
51
+ output :instanceip, Fn::get_att(:instance, :PublicIp)
@@ -49,7 +49,7 @@ resource :routetable, 'AWS::EC2::RouteTable' do
49
49
  vpc_id Fn::ref(:vpc)
50
50
  end
51
51
 
52
- (1..3).each do |i|
52
+ (1..2).each do |i|
53
53
  resource "subnet#{i}", 'AWS::EC2::Subnet' do
54
54
  # Other CloudFormation intrinsics, such as `Fn::Select` and `AWS::Region` are available as Ruby objects
55
55
  # Inspecting these functions will reveal that they simply return a Ruby hash representing the same CloudFormation structures
@@ -20,22 +20,30 @@ module Cfer
20
20
  module Core
21
21
  end
22
22
 
23
+ %w{
24
+ DB
25
+ ASG
26
+ }.each do |acronym|
27
+ ActiveSupport::Inflector.inflections.acronym acronym
28
+ end
29
+
23
30
  # The Cfer logger
24
31
  LOGGER = Logger.new(STDERR)
25
32
  LOGGER.level = Logger::INFO
26
- LOGGER.formatter = proc { |severity, datetime, progname, msg|
27
- msg = case severity
28
- when 'FATAL'
29
- Rainbow(msg).red.bright
30
- when 'ERROR'
31
- Rainbow(msg).red
32
- when 'WARN'
33
- Rainbow(msg).yellow
34
- when 'DEBUG'
35
- Rainbow(msg).black.bright
36
- else
37
- msg
38
- end
33
+ LOGGER.formatter = proc { |severity, _datetime, _progname, msg|
34
+ msg =
35
+ case severity
36
+ when 'FATAL'
37
+ Rainbow(msg).red.bright
38
+ when 'ERROR'
39
+ Rainbow(msg).red
40
+ when 'WARN'
41
+ Rainbow(msg).yellow
42
+ when 'DEBUG'
43
+ Rainbow(msg).black.bright
44
+ else
45
+ msg
46
+ end
39
47
 
40
48
  "#{msg}\n"
41
49
  }
@@ -47,8 +55,8 @@ module Cfer
47
55
  tmpl = options[:template] || "#{stack_name}.rb"
48
56
  cfn = options[:aws_options] || {}
49
57
 
50
- cfn_stack = Cfer::Cfn::Client.new(cfn.merge(stack_name: stack_name))
51
- stack = Cfer::stack_from_file(tmpl, options.merge(client: cfn_stack))
58
+ cfn_stack = options[:cfer_client] || Cfer::Cfn::Client.new(cfn.merge(stack_name: stack_name))
59
+ stack = options[:cfer_stack] || Cfer::stack_from_file(tmpl, options.merge(client: cfn_stack))
52
60
 
53
61
  begin
54
62
  cfn_stack.converge(stack, options)
@@ -58,25 +66,36 @@ module Cfer
58
66
  rescue Aws::CloudFormation::Errors::ValidationError => e
59
67
  Cfer::LOGGER.info "CFN validation error: #{e.message}"
60
68
  end
61
- describe! stack_name, options
69
+ describe! stack_name, options unless options[:follow]
62
70
  end
63
71
 
64
72
  def describe!(stack_name, options = {})
65
73
  config(options)
66
74
  cfn = options[:aws_options] || {}
67
- cfn_stack = Cfer::Cfn::Client.new(cfn.merge(stack_name: stack_name)).fetch_stack
75
+ cfn_stack = options[:cfer_client] || Cfer::Cfn::Client.new(cfn.merge(stack_name: stack_name))
76
+ cfn_stack = cfn_stack.fetch_stack
68
77
 
69
78
  Cfer::LOGGER.debug "Describe stack: #{cfn_stack}"
70
79
 
71
- case options[:output_format] || 'table'
80
+ case options[:output_format]
72
81
  when 'json'
73
82
  puts render_json(cfn_stack, options)
74
- when 'table'
83
+ when 'table', nil
75
84
  puts "Status: #{cfn_stack[:stack_status]}"
76
85
  puts "Description: #{cfn_stack[:description]}" if cfn_stack[:description]
77
86
  puts ""
78
- parameters = (cfn_stack[:parameters] || []).map { |param| {:Type => "Parameter", :Key => param[:parameter_key], :Value => param[:parameter_value]} }
79
- outputs = (cfn_stack[:outputs] || []).map { |output| {:Type => "Output", :Key => output[:output_key], :Value => output[:output_value]} }
87
+ def tablify(list, type)
88
+ list ||= []
89
+ list.map { |param|
90
+ {
91
+ :Type => type.to_s.titleize,
92
+ :Key => param[:"#{type}_key"],
93
+ :Value => param[:"#{type}_value"]
94
+ }
95
+ }
96
+ end
97
+ parameters = tablify(cfn_stack[:parameters] || [], 'parameter')
98
+ outputs = tablify(cfn_stack[:outputs] || [], 'output')
80
99
  tp parameters + outputs, :Type, :Key, {:Value => {:width => 80}}
81
100
  else
82
101
  raise Cfer::Util::CferError, "Invalid output format #{options[:output_format]}."
@@ -86,7 +105,7 @@ module Cfer
86
105
  def tail!(stack_name, options = {}, &block)
87
106
  config(options)
88
107
  cfn = options[:aws_options] || {}
89
- cfn_client = Cfer::Cfn::Client.new(cfn.merge(stack_name: stack_name))
108
+ cfn_client = options[:cfer_client] || Cfer::Cfn::Client.new(cfn.merge(stack_name: stack_name))
90
109
  if block
91
110
  cfn_client.tail(options, &block)
92
111
  else
@@ -143,7 +162,7 @@ module Cfer
143
162
 
144
163
  def render_json(obj, options = {})
145
164
  if options[:pretty_print]
146
- puts JSON.pretty_generate(obj)
165
+ puts JSON.pretty_generate(obj, options)
147
166
  else
148
167
  puts obj.to_json
149
168
  end