aws-cft-tools 0.1.0 → 0.1.1
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.md +23 -0
- data/README.md +65 -0
- data/code.json +1 -1
- data/exe/aws-cft +8 -1
- data/lib/aws_cft_tools.rb +1 -0
- data/lib/aws_cft_tools/client/cft/changeset_management.rb +25 -5
- data/lib/aws_cft_tools/client/cft/stack_management.rb +16 -2
- data/lib/aws_cft_tools/runbook.rb +12 -0
- data/lib/aws_cft_tools/runbooks/common/changesets.rb +1 -0
- data/lib/aws_cft_tools/runbooks/deploy.rb +23 -6
- data/lib/aws_cft_tools/runbooks/deploy/threading.rb +3 -20
- data/lib/aws_cft_tools/threaded_output.rb +103 -0
- data/lib/aws_cft_tools/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e2c4148f5b7aa3cf1465fcc735a1b3ebd8b559d9
|
|
4
|
+
data.tar.gz: 654d8ffb95b10000a1e3d364803fc014aaf2788a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 31d7266d177d7cc7359b35ddea6271b4c4f1d14ef8e4fdf850056e6a7823ffc4bef9ee802c85513e99944bab14110098c33e568ca1faa58883a41ae44efc6e60
|
|
7
|
+
data.tar.gz: 12d6742f6050e73e68a0a54d10138ba21d33fc3ac10be49e2b150ee248651b7be6f6337d34558e8a5ea51153e174b2a59e9487977e968897f74debb1652a5913
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.1] - 2017-12-15
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
* The `-d` and `--debug` options for `deploy` show more narrative. This can be useful if running parallel
|
|
8
|
+
jobs in a deployment (`-j` option with a value greater than one). This option is available for all
|
|
9
|
+
runbooks, but only the `deploy` runbook uses it in this release.
|
|
10
|
+
* Documentation on why `aws-cft-tools` is better than plain vanilla CloudFormation or Terraform. (Issue #10)
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
* Parallel deployments use threads. We weren't always handling output properly, so some output could be lost
|
|
15
|
+
if there was an error. We've reworked how we coordinate output from threads to reduce the chance of this
|
|
16
|
+
happening. (Issue #7)
|
|
17
|
+
* Deployments special case the `-j1` option to avoid threads. If you run into issues with `-j` and a value
|
|
18
|
+
greater than one, run without the option or with `-j1` to make sure that threads aren't the source of the
|
|
19
|
+
problem. (Issue #7)
|
|
20
|
+
|
|
21
|
+
## [0.1.0] - 2017-11-29
|
|
22
|
+
|
|
23
|
+
Initial release.
|
data/README.md
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
# AwsCftTools
|
|
2
2
|
|
|
3
|
+
[](https://badge.fury.io/rb/aws-cft-tools)
|
|
4
|
+
[](https://travis-ci.org/USSBA/aws-cft-tools)
|
|
5
|
+
|
|
3
6
|
CloudFormation and related services provide a way to manage infrastructure state in "the cloud." This
|
|
4
7
|
gem and its included command (`aws-cft`) build on top of this state management system to create an
|
|
5
8
|
infrastructure management solution native to the AWS environment.
|
|
6
9
|
|
|
10
|
+
`aws-cft-tools` empowers users to organize their CloudFormation templates using any form of directory
|
|
11
|
+
structure, without the need to tediously deploy their templates in a specific order or create quickly
|
|
12
|
+
outdated scripts to manage the deployment thereof. This project links together templates using the
|
|
13
|
+
Export/ImportValue features of CloudFormation to determine the order of operations, manages stack
|
|
14
|
+
names, and supports multiple parallel "Environments" within a single AWS account.
|
|
15
|
+
|
|
7
16
|
## Installation
|
|
8
17
|
|
|
9
18
|
Add this line to your application's Gemfile:
|
|
@@ -71,6 +80,62 @@ update the version number in `version.rb`, and then run `bundle exec rake releas
|
|
|
71
80
|
tag for the version, push git commits and tags, and push the `.gem` file to
|
|
72
81
|
[rubygems.org](https://rubygems.org).
|
|
73
82
|
|
|
83
|
+
## Why `aws-cft-tools`?
|
|
84
|
+
|
|
85
|
+
`aws-cft-tools` is designed to work in an "infrastructure as code" DevOps environment. Infrastructure is
|
|
86
|
+
software that is developed, tested, peer reviewed, and finally merged and deployed.
|
|
87
|
+
|
|
88
|
+
### "Vanilla" CloudFormation
|
|
89
|
+
|
|
90
|
+
When first using CloudFormation, it is very easy to launch a single stack and get off the ground quickly.
|
|
91
|
+
As you move forward, users quickly find out that their Templates need to be managed in source control.
|
|
92
|
+
Later, users want to test their infrastructure changes in a different Environment, so a "dev" layer is
|
|
93
|
+
created, then an "integration", then a "staging", etc. Before too long, launching stacks is a nightmare
|
|
94
|
+
due to dependency conflicts, manual naming failures of Stacks, typos, and so on. On top of that,
|
|
95
|
+
remembering which Stacks have been deployed for which environment becomes impossible, so infrastructure
|
|
96
|
+
drift is inevitable.
|
|
97
|
+
|
|
98
|
+
This tool builds on top of the normal progression of teams using CloudFormation, enabling managed
|
|
99
|
+
Environments using parameters on templates. It offers simple deployments to roll out a full stack in
|
|
100
|
+
a new environment with a single command. It allows developers to continue to use CloudFormation for all
|
|
101
|
+
their infrastructure, while vastly simplifying the deployment and retraction process.
|
|
102
|
+
|
|
103
|
+
### Ansible
|
|
104
|
+
|
|
105
|
+
[Ansible](https://www.ansible.com/) provides features that are a mix of infrastructure management and
|
|
106
|
+
instance configuration. For example, Ansible can do the work of TerraForm and Chef, combined. However,
|
|
107
|
+
Ansible works best when working with an expected inventory of resources. It makes changes to bring
|
|
108
|
+
infrastructure in line with the inventory. `aws-cft-tools` only manages CloudFormation templates and leaves
|
|
109
|
+
configuration of instances to other tools such as Chef or Ansible.
|
|
110
|
+
|
|
111
|
+
#### Using Ansible with `aws-cft-tools`
|
|
112
|
+
|
|
113
|
+
Ansible can manage the production of an Amazon Machine Image (AMI). It can spin up a temporary EC2 instance
|
|
114
|
+
and install all of the necessary system packages, make any configuration changes, and trigger the creation
|
|
115
|
+
of a tagged AMI. If the AMI is tagged with an Environment and Role, then `aws-cft-tools` can discover the
|
|
116
|
+
AMI and provide it as a parameter to any CloudFormation stacks that require the image. For example, creating
|
|
117
|
+
a new AMI and then using `aws-cft-tools` to deploy the CloudFormation Template for an auto-scaling group
|
|
118
|
+
that uses that AMI can result in the deployment of a new version of an application.
|
|
119
|
+
|
|
120
|
+
### TerraForm
|
|
121
|
+
|
|
122
|
+
[TerraForm](https://www.terraform.io/) and `aws-cft-tools` are solving similar problems with fundamentally
|
|
123
|
+
different approaches. TerraForm is designed to work with multiple cloud providers while `aws-cft-tools` is
|
|
124
|
+
specific to AWS. So TerraForm can't depend on features that aren't provided by all cloud providers. Thus,
|
|
125
|
+
TerraForm requires a state file that introduces some complexity into managing infrastructure.
|
|
126
|
+
|
|
127
|
+
Using `aws-cft-tools` doesn't mean infrastructure management is less complex than when using TerraForm. Only
|
|
128
|
+
that the complexity is different. Instead of managing a state file outside of AWS, `aws-cft-tools` assumes
|
|
129
|
+
that AWS is the source of all state information.
|
|
130
|
+
|
|
131
|
+
Rather than computing changes, for example, `aws-cft-tools` requests a list of changes from AWS for a given
|
|
132
|
+
change in template and parameters. This does take more time than if all of that information was in a local
|
|
133
|
+
state file, but it ensures that any changes reflect the current deployment.
|
|
134
|
+
|
|
135
|
+
In exchange for taking a little more time to make changes (e.g., pull requests and code reviews after
|
|
136
|
+
initial development), teams can work on different parts of the infrastructure without having to coordinate
|
|
137
|
+
with each other.
|
|
138
|
+
|
|
74
139
|
## Building Gem for Local Use
|
|
75
140
|
|
|
76
141
|
```shell
|
data/code.json
CHANGED
data/exe/aws-cft
CHANGED
|
@@ -21,6 +21,7 @@ Clamp do
|
|
|
21
21
|
option ['-T', '--tag'], 'NAME:VALUE', 'require a tag have the given value (may be given more than once)',
|
|
22
22
|
multivalued: true
|
|
23
23
|
option ['-v', '--[no-]verbose'], :flag, 'verbose narration of actions'
|
|
24
|
+
option ['-D', '--debug'], :flag, 'extra verbosity to aid in debugging'
|
|
24
25
|
option '--version', :flag, 'Show version' do
|
|
25
26
|
puts AwsCftTools::VERSION
|
|
26
27
|
exit(0)
|
|
@@ -160,10 +161,16 @@ Clamp do
|
|
|
160
161
|
profile: profile,
|
|
161
162
|
root: root,
|
|
162
163
|
region: region,
|
|
164
|
+
tags: tag_hash
|
|
165
|
+
}.merge(flag_options)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def flag_options
|
|
169
|
+
{
|
|
163
170
|
noop: noop?,
|
|
164
171
|
check: check?,
|
|
165
172
|
verbose: verbose?,
|
|
166
|
-
|
|
173
|
+
debug: debug?
|
|
167
174
|
}
|
|
168
175
|
end
|
|
169
176
|
|
data/lib/aws_cft_tools.rb
CHANGED
|
@@ -23,6 +23,7 @@ module AwsCftTools
|
|
|
23
23
|
require 'aws_cft_tools/deletion_change'
|
|
24
24
|
require 'aws_cft_tools/client'
|
|
25
25
|
require 'aws_cft_tools/dependency_tree'
|
|
26
|
+
require 'aws_cft_tools/threaded_output'
|
|
26
27
|
require 'aws_cft_tools/stack'
|
|
27
28
|
require 'aws_cft_tools/template'
|
|
28
29
|
require 'aws_cft_tools/template_set'
|
|
@@ -53,16 +53,36 @@ module AwsCftTools
|
|
|
53
53
|
id = id_params(params)
|
|
54
54
|
|
|
55
55
|
aws_client.create_change_set(params)
|
|
56
|
-
|
|
57
|
-
aws_client.wait_until(:change_set_create_complete, id)
|
|
58
|
-
|
|
56
|
+
return [] if wait_for_changeset(id) == :nochanges
|
|
59
57
|
mapped_changes(AWSEnumerator.new(aws_client, :describe_change_set, id, &:changes).to_a)
|
|
60
|
-
rescue Aws::Waiters::Errors::FailureStateError
|
|
61
|
-
[]
|
|
62
58
|
ensure
|
|
63
59
|
aws_client.delete_change_set(id)
|
|
64
60
|
end
|
|
65
61
|
|
|
62
|
+
def wait_for_changeset(id, times_waited = 0)
|
|
63
|
+
times_waited += 1
|
|
64
|
+
aws_client.wait_until(:change_set_create_complete, id)
|
|
65
|
+
rescue Aws::Waiters::Errors::FailureStateError
|
|
66
|
+
status = check_failure(id)
|
|
67
|
+
return status unless status == :retry
|
|
68
|
+
raise_if_too_many_retries(params, times_waited)
|
|
69
|
+
sleep(2**times_waited + 1)
|
|
70
|
+
retry
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def raise_if_too_many_retries(params, retries)
|
|
74
|
+
return if retries < 5
|
|
75
|
+
raise CloudFormationError, "Error waiting on changeset for #{params[:stack_name]}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def check_failure(id)
|
|
79
|
+
status = aws_client.describe_change_set(id)
|
|
80
|
+
return :retry unless status.status == 'FAILED'
|
|
81
|
+
return :no_changes if status.status_reason.match?(/didn't contain changes/)
|
|
82
|
+
raise CloudFormationError,
|
|
83
|
+
"Error creating changeset for #{params[:stack_name]}: #{status.status_reason}"
|
|
84
|
+
end
|
|
85
|
+
|
|
66
86
|
def id_params(params)
|
|
67
87
|
{
|
|
68
88
|
change_set_name: params[:change_set_name],
|
|
@@ -25,7 +25,7 @@ module AwsCftTools
|
|
|
25
25
|
def update_stack(template)
|
|
26
26
|
aws_client.update_stack(update_stack_params(template))
|
|
27
27
|
# we want to wait for the update to complete before we proceed
|
|
28
|
-
|
|
28
|
+
wait_for_stack_operation(:stack_update_complete, template.name)
|
|
29
29
|
rescue Aws::CloudFormation::Errors::ValidationError => exception
|
|
30
30
|
raise exception unless exception.message.match?(/No updates/)
|
|
31
31
|
end
|
|
@@ -39,7 +39,7 @@ module AwsCftTools
|
|
|
39
39
|
def create_stack(template)
|
|
40
40
|
aws_client.create_stack(create_stack_params(template))
|
|
41
41
|
# we want to wait for the create to complete before we proceed
|
|
42
|
-
|
|
42
|
+
wait_for_stack_operation(:stack_create_complete, template.name)
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
##
|
|
@@ -55,6 +55,20 @@ module AwsCftTools
|
|
|
55
55
|
|
|
56
56
|
private
|
|
57
57
|
|
|
58
|
+
def wait_for_stack_operation(op, stack_name, times_waited = 0)
|
|
59
|
+
times_waited += 1
|
|
60
|
+
aws_client.wait_until(op, stack_name: stack_name)
|
|
61
|
+
rescue Aws::Waiters::Errors::FailureStateError
|
|
62
|
+
raise_if_too_many_retries(stack_name, times_waited)
|
|
63
|
+
sleep(2**times_waited + 1)
|
|
64
|
+
retry
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def raise_if_too_many_retries(stack_name, retries)
|
|
68
|
+
return if retries < 5
|
|
69
|
+
raise CloudFormationError, "Error waiting on stack operation for #{stack_name}"
|
|
70
|
+
end
|
|
71
|
+
|
|
58
72
|
def update_stack_params(template)
|
|
59
73
|
common_stack_params(template).merge(
|
|
60
74
|
use_previous_template: false,
|
|
@@ -48,6 +48,7 @@ module AwsCftTools
|
|
|
48
48
|
def initialize(configuration = {})
|
|
49
49
|
@options = configuration
|
|
50
50
|
@client = AwsCftTools::Client.new(options)
|
|
51
|
+
@stdout = $stdout
|
|
51
52
|
end
|
|
52
53
|
|
|
53
54
|
# @!group Callbacks
|
|
@@ -137,6 +138,17 @@ module AwsCftTools
|
|
|
137
138
|
end
|
|
138
139
|
end
|
|
139
140
|
|
|
141
|
+
##
|
|
142
|
+
# @param note [String] a debug note
|
|
143
|
+
#
|
|
144
|
+
# Prints the given content to stdout if running in +debug+ mode. Debug statements are output
|
|
145
|
+
# without any capture when running multiple threads.
|
|
146
|
+
#
|
|
147
|
+
def debug(note = nil)
|
|
148
|
+
return unless note && options[:debug]
|
|
149
|
+
@stdout.puts "DEBUG\nDEBUG " + note.split(/\n/).join("\nDEBUG ") + "\nDEBUG"
|
|
150
|
+
end
|
|
151
|
+
|
|
140
152
|
##
|
|
141
153
|
# @param description [String] an optional verbose description
|
|
142
154
|
# @yield runs the block if in +verbose+ mode
|
|
@@ -48,19 +48,36 @@ module AwsCftTools
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def process_slice(templates)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
jobs = options[:jobs]
|
|
52
|
+
if jobs && jobs > 1
|
|
53
|
+
process_slice_threaded(templates)
|
|
54
|
+
else
|
|
55
|
+
templates.each(&method(:process_template))
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def process_slice_threaded(templates)
|
|
60
|
+
original_stdout = $stdout
|
|
61
|
+
$stdout = ThreadedOutput.new(original_stdout)
|
|
62
|
+
template_list = templates.map(&:name).join(', ')
|
|
63
|
+
debug("Creating threads for #{template_list}")
|
|
64
|
+
threads = create_threads(templates, &method(:process_template))
|
|
65
|
+
debug("Waiting on threads for #{template_list}")
|
|
66
|
+
threads.map(&:join)
|
|
55
67
|
ensure
|
|
56
|
-
$stdout =
|
|
68
|
+
$stdout = original_stdout
|
|
57
69
|
end
|
|
58
70
|
|
|
59
71
|
def process_template(template)
|
|
72
|
+
ThreadedOutput.prefix = template_name = template.name
|
|
73
|
+
debug("Processing #{template_name}")
|
|
60
74
|
is_update = deployed_templates.include?(template)
|
|
61
|
-
operation("#{is_update ? 'Updating' : 'Creating'}: #{
|
|
75
|
+
operation("#{is_update ? 'Updating' : 'Creating'}: #{template_name}") do
|
|
62
76
|
exec_template(template: template, type: is_update ? :update : :create)
|
|
63
77
|
end
|
|
78
|
+
ensure
|
|
79
|
+
$stdout.flush
|
|
80
|
+
debug("Finished processing #{template_name}")
|
|
64
81
|
end
|
|
65
82
|
|
|
66
83
|
def exec_template(params) # template:, type:
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'thread'
|
|
4
|
+
|
|
3
5
|
module AwsCftTools
|
|
4
6
|
module Runbooks
|
|
5
7
|
class Deploy
|
|
@@ -9,27 +11,8 @@ module AwsCftTools
|
|
|
9
11
|
module Threading
|
|
10
12
|
private
|
|
11
13
|
|
|
12
|
-
# FIXME: things don't always work out well when capturing output
|
|
13
|
-
# for now, we don't, and output gets mangled a bit when running with
|
|
14
|
-
# multiple jobs in parallel
|
|
15
|
-
def with_captured_stdout(capture)
|
|
16
|
-
old_stdout = $stdout
|
|
17
|
-
old_table_io = TablePrint::Config.io
|
|
18
|
-
TablePrint::Config.io = $stdout = capture
|
|
19
|
-
yield
|
|
20
|
-
ensure
|
|
21
|
-
$stdout = old_stdout
|
|
22
|
-
TablePrint::Config.io = old_table_io
|
|
23
|
-
end
|
|
24
|
-
|
|
25
14
|
def create_threads(list, &_block)
|
|
26
|
-
list.map { |item|
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def threaded_process(&block)
|
|
30
|
-
output = StringIO.new
|
|
31
|
-
thread = Thread.new { with_captured_stdout(output, &block) }
|
|
32
|
-
OpenStruct.new(output: output, thread: thread)
|
|
15
|
+
list.map { |item| Thread.new { yield item } }
|
|
33
16
|
end
|
|
34
17
|
end
|
|
35
18
|
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'forwardable'
|
|
4
|
+
require 'thread'
|
|
5
|
+
|
|
6
|
+
module AwsCftTools
|
|
7
|
+
##
|
|
8
|
+
# Provides a way to process output and prefix with a thread identifier. The object is shared by
|
|
9
|
+
# threads. Each thread should set its own prefix.
|
|
10
|
+
#
|
|
11
|
+
class ThreadedOutput
|
|
12
|
+
extend Forwardable
|
|
13
|
+
|
|
14
|
+
#
|
|
15
|
+
# @param real_stdout [IO] The file object that should be written to with prefixed text.
|
|
16
|
+
#
|
|
17
|
+
def initialize(real_stdout)
|
|
18
|
+
@stdout = real_stdout
|
|
19
|
+
@buffer = Hash.new { |hash, key| hash[key] = '' }
|
|
20
|
+
@semaphore = Mutex.new
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def_delegator :@semaphore, :synchronize, :guarded
|
|
24
|
+
def_delegators ThreadedOutput, :prefix
|
|
25
|
+
|
|
26
|
+
##
|
|
27
|
+
# The prefix for output from the current thread.
|
|
28
|
+
#
|
|
29
|
+
def self.prefix
|
|
30
|
+
Thread.current['output_prefix'] || ''
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
##
|
|
34
|
+
# @param prefix [String] The prefix for each line of text output by the calling thread.
|
|
35
|
+
#
|
|
36
|
+
def self.prefix=(prefix)
|
|
37
|
+
Thread.current['output_prefix'] = prefix + ': '
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
##
|
|
41
|
+
# Ensure all buffered text is output. If any text is output, a newline is output as well.
|
|
42
|
+
#
|
|
43
|
+
def flush
|
|
44
|
+
guarded { @stdout.puts prefix + buffer } if buffer != ''
|
|
45
|
+
self.buffer = ''
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
##
|
|
49
|
+
# Write the string to the output with prefixes as appropriate. If the string does not end in a
|
|
50
|
+
# newline, then the remaining text will be buffered until a newline is seen.
|
|
51
|
+
#
|
|
52
|
+
def write(string)
|
|
53
|
+
print(string)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
##
|
|
57
|
+
# Writes all of the arguments to the output with prefixes. Appends a newline to each argument.
|
|
58
|
+
#
|
|
59
|
+
# @param args [Array<String>]
|
|
60
|
+
#
|
|
61
|
+
def puts(*args)
|
|
62
|
+
print(args.join("\n") + "\n")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
##
|
|
66
|
+
# Writes all of the arugments to the output without newlines. Will output the prefix after each
|
|
67
|
+
# newline.
|
|
68
|
+
#
|
|
69
|
+
# @param args [Array<String>]
|
|
70
|
+
#
|
|
71
|
+
def print(*args)
|
|
72
|
+
append(args.join(''))
|
|
73
|
+
printable_lines.each do |line|
|
|
74
|
+
guarded { @stdout.puts prefix + line }
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def printable_lines
|
|
81
|
+
lines = buffer.split(/\n/)
|
|
82
|
+
if buffer[-1..-1] == "\n"
|
|
83
|
+
self.buffer = ''
|
|
84
|
+
else
|
|
85
|
+
self.buffer = lines.last
|
|
86
|
+
lines = lines[0..-2]
|
|
87
|
+
end
|
|
88
|
+
lines
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def buffer
|
|
92
|
+
@buffer[prefix]
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def append(value)
|
|
96
|
+
@buffer[prefix] += value
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def buffer=(string)
|
|
100
|
+
@buffer[prefix] = string
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: aws-cft-tools
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Small Business Administration
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2017-
|
|
11
|
+
date: 2017-12-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: aws-sdk
|
|
@@ -235,6 +235,7 @@ files:
|
|
|
235
235
|
- ".travis.yml"
|
|
236
236
|
- ".yardopts"
|
|
237
237
|
- BEST-PRACTICES.md
|
|
238
|
+
- CHANGELOG.md
|
|
238
239
|
- CONTRIBUTING.md
|
|
239
240
|
- Gemfile
|
|
240
241
|
- LICENSE
|
|
@@ -292,6 +293,7 @@ files:
|
|
|
292
293
|
- lib/aws_cft_tools/template_set/closure.rb
|
|
293
294
|
- lib/aws_cft_tools/template_set/dependencies.rb
|
|
294
295
|
- lib/aws_cft_tools/template_set/each_slice_state.rb
|
|
296
|
+
- lib/aws_cft_tools/threaded_output.rb
|
|
295
297
|
- lib/aws_cft_tools/version.rb
|
|
296
298
|
- rubycritic.reek
|
|
297
299
|
homepage: https://github.com/USSBA/aws-cft-tools
|