stackup 1.0.4 → 1.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f70a25b2393218900fde09713ec91cfc485121c2
4
- data.tar.gz: 3f6c5693a0b23ab0dd30884f7d42ed45e61ccc68
3
+ metadata.gz: cd8f6344aa29806003cac476d52ecd7070747ec3
4
+ data.tar.gz: 1a64e7205d279d0e7922171de005ac6ddcbb08ef
5
5
  SHA512:
6
- metadata.gz: 2e41add2eb41807de33893aa472613741925e420e9ee327c4bbab879bbb11d9e798fb09c022c0d0820da26f53b6a375564b62771156040bcd6d5da975a6ffb05
7
- data.tar.gz: 9131421f4beeca6244df73b7e9302587a6ca9c9e99c17aa8aa43497c3fca0270654bbb40de212807140a70bec462e1a6bc8fb2713933c9c3be164f052f9dc034
6
+ metadata.gz: abbf1538c8f0a04dcbd72b180803f7eb9211c07ec74eda51078135f40fd31088eaf6a3442688a6a92cee886799374475f7c4ca5ea9a350fc6fb6b6ea2ccc7b33
7
+ data.tar.gz: 6fd7cb6adb0b1bd846ab7a347be74f630a4ac6514db282d1f5cd3073991ce0155f80b09b100e32fbcd965f1ad30b6ab6d9caf9c7e0342756d6ee7b9c8a34dee8
data/CHANGES.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 1.1.0 (2017-02-23)
2
+
3
+ * The `stackup` CLI now allows stack template and policy documents to be specified as URLs.
4
+ * Template URL must point to a template stored in an Amazon S3 bucket, e.g. `https://s3-ap-southeast-2.amazonaws.com/bucket/template.json`.
5
+ * Policy URL must point to an object located in an S3 bucket in the same region as the stack.
6
+
1
7
  ## 1.0.4 (2017-01-04)
2
8
 
3
9
  * Fix #34: make YAML parsing work in ruby-2.0.
data/bin/stackup CHANGED
@@ -8,6 +8,7 @@ require "multi_json"
8
8
  require "securerandom"
9
9
  require "stackup"
10
10
  require "stackup/differ"
11
+ require "stackup/source"
11
12
  require "stackup/version"
12
13
  require "stackup/yaml"
13
14
 
@@ -55,6 +56,8 @@ Clamp do
55
56
 
56
57
  def run(arguments)
57
58
  super(arguments)
59
+ rescue Stackup::Source::ReadError => e
60
+ signal_error e.message
58
61
  rescue Stackup::ServiceError => e
59
62
  signal_error e.message
60
63
  rescue Aws::Errors::MissingCredentialsError
@@ -126,14 +129,10 @@ Clamp do
126
129
  puts final_status unless final_status.nil?
127
130
  end
128
131
 
129
- def load_data(file)
130
- Stackup::YAML.load_file(file)
131
- rescue Errno::ENOENT
132
- signal_error "no such file: #{file.inspect}"
133
- end
134
-
135
- def load_parameters(file)
136
- Stackup::Parameters.new(load_data(file)).to_hash
132
+ def parameters_from_files
133
+ parameter_sources.map { |src|
134
+ Stackup::Parameters.new(src.data).to_hash
135
+ }.inject(:merge)
137
136
  end
138
137
 
139
138
  subcommand "status", "Print stack status." do
@@ -144,42 +143,58 @@ Clamp do
144
143
 
145
144
  end
146
145
 
147
- subcommand "up", "Create/update the stack" do
146
+ subcommand "up", "Create/update the stack." do
148
147
 
149
- option ["-t", "--template"], "FILE", "template file",
150
- :attribute_name => :template_file
148
+ option ["-t", "--template"], "FILE", "template source",
149
+ :attribute_name => :template_source,
150
+ &Stackup::Source.method(:new)
151
151
 
152
152
  option ["-T", "--use-previous-template"], :flag,
153
153
  "reuse the existing template"
154
154
 
155
155
  option ["-p", "--parameters"], "FILE", "parameters file (last wins)",
156
156
  :multivalued => true,
157
- :attribute_name => :parameter_file_list
157
+ :attribute_name => :parameter_sources,
158
+ &Stackup::Source.method(:new)
158
159
 
159
160
  option ["-o", "--override"], "PARAM=VALUE", "parameter overrides",
160
161
  :multivalued => true,
161
162
  :attribute_name => :override_list
162
163
 
163
164
  option "--tags", "FILE", "stack tags file",
164
- :attribute_name => :tags_file
165
+ :attribute_name => :tag_source,
166
+ &Stackup::Source.method(:new)
165
167
 
166
168
  option "--policy", "FILE", "stack policy file",
167
- :attribute_name => :policy_file
169
+ :attribute_name => :policy_source,
170
+ &Stackup::Source.method(:new)
168
171
 
169
172
  option "--on-failure", "ACTION",
170
173
  "when stack creation fails: DO_NOTHING, ROLLBACK, or DELETE",
171
174
  :default => "ROLLBACK"
172
175
 
173
176
  def execute
174
- unless template_file || use_previous_template?
177
+ unless template_source || use_previous_template?
175
178
  signal_usage_error "Specify either --template or --use-previous-template"
176
179
  end
177
180
  options = {}
178
- options[:template] = load_data(template_file) if template_file
181
+ if template_source
182
+ if is_s3_url?(template_source.location)
183
+ options[:template_url] = template_source.location
184
+ else
185
+ options[:template] = template_source.data
186
+ end
187
+ end
179
188
  options[:on_failure] = on_failure
180
189
  options[:parameters] = parameters
181
- options[:tags] = load_data(tags_file) if tags_file
182
- options[:stack_policy] = load_data(policy_file) if policy_file
190
+ options[:tags] = tag_source.data if tag_source
191
+ if policy_source
192
+ if is_s3_url?(policy_source.location)
193
+ options[:stack_policy_url] = policy_source.location
194
+ else
195
+ options[:stack_policy] = policy_source.data
196
+ end
197
+ end
183
198
  options[:use_previous_template] = use_previous_template?
184
199
  report_change do
185
200
  stack.create_or_update(options)
@@ -189,9 +204,7 @@ Clamp do
189
204
  private
190
205
 
191
206
  def parameters
192
- hashes = parameter_file_list.map(&method(:load_parameters))
193
- hashes << parameter_overrides
194
- hashes.inject(:merge)
207
+ parameters_from_files.merge(parameter_overrides)
195
208
  end
196
209
 
197
210
  def parameter_overrides
@@ -203,36 +216,43 @@ Clamp do
203
216
  end
204
217
  end
205
218
 
219
+ def is_s3_url?(location)
220
+ location =~ %r{^https://s3\w*.amazonaws.com}
221
+ end
222
+
206
223
  end
207
224
 
208
225
  subcommand "diff", "Compare template/params to current stack." do
209
226
 
210
- option ["-t", "--template"], "FILE", "template file",
211
- :attribute_name => :template_file
227
+ option ["-t", "--template"], "FILE", "template source",
228
+ :attribute_name => :template_source,
229
+ &Stackup::Source.method(:new)
212
230
 
213
231
  option ["-p", "--parameters"], "FILE", "parameters file (last wins)",
214
232
  :multivalued => true,
215
- :attribute_name => :parameter_file_list
233
+ :attribute_name => :parameter_sources,
234
+ &Stackup::Source.method(:new)
216
235
 
217
236
  option "--tags", "FILE", "stack tags file",
218
- :attribute_name => :tags_file
237
+ :attribute_name => :tag_source,
238
+ &Stackup::Source.method(:new)
219
239
 
220
240
  option "--diff-format", "FORMAT", "'text', 'color', or 'html'", :default => "color"
221
241
 
222
242
  def execute
223
243
  current = {}
224
244
  planned = {}
225
- if template_file
245
+ if template_source
226
246
  current["Template"] = stack.template
227
- planned["Template"] = load_data(template_file)
247
+ planned["Template"] = template_source.data
228
248
  end
229
- unless parameter_file_list.empty?
249
+ unless parameter_sources.empty?
230
250
  current["Parameters"] = existing_parameters.sort.to_h
231
251
  planned["Parameters"] = new_parameters.sort.to_h
232
252
  end
233
- if tags_file
253
+ if tag_source
234
254
  current["Tags"] = stack.tags.sort.to_h
235
- planned["Tags"] = load_data(tags_file).sort.to_h
255
+ planned["Tags"] = tag_source.data.sort.to_h
236
256
  end
237
257
  signal_usage_error "specify '--template' or '--parameters'" if planned.empty?
238
258
  puts differ.diff(current, planned)
@@ -248,10 +268,6 @@ Clamp do
248
268
  @existing_parameters ||= stack.parameters
249
269
  end
250
270
 
251
- def parameters_from_files
252
- parameter_file_list.map(&method(:load_parameters)).inject(:merge)
253
- end
254
-
255
271
  def new_parameters
256
272
  existing_parameters.merge(parameters_from_files)
257
273
  end
@@ -268,7 +284,7 @@ Clamp do
268
284
 
269
285
  end
270
286
 
271
- subcommand "cancel-update", "Cancel the update in-progress" do
287
+ subcommand "cancel-update", "Cancel the update in-progress." do
272
288
 
273
289
  def execute
274
290
  report_change do
@@ -278,7 +294,7 @@ Clamp do
278
294
 
279
295
  end
280
296
 
281
- subcommand "wait", "Wait until stack is stable" do
297
+ subcommand "wait", "Wait until stack is stable." do
282
298
 
283
299
  def execute
284
300
  puts stack.wait
@@ -286,7 +302,7 @@ Clamp do
286
302
 
287
303
  end
288
304
 
289
- subcommand "events", "List stack events" do
305
+ subcommand "events", "List stack events." do
290
306
 
291
307
  option ["-f", "--follow"], :flag, "follow new events"
292
308
  option ["--data"], :flag, "display events as data"
@@ -376,7 +392,7 @@ Clamp do
376
392
 
377
393
  end
378
394
 
379
- subcommand "inspect", "Display stack particulars" do
395
+ subcommand "inspect", "Display stack particulars." do
380
396
 
381
397
  def execute
382
398
  data = {
@@ -0,0 +1,58 @@
1
+ require "aws-sdk-core"
2
+ require "open-uri"
3
+ require "stackup/stack"
4
+
5
+ module Stackup
6
+
7
+ # Represents a source of input, e.g. template, parameter, etc.
8
+ #
9
+ class Source
10
+
11
+ def initialize(location)
12
+ @location = location
13
+ end
14
+
15
+ attr_reader :location
16
+
17
+ def body
18
+ @body ||= read
19
+ end
20
+
21
+ def data
22
+ @data ||= parse_body
23
+ end
24
+
25
+ private
26
+
27
+ LOOKS_LIKE_JSON = /^\s*[\{\[]/
28
+
29
+ def uri
30
+ URI(location)
31
+ end
32
+
33
+ def read
34
+ if uri.scheme
35
+ uri.read
36
+ else
37
+ IO.read(location)
38
+ end
39
+ rescue Errno::ENOENT
40
+ raise ReadError, "no such file: #{location.inspect}"
41
+ rescue OpenURI::HTTPError => e
42
+ raise ReadError, "#{e}: #{location.inspect}"
43
+ end
44
+
45
+ def parse_body
46
+ if body =~ LOOKS_LIKE_JSON
47
+ MultiJson.load(body)
48
+ else
49
+ Stackup::YAML.load(body)
50
+ end
51
+ end
52
+
53
+ class ReadError < StandardError
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -1,5 +1,5 @@
1
1
  module Stackup
2
2
 
3
- VERSION = "1.0.4"
3
+ VERSION = "1.1.0"
4
4
 
5
5
  end
@@ -0,0 +1,75 @@
1
+ require "spec_helper"
2
+
3
+ require "multi_json"
4
+ require "stackup/source"
5
+ require "stackup/yaml"
6
+
7
+ describe Stackup::Source do
8
+
9
+ let(:example_dir) { File.expand_path("../../../examples", __FILE__) }
10
+
11
+ context "from a JSON file" do
12
+
13
+ let(:json_file) { File.join(example_dir, "template.json") }
14
+
15
+ subject(:source) { described_class.new(json_file) }
16
+
17
+ describe "#body" do
18
+
19
+ it "returns JSON body" do
20
+ expect(subject.body).to eql(File.read(json_file))
21
+ end
22
+
23
+ end
24
+
25
+ describe "#data" do
26
+
27
+ it "returns parsed JSON" do
28
+ expect(subject.data).to eql(MultiJson.load(File.read(json_file)))
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+ context "from a YAML file" do
36
+
37
+ let(:yaml_file) { File.join(example_dir, "template.yml") }
38
+
39
+ subject(:source) { described_class.new(yaml_file) }
40
+
41
+ describe "#body" do
42
+
43
+ it "returns YAML body" do
44
+ expect(subject.body).to eql(File.read(yaml_file))
45
+ end
46
+
47
+ end
48
+
49
+ describe "#data" do
50
+
51
+ it "returns parsed YAML" do
52
+ expect(subject.data).to eql(Stackup::YAML.load_file(yaml_file))
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+
59
+ context "with a non-existant file" do
60
+
61
+ let(:bogus_file) { "notreallythere.json" }
62
+
63
+ subject(:source) { described_class.new(bogus_file) }
64
+
65
+ describe "#body" do
66
+
67
+ it "raises a ReadError" do
68
+ expect { subject.body }.to raise_error(Stackup::Source::ReadError, %q(no such file: "notreallythere.json"))
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+
75
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stackup
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Williams
@@ -9,76 +9,76 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-01-04 00:00:00.000000000 Z
12
+ date: 2017-02-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aws-sdk-resources
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - ~>
18
+ - - "~>"
19
19
  - !ruby/object:Gem::Version
20
20
  version: '2.0'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - ~>
25
+ - - "~>"
26
26
  - !ruby/object:Gem::Version
27
27
  version: '2.0'
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: clamp
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - ~>
32
+ - - "~>"
33
33
  - !ruby/object:Gem::Version
34
34
  version: '1.0'
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - ~>
39
+ - - "~>"
40
40
  - !ruby/object:Gem::Version
41
41
  version: '1.0'
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: console_logger
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - '>='
46
+ - - ">="
47
47
  - !ruby/object:Gem::Version
48
48
  version: '0'
49
49
  type: :runtime
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
- - - '>='
53
+ - - ">="
54
54
  - !ruby/object:Gem::Version
55
55
  version: '0'
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: diffy
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
- - - ~>
60
+ - - "~>"
61
61
  - !ruby/object:Gem::Version
62
62
  version: '3.0'
63
63
  type: :runtime
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
- - - ~>
67
+ - - "~>"
68
68
  - !ruby/object:Gem::Version
69
69
  version: '3.0'
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: multi_json
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
- - - '>='
74
+ - - ">="
75
75
  - !ruby/object:Gem::Version
76
76
  version: '0'
77
77
  type: :runtime
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
- - - '>='
81
+ - - ">="
82
82
  - !ruby/object:Gem::Version
83
83
  version: '0'
84
84
  description:
@@ -100,6 +100,7 @@ files:
100
100
  - lib/stackup/parameters.rb
101
101
  - lib/stackup/rake_tasks.rb
102
102
  - lib/stackup/service.rb
103
+ - lib/stackup/source.rb
103
104
  - lib/stackup/stack.rb
104
105
  - lib/stackup/stack_watcher.rb
105
106
  - lib/stackup/utils.rb
@@ -107,6 +108,7 @@ files:
107
108
  - lib/stackup/yaml.rb
108
109
  - spec/spec_helper.rb
109
110
  - spec/stackup/parameters_spec.rb
111
+ - spec/stackup/source_spec.rb
110
112
  - spec/stackup/stack_spec.rb
111
113
  - spec/stackup/stack_watcher_spec.rb
112
114
  - spec/stackup/utils_spec.rb
@@ -121,23 +123,24 @@ require_paths:
121
123
  - lib
122
124
  required_ruby_version: !ruby/object:Gem::Requirement
123
125
  requirements:
124
- - - '>='
126
+ - - ">="
125
127
  - !ruby/object:Gem::Version
126
128
  version: '0'
127
129
  required_rubygems_version: !ruby/object:Gem::Requirement
128
130
  requirements:
129
- - - '>='
131
+ - - ">="
130
132
  - !ruby/object:Gem::Version
131
133
  version: '0'
132
134
  requirements: []
133
135
  rubyforge_project:
134
- rubygems_version: 2.6.4
136
+ rubygems_version: 2.6.10
135
137
  signing_key:
136
138
  specification_version: 4
137
139
  summary: Manage CloudFormation stacks
138
140
  test_files:
139
141
  - spec/spec_helper.rb
140
142
  - spec/stackup/parameters_spec.rb
143
+ - spec/stackup/source_spec.rb
141
144
  - spec/stackup/stack_spec.rb
142
145
  - spec/stackup/stack_watcher_spec.rb
143
146
  - spec/stackup/utils_spec.rb