cfer 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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