sparkle-guides 0.1.2 → 0.1.4

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: 6ca6dbae97e6ffca7a7acae9ac3c4c2d912a3e55
4
- data.tar.gz: 25c8f24c9d55cb47d8ef1e77de7691f2e82959f9
3
+ metadata.gz: b6d84bfd1702300758e47d0348e9905a0d70c0f8
4
+ data.tar.gz: c2a02647503ca0fd162e28a3c33bae7cb2c53e81
5
5
  SHA512:
6
- metadata.gz: 8c99905620769f194128a8d297545a2e6d5be27ac6c96919dfaed3eb8d0a4a7d833fe67064593dc8ea07e03a80e77e15ba1c366e182c1b187b9eb21be46843b7
7
- data.tar.gz: af9d5353d8cf60122d6c09c88e2a845b4ad1635bede36a075550949160cca0154dc5143587e0c9997aa91dfcd8d0858f79f32b258088c9e3c90ee9866dd74f85
6
+ metadata.gz: 824eba256e15ac401704765d07500313c61fa7930637992f9bf793d03978c88e0922b5059e3757f345b21a65af082de1c9f2ea7009684ed23cc5ea9c9d7a4031
7
+ data.tar.gz: e76f5398b1baa00c49d3abf332662e63c5ca4bcf36474cb9b524daead8418fa9baa690a5f47b124f3e3186f5ded5a414104c18afe682302d7b3cc5e8f17e8d8f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # v0.1.4
2
+ * Update file system layout.
3
+ * Fix YAML error on guide header.
4
+
1
5
  # v0.1.2
2
6
  * Add apply and nesting guide
3
7
 
@@ -0,0 +1,375 @@
1
+ ---
2
+ title: "Stack Interdependencies"
3
+ weight: 2
4
+ anchors:
5
+ - title: "Overview"
6
+ url: "#overview"
7
+ - title: "Apply stack"
8
+ url: "#apply-stack-implementation"
9
+ - title: "Nested stack"
10
+ url: "#nested-stack-implementation"
11
+ - title: "Apply nested stack"
12
+ url: "#apply-nested-stack"
13
+ ---
14
+
15
+ ## Overview
16
+
17
+ ### Apply stack
18
+
19
+ The SparkleFormation CLI includes functionality to ease the sharing
20
+ of resources between isolated stacks. This functionality enables
21
+ groupings of similar resources which can define logical units of
22
+ a full architecture. The sharing is accomplished using a combination
23
+ of stack outputs and stack parameters. This feature is commonly
24
+ referred to as the _apply stack_ functionality.
25
+
26
+ One drawback of the _apply stack_ functionality is that it introduces
27
+ dependencies between disparate stacks. These dependencies must be
28
+ manually resolved when resource modifications occur. When an infrastructure
29
+ is composed of few stacks, the _apply stack_ approach can be sufficient
30
+ to manage stack dependencies leaving the user to ensure updates are
31
+ properly applied. For larger implementations composed of many interdependent
32
+ stacks, the _nested stack_ functionality is recommended.
33
+
34
+ ### Nested stack
35
+
36
+ _Nested stack_ functionality is a generic feature provided by the SparkleFormation
37
+ library which the SparkleFormation CLI then specializes based on the target provider.
38
+ The _nested stack_ functionality utilizes a core feature provided by orchestration
39
+ APIs. This features allows nesting stack resources within a stack allowing a
40
+ parent stack to have many child stacks. By nesting stack resources, the provider
41
+ API will be aware of child stack interdependencies and automatically apply updates
42
+ when resources have been modified. This removes the requirement of stack updates
43
+ being tracked and applied manually.
44
+
45
+ The behavior of the _nested stack_ functionality is based directly on the
46
+ behavior of the _apply stack_ functionality. This commonality in behavior
47
+ allows for initial testing development using the _apply stack_ functionality
48
+ and, once stable, migrating to _nested stack_ functionality without requiring any
49
+ modifications to existing templates. The commonality also allows for the two
50
+ functionalities to be mixed in practice.
51
+
52
+ This guide will first display template implementations using the _apply stack_
53
+ functionality. The example templates will then be used to provide a _nested stack_
54
+ implementation.
55
+
56
+ _NOTE: This guide targets the AWS provider for simplicity to allow focus on the
57
+ features discussed. All providers support this behavior._
58
+
59
+ ## Apply stack implementation
60
+
61
+ Lets start by defining a simple infrastructure. Our infrastructure will be composed
62
+ of a "network" and a collection of "computes" that utilizes the network. This
63
+ infrastructure can be easily defined within two units:
64
+
65
+ 1. network
66
+ 2. computes
67
+
68
+ Lets start by creating a simple `network` template.
69
+
70
+ Create a new file: `./sparkleformation/network.rb`
71
+
72
+ #### Template sparkles AWS
73
+
74
+ ~~~ruby
75
+ SparkleFormation.new(:network) do
76
+
77
+ parameters do
78
+ cidr_prefix do
79
+ type 'String'
80
+ default '172.20'
81
+ end
82
+ end
83
+
84
+ dynamic!(:ec2_vpc, :network) do
85
+ properties do
86
+ cidr_block join!(ref!(:cidr_prefix, '.0.0/24'))
87
+ enable_dns_support true
88
+ enable_dns_hostnames true
89
+ end
90
+ end
91
+
92
+ dynamic!(:ec2_dhcp_options, :network) do
93
+ properties do
94
+ domain_name join!(region!, 'compute.internal')
95
+ domain_name_servers ['AmazonProvidedDNS']
96
+ end
97
+ end
98
+
99
+ dynamic!(:ec2_vpc_dhcp_options_association, :network) do
100
+ properties do
101
+ dhcp_options_id ref!(:network_ec2_dhcp_options)
102
+ vpc_id ref!(:network_ec2_vpc)
103
+ end
104
+ end
105
+
106
+ dynamic!(:ec2_internet_gateway, :network)
107
+
108
+ dynamic!(:ec2_vpc_gateway_attachment, :network) do
109
+ properties do
110
+ internet_gateway_id ref!(:network_ec2_internet_gateway)
111
+ vpc_id ref!(:network_ec2_vpc)
112
+ end
113
+ end
114
+
115
+ dynamic!(:ec2_route_table, :network) do
116
+ properties.vpc_id ref!(:network_ec2_vpc)
117
+ end
118
+
119
+ dynamic!(:ec2_route, :network_public) do
120
+ properties do
121
+ destination_cidr_block '0.0.0.0/0'
122
+ gateway_id ref!(:network_ec2_internet_gateway)
123
+ route_table_id ref!(:network_ec2_route_table)
124
+ end
125
+ end
126
+
127
+ dynamic!(:ec2_subnet, :network) do
128
+ properties do
129
+ availability_zone select!(0, azs!)
130
+ cidr_block join!(ref!(:cidr_prefix, '.0.0/24'))
131
+ vpc_id ref!(:network_ec2_vpc)
132
+ end
133
+ end
134
+
135
+ dynamic!(:ec2_subnet_route_table_association, :network) do
136
+ properties do
137
+ route_table_id ref!(:network_ec2_route_table)
138
+ subnet_id ref!(:network_ec2_subnet)
139
+ end
140
+ end
141
+
142
+ outputs do
143
+ network_vpc_id.value ref!(:network_ec2_vpc)
144
+ network_subnet_id.value ref!(:network_ec2_subnet)
145
+ network_route_table.value ref!(:network_ec2_route_table)
146
+ network_cidr.value join!(ref!(:cidr_prefix, '.0.0/24'))
147
+ end
148
+
149
+ end
150
+ ~~~
151
+
152
+ Here we have an extremely simple VPC defined for our infrastructure. It is
153
+ important to note the outputs defined within our `network` template. These
154
+ outputs are values that will be required for other resources to effectively
155
+ utilize the VPC. With the `network` template defined, we can create that unit
156
+ of our infrastructure:
157
+
158
+ ~~~
159
+ $ bundle exec sfn create sparkle-guide-network --file network
160
+ ~~~
161
+
162
+ Having successfully built the `network` unit of the infrastructure, we can
163
+ now compose the `computes` template.
164
+
165
+ Create a new file: `./sparkleformation/computes.rb`
166
+
167
+ #### Template sparkles AWS
168
+
169
+ ~~~ruby
170
+ SparkleFormation.new(:computes) do
171
+
172
+ parameters do
173
+ ssh_key_name.type 'String'
174
+ network_vpc_id.type 'String'
175
+ network_subnet_id.type 'String'
176
+ end
177
+
178
+ dynamic!(:ec2_security_group, :compute) do
179
+ properties do
180
+ group_description 'SSH Access'
181
+ security_group_ingress do
182
+ cidr_ip '0.0.0.0/0'
183
+ from_port 22
184
+ to_port 22
185
+ ip_protocol 'tcp'
186
+ end
187
+ vpc_id ref!(:network_vpc_id)
188
+ end
189
+ end
190
+
191
+ dynamic!(:ec2_instance, :micro) do
192
+ properties do
193
+ image_id 'ami-25c52345'
194
+ image_type 't2.micro'
195
+ key_name ref!(:ssh_key_name)
196
+ network_interfaces array!(
197
+ ->{
198
+ device_index 0
199
+ associate_public_ip_address 'true'
200
+ subnet_id ref!(:network_subnet_id)
201
+ group_set [ref!(:compute_ec2_security_group)]
202
+ }
203
+ )
204
+ end
205
+ end
206
+
207
+ dynamic!(:ec2_instance, :small) do
208
+ properties do
209
+ image_id 'ami-25c52345'
210
+ image_type 't2.micro'
211
+ key_name ref!(:ssh_key_name)
212
+ network_interfaces array!(
213
+ ->{
214
+ device_index 0
215
+ associate_public_ip_address 'true'
216
+ subnet_id ref!(:network_subnet_id)
217
+ group_set [ref!(:compute_ec2_security_group)]
218
+ }
219
+ )
220
+ end
221
+ end
222
+
223
+ outputs do
224
+ micro_address.value attr!(:micro_ec2_instance, :public_ip)
225
+ small_address.value attr!(:small_ec2_instance, :public_ip)
226
+ end
227
+
228
+ end
229
+ ~~~
230
+
231
+ Our `computes` template will create one micro and one small EC2
232
+ instance and output the public IP addresses of each resource. To
233
+ build the EC2 instances into the VPC created in the `sparkle-guide-network`
234
+ stack we need two pieces of information:
235
+
236
+ * VPC ID
237
+ * VPC subnet ID
238
+
239
+ In our `computes` template we define two parameters for the required
240
+ VPC information: `network_vpc_id` and `network_subnet_id`. When we
241
+ create a stack using this template we can copy the output values from the
242
+ `sparkle-guide-network` stack and paste them into these parameters, but
243
+ that is extremely cumbersome. The SparkleFormation CLI instead allows
244
+ "applying" a stack on creation or update.
245
+
246
+ Notice that the output names in our `network` template match the parameter
247
+ names in our `computes` template.
248
+
249
+ ~~~ruby
250
+ # From ./sparkleformation/network.rb
251
+ outputs do
252
+ network_vpc_id.value ref!(:network_ec2_vpc)
253
+ network_subnet_id.value ref!(:network_ec2_subnet)
254
+ network_route_table.value ref!(:network_ec2_route_table)
255
+ network_cidr.value join!(ref!(:cidr_prefix, '.0.0/24'))
256
+ end
257
+
258
+ # From ./sparkleformation/computes.rb
259
+ parameters do
260
+ ssh_key_name.type 'String'
261
+ network_vpc_id.type 'String'
262
+ network_subnet_id.type 'String'
263
+ end
264
+ ~~~
265
+
266
+ The apply stack functionality will automatically collect outputs from
267
+ the stack names provided and use them to seed the parameters of the
268
+ stack being created or updated. This means instead of having to copy
269
+ and paste the VPC ID and subnet ID values, we can instruct the SparkleFormation
270
+ CLI to automatically use the outputs of our `sparkle-guide-network` stack:
271
+
272
+ ~~~
273
+ $ bundle exec sfn create sparkle-guide-computes --file computes --apply-stack sparkle-guide-network
274
+ ~~~
275
+
276
+ During the create process, the SparkleFormation CLI will prompt for parameters. The
277
+ default values for the VPC ID and subnet ID will be automatically inserted, matching
278
+ the outputs from the `sparkle-guide-network`.
279
+
280
+ ## Nested stack implementation
281
+
282
+ Now that our infrastructure has been successfully created using disparate stacks, lets
283
+ combine them to create a single `infrastructure` unit composed of sub-units. Using
284
+ nested stacks requires a bucket to store templates. Create a bucket in S3 and then
285
+ add the following to the `.sfn` configuration file:
286
+
287
+ ~~~ruby
288
+ Configuration.new do
289
+ ...
290
+ nesting_bucket 'NAME_OF_BUCKET'
291
+ ...
292
+ end
293
+ ~~~
294
+
295
+ With the required bucket in place and SparkleFormation CLI configured we can
296
+ now create our `infrastructure` template.
297
+
298
+ Create a new file: `./sparkleformation/infrastructure.rb`
299
+
300
+ #### Template sparkles AWS
301
+
302
+ ~~~ruby
303
+ SparkleFormation.new(:infrastructure) do
304
+ nest!(:network, :infra)
305
+ nest!(:computes, :infra)
306
+ end
307
+ ~~~
308
+
309
+ This new `infrastructure` template is using SparkleFormation's builtin nesting
310
+ functionality to create stack resources within our `infrastructure` template
311
+ composed of our `network` and `computes` template. To see the conceptual result
312
+ of this nesting, we can print the `infrastructure` template:
313
+
314
+ ~~~
315
+ $ bundle exec sfn print --file infrastructure
316
+ ~~~
317
+
318
+ There are a few things of note in this output. First, the `Stack` property is
319
+ not a real resource property. It is used by SparkleFormation for template processing
320
+ and is included in print functions to display the template in its entirety. Next,
321
+ the generated URLs are not real URLs. This is due to the SparkleFormation CLI not
322
+ actually storing the templates in the remote bucket. Lastly, and most importantly,
323
+ the parameters property of the `ComputesInfra` resource.
324
+
325
+ ~~~json
326
+ "Parameters": {
327
+ "NetworkVpcId": {
328
+ "Fn::GetAtt": [
329
+ "NetworkInfra",
330
+ "Outputs.NetworkVpcId"
331
+ ]
332
+ },
333
+ "NetworkSubnetId": {
334
+ "Fn::GetAtt": [
335
+ "NetworkInfra",
336
+ "Outputs.NetworkSubnetId"
337
+ ]
338
+ }
339
+ }
340
+ ~~~
341
+
342
+ SparkleFormation registers outputs when processing templates and will automatically
343
+ map outputs to subsequent stack resource parameters if they match. Cross stack resource
344
+ dependencies are now explicitly defined allowing the orchestration API to automatically
345
+ determine creation order as well as triggering updates when required.
346
+
347
+ Now we can create our full infrastructure with a single command:
348
+
349
+ ~~~
350
+ $ bundle exec sfn create sparkle-guide-infrastructure --file infrastructure
351
+ ~~~
352
+
353
+ As SparkleFormation processes the nested templates for the `create` command, the SparkleFormation
354
+ CLI will extract the nested templates, store them in the configured nesting bucket, and updates
355
+ the template location URL in the resource.
356
+
357
+ Using nested templates, `update` commands follow the same behavior as `create` commands. All nested
358
+ templates are extracted and automatically uploaded prior to execution of the `update` request with
359
+ the orchestration API. This results in _all_ nested stacks being automatically updated by the API
360
+ as required based on dependent resource modifications.
361
+
362
+ ## Apply nested stack
363
+
364
+ Nested stacks can be applied to disparate stacks in the same manner described in the apply
365
+ stack implementation section. When the stack to be applied is a nested stack, SparkleFormation
366
+ CLI will gather outputs from all the nested stacks, and then apply to the target command. This
367
+ means using the `sparkle-guide-infrastructure` stack we previously built can be used for creating
368
+ new `computes` stacks without being nested into the `infrastructure` template.
369
+
370
+ ~~~
371
+ $ bundle exec sfn create sparkle-guide-computes-infra --file computes --apply-stack sparkle-guide-infrastructure
372
+ ~~~
373
+
374
+ The ability to apply nested stacks to disparate stacks make it easy to provide resources to new
375
+ stacks, or to test building new stacks in isolation before being nested into the root stack.
@@ -0,0 +1,734 @@
1
+ ---
2
+ title: "Getting Started"
3
+ weight: 1
4
+ anchors:
5
+ - title: "Requirements"
6
+ url: "#requirements"
7
+ - title: "Installation"
8
+ url: "#installation"
9
+ - title: "Configuration"
10
+ url: "#configuration"
11
+ - title: "Setup"
12
+ url: "#setup"
13
+ - title: "Create a template"
14
+ url: "#create-a-template"
15
+ - title: "Create a new stack"
16
+ url: "#create-a-new-stack"
17
+ - title: "Template refactor"
18
+ url: "#template-refactor"
19
+ - title: "Stack update"
20
+ url: "#stack-update"
21
+ - title: "Stack information"
22
+ url: "#stack-information"
23
+ - title: "Clean up"
24
+ url: "#clean-up"
25
+ ---
26
+
27
+ ## Requirements
28
+
29
+ * Ruby (Version `>=` 2.0)
30
+ * Bundler
31
+ * Provider Credentials
32
+
33
+ ### Installing Ruby
34
+
35
+ There are a variety of ways to install Ruby depending on platform and toolset. The
36
+ Ruby website provides information about many of the installation options:
37
+
38
+ * [Installing Ruby](https://www.ruby-lang.org/en/documentation/installation/)
39
+
40
+ ### Installing Bundler
41
+
42
+ Once Ruby is installed, install Bundler using the `gem` command:
43
+
44
+ ~~~
45
+ $ gem install bundler
46
+ ~~~
47
+
48
+ ### Provider credentials
49
+
50
+ Required credentials differ based on the target provider in use. For more information
51
+ about credentials specific to a certain provider, reference the miasma library for
52
+ the provider:
53
+
54
+ * [miasma-aws](https://github.com/miasma-rb/miasma-aws)
55
+ * [miasma-azure](https://github.com/miasma-rb/miasma-azure)
56
+ * [miasma-open-stack](https://github.com/miasma-rb/miasma-open-stack)
57
+ * [miasma-rackspace](https://github.com/miasma-rb/miasma-rackspace)
58
+
59
+ ## Installation
60
+
61
+ First, install the `sfn` gem:
62
+
63
+ ~~~
64
+ $ gem install sfn
65
+ ~~~
66
+
67
+ Now initialize a new project:
68
+
69
+ ~~~
70
+ $ sfn init sparkle-guide
71
+ ~~~
72
+
73
+ Finally change to the new project directory:
74
+
75
+ ~~~
76
+ $ cd sparkle-guide
77
+ ~~~
78
+
79
+ ## Configuration
80
+
81
+ The `init` command will have automatically generated a configuration file
82
+ within the project directory. To view the current configuration status the
83
+ `conf` command can be used:
84
+
85
+ ~~~
86
+ $ bundle exec sfn conf
87
+ ~~~
88
+
89
+ The configuration file for `sfn` is located within the `.sfn` file. Open this file
90
+ and adjust the credentials section for your desired provider. The generated
91
+ configuration file uses environment variables for provider configuration. This
92
+ style of setup makes it easy to automatically set credential information using
93
+ tools like direnv. Environment variable usage is not required, and credential
94
+ values can be provided directly.
95
+
96
+ _NOTE: It is important to **not** store provider credential secrets within the
97
+ `.sfn` file if it will be checked into source control._
98
+
99
+ ### Test configuration
100
+
101
+ Test the configuration by running a list command:
102
+
103
+ ~~~
104
+ $ bundle exec sfn list
105
+ ~~~
106
+
107
+ This should print a list of existing stacks on the configured provider. If no
108
+ stacks exist, no entries will be shown. If an error message is received, check
109
+ that the credentials information is properly set and try again.
110
+
111
+ ## Create a template
112
+
113
+ Lets start by creating a full template. This template will create a compute resource
114
+ on the desired provider and output the remote public address of the new compute instance.
115
+
116
+ Create a new file: `./sparkleformation/compute.rb`:
117
+
118
+ #### Template sparkles AWS
119
+
120
+ ~~~ruby
121
+ SparkleFormation.new(:compute, :provider => :aws) do
122
+ AWSTemplateFormatVersion '2010-09-09'
123
+ description 'Sparkle Guide Compute Template'
124
+
125
+ parameters do
126
+ sparkle_image_id.type 'String'
127
+ sparkle_ssh_key_name.type 'String'
128
+ sparkle_flavor do
129
+ type 'String'
130
+ default 't2.micro'
131
+ allowed_values ['t2.micro', 't2.small']
132
+ end
133
+ end
134
+
135
+ dynamic!(:ec2_instance, :sparkle) do
136
+ properties do
137
+ image_id ref!(:sparkle_image_id)
138
+ instance_type ref!(:sparkle_flavor)
139
+ key_name ref!(:sparkle_ssh_key_name)
140
+ end
141
+ end
142
+
143
+ outputs.sparkle_public_address do
144
+ description 'Compute instance public address'
145
+ value attr!(:sparkle_ec2_instance, :public_ip)
146
+ end
147
+
148
+ end
149
+ ~~~
150
+
151
+ #### Template sparkles HEAT
152
+
153
+ ~~~ruby
154
+ SparkleFormation.new(:compute, :provider => :heat) do
155
+ heat_template_version '2015-04-30'
156
+ description 'Sparkle Guide Compute Template'
157
+
158
+ parameters do
159
+ sparkle_image_id.type 'String'
160
+ sparkle_ssh_key_name.type 'String'
161
+ sparkle_flavor do
162
+ type 'String'
163
+ default 'm1.small'
164
+ allowed_values ['m1.small', 'm1.medium']
165
+ end
166
+ end
167
+
168
+ dynamic!(:nova_server, :sparkle) do
169
+ properties do
170
+ image ref!(:sparkle_image_id)
171
+ flavor ref!(:sparkle_flavor)
172
+ key_name ref!(:sparkle_ssh_key_name)
173
+ end
174
+ end
175
+
176
+ outputs.sparkle_public_address do
177
+ description 'Compute instance public address'
178
+ value attr!(:sparkle_nova_instance, 'accessIPv4')
179
+ end
180
+
181
+ end
182
+ ~~~
183
+
184
+ #### Template sparkles Azure
185
+
186
+ ~~~ruby
187
+ SparkleFormation.new(:compute, :provider => :azure) do
188
+ set!('$schema', 'https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#')
189
+ content_version '1.0.0.0'
190
+ parameters do
191
+ sparkle_image_id do
192
+ type 'string'
193
+ default_value '14.04.2-LTS'
194
+ end
195
+ sparkle_flavor do
196
+ type 'string'
197
+ allowed_values [
198
+ 'Standard_D1'
199
+ ]
200
+ end
201
+ storage_account_name.type 'string'
202
+ storage_container_name.type 'string'
203
+ end
204
+
205
+ dynamic!(:network_public_ip_addresses, :sparkle) do
206
+ properties do
207
+ set!('publicIPAllocationMethod', 'Dynamic')
208
+ dns_settings.domain_name_label 'sparkle'
209
+ end
210
+ end
211
+
212
+ dynamic!(:network_virtual_networks, :sparkle) do
213
+ properties do
214
+ address_space.address_prefixes ['10.0.0.0/16']
215
+ subnets array!(
216
+ ->{
217
+ name 'sparkle-subnet'
218
+ properties.address_prefix '10.0.0.0/24'
219
+ }
220
+ )
221
+ end
222
+ end
223
+
224
+ dynamic!(:network_interfaces, :sparkle) do
225
+ properties.ip_configurations array!(
226
+ ->{
227
+ name 'ipconfig1'
228
+ properties do
229
+ set!('privateIPAllocationMethod', 'Dynamic')
230
+ set!('publicIPAddress').id resource_id!(:sparkle_network_public_ip_addresses)
231
+ subnet.id concat!(resource_id!(:sparkle_network_virtual_networks), '/subnets/sparkle-subnet')
232
+ end
233
+ }
234
+ )
235
+ end
236
+
237
+ dynamic!(:compute_virtual_machines, :sparkle) do
238
+ properties do
239
+ hardware_profile.vm_size parameters!(:sparkle_flavor)
240
+ os_profile do
241
+ computer_name 'sparkle'
242
+ admin_username 'sparkle'
243
+ admin_password 'SparkleFormation2016'
244
+ end
245
+ storage_profile do
246
+ image_reference do
247
+ publisher 'Canonical'
248
+ offer 'UbuntuServer'
249
+ sku parameters!(:sparkle_image_id)
250
+ version 'latest'
251
+ end
252
+ os_disk do
253
+ name 'osdisk'
254
+ vhd.uri concat!('http://', parameters!(:storage_account_name), '.blob.core.windows.net/', parameters!(:storage_container_name), '/sparkle.vhd')
255
+ caching 'ReadWrite'
256
+ create_option 'FromImage'
257
+ end
258
+ data_disks array!(
259
+ ->{
260
+ name 'datadisk1'
261
+ set!('diskSizeGB', 100)
262
+ lun 0
263
+ vhd.uri concat!('http://', parameters!(:storage_account_name), '.blob.core.windows.net/', parameters!(:storage_container_name), '/sparkle-data.vhd')
264
+ create_option 'Empty'
265
+ }
266
+ )
267
+ end
268
+ network_profile.network_interfaces array!(
269
+ ->{ id resource_id!(:sparkle_network_interfaces) }
270
+ )
271
+ end
272
+ end
273
+
274
+ outputs.sparkle_public_address do
275
+ type 'string'
276
+ value reference!(:sparkle_network_public_ip_addresses).ipAddress
277
+ end
278
+ end
279
+ ~~~
280
+
281
+ ### View template
282
+
283
+ View the processed JSON result:
284
+
285
+ ~~~
286
+ $ bundle exec sfn print --file compute
287
+ ~~~
288
+
289
+ This will output the serialized JSON template generated by SparkleFormation. The JSON is the template
290
+ content which will be sent to the remote provider API with the stack create request.
291
+
292
+ ## Create a new stack
293
+
294
+ After seeing the result of the compiled and serialized template, lets use that template to
295
+ create a new stack. To start the stack creation process run the following command:
296
+
297
+ ~~~
298
+ $ bundle exec sfn create sparkle-guide-compute --file compute
299
+ ~~~
300
+
301
+ Before creating this new stack, `sfn` will prompt for the parameters defined within the template.
302
+ The `sparkle_image_id` and `sparkle_ssh_key_name` parameters are not defined with default values within the template,
303
+ so values _must_ be provided when prompted. The prompt for the `sparkle_flavor` parameter will show the
304
+ default value defined within the template, and can be used or overridden with a different value.
305
+
306
+ After `sfn` has completed prompting for stack parameters, it will initiate the stack creation
307
+ request with the remote provider. The creation request to the API is only for initiation. A successful
308
+ response does not indicate that the stack was created successfully, rather it indicates that the request
309
+ to create the stack was successful.
310
+
311
+ Once the create request is complete, `sfn` will automatically transition to event polling. Resource
312
+ events related to the new stack will be displayed until the stack reaches a "complete" state: success
313
+ or failure. The automatic transition to event polling ensures that the `sfn create` command will return
314
+ a proper exit code once the stack has reached a completion state.
315
+
316
+ At successful completion of the stack creation, the outputs defined within the template will be displayed
317
+ showing the public address of the newly created compute instance.
318
+
319
+ ## Template refactor
320
+
321
+ A key feature of SparkleFormation is the ability to break down templates into reusable parts which can
322
+ then be re-used in multiple templates. Lets break down our existing template into re-usable parts and
323
+ re-build the template using those parts.
324
+
325
+ ### Registry
326
+
327
+ The registry is a place to store values that may be used in multiple places. With the value defined in
328
+ a single location, updates to the value only require one modification to apply globally. In our `compute`
329
+ example, the allowed instance flavor values would an ideal candidate for a registry entry.
330
+
331
+ Create a new file at `./sparkleformation/registry/instance_flavor.rb`
332
+
333
+ #### Registry sparkles AWS
334
+
335
+ ~~~ruby
336
+ SfnRegistry.register(:instance_flavor) do
337
+ ['m1.small', 'm1.medium']
338
+ end
339
+ ~~~
340
+
341
+ #### Registry sparkles HEAT
342
+
343
+ ~~~ruby
344
+ SfnRegistry.register(:instance_flavor) do
345
+ ['m1.small', 'm1.medium']
346
+ end
347
+ ~~~
348
+
349
+ #### Registry sparkles Azure
350
+ ~~~ruby
351
+ SfnRegistry.register(:instance_flavor) do
352
+ ['Standard_D1']
353
+ end
354
+ ~~~
355
+
356
+ _NOTE: For more information see: [Registry building blocks](../sparkle_formation/building-blocks.html#registry)_
357
+
358
+ ### Component
359
+
360
+ Components are items which are used a single time within a template. The version information and description
361
+ are both items in our `compute` template that are only used a single time, but should be defined in all templates.
362
+ Lets move those items into a component.
363
+
364
+ Create a new file at `./sparkleformation/components/base.rb`
365
+
366
+ #### Component sparkles AWS
367
+
368
+ ~~~ruby
369
+ SparkleFormation.component(:base) do
370
+ AWSTemplateFormatVersion '2010-09-09'
371
+ description 'Sparkle Guide Compute Template'
372
+ end
373
+ ~~~
374
+
375
+ #### Component sparkles HEAT
376
+
377
+ ~~~ruby
378
+ SparkleFormation.component(:base) do
379
+ heat_template_version '2015-04-30'
380
+ description 'Sparkle Guide Compute Template'
381
+ end
382
+ ~~~
383
+
384
+ #### Component sparkles Azure
385
+
386
+ ~~~ruby
387
+ SparkleFormation.component(:base) do
388
+ set!('$schema', 'https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#')
389
+ content_version '1.0.0.0'
390
+ end
391
+ ~~~
392
+
393
+ _NOTE: For more information see: [Component building blocks](../sparkle_formation/building-blocks.html#components)_
394
+
395
+ ### Dynamic
396
+
397
+ Dynamics are items which can be used multiple times within a template. A dynamic requires a custom name be provided
398
+ and allows for an optional Hash of values. Dynamics are useful for injecting a common structure into a template
399
+ multiple times. In the `compute` template above, we can now extract the remainder of the template and convert it
400
+ into a dynamic.
401
+
402
+ Create a new file at `./sparkleformation/dynamics/node.rb`
403
+
404
+ #### Dynamic sparkles AWS
405
+
406
+ ~~~ruby
407
+ SparkleFormation.dynamic(:node) do |name, opts={}|
408
+
409
+ parameters do
410
+ set!("#{name}_image_id".to_sym).type 'String'
411
+ set!("#{name}_ssh_key_name".to_sym).type 'String'
412
+ set!("#{name}_flavor".to_sym) do
413
+ type 'String'
414
+ default 'm1.small'
415
+ allowed_values registry!(:instance_flavor)
416
+ end
417
+ end
418
+
419
+ outputs.set!("#{name}_public_address".to_sym) do
420
+ description "Compute instance public address - #{name}"
421
+ value attr!("#{name}_ec2_instance".to_sym, :public_ip)
422
+ end
423
+
424
+ dynamic!(:ec2_instance, name) do
425
+ properties do
426
+ image_id ref!("#{name}_image_id".to_sym)
427
+ instance_type ref!("#{name}_flavor".to_sym)
428
+ key_name ref!("#{name}_ssh_key_name".to_sym)
429
+ end
430
+ end
431
+
432
+ end
433
+ ~~~
434
+
435
+ #### Dynamic sparkles HEAT
436
+
437
+ ~~~ruby
438
+ SparkleFormation.dynamic(:node) do |name, opts={}|
439
+
440
+ parameters do
441
+ set!("#{name}_image_id".to_sym).type 'String'
442
+ set!("#{name}_ssh_key_name".to_sym).type 'String'
443
+ set!("#{name}_flavor".to_sym) do
444
+ type 'String'
445
+ default 'm1.small'
446
+ allowed_values registry!(:instance_flavor)
447
+ end
448
+ end
449
+
450
+ outputs.set!("#{name}_public_address".to_sym) do
451
+ description "Compute instance public address - #{name}"
452
+ value attr!("#{name}_nova_server".to_sym, 'accessIPv4')
453
+ end
454
+
455
+ dynamic!(:nova_server, name) do
456
+ properties do
457
+ image ref!("#{name}_image_id".to_sym)
458
+ flavor ref!("#{name}_flavor".to_sym)
459
+ key_name ref!("#{name}_ssh_key_name".to_sym)
460
+ end
461
+ end
462
+
463
+ end
464
+ ~~~
465
+
466
+ #### Dynamic sparkles Azure
467
+
468
+ ~~~~ruby
469
+ SparkleFormation.dynamic(:node) do |name, opts={}|
470
+ parameters do
471
+ set!("#{name}_image_id".to_sym) do
472
+ type 'string'
473
+ default_value '14.04.2-LTS'
474
+ end
475
+ set!("#{name}_flavor".to_sym) do
476
+ type 'string'
477
+ allowed_values registry!(:instance_flavor)
478
+ end
479
+ end
480
+
481
+ dynamic!(:network_public_ip_addresses, name) do
482
+ properties do
483
+ set!('publicIPAllocationMethod', 'Dynamic')
484
+ dns_settings.domain_name_label name
485
+ end
486
+ end
487
+
488
+ dynamic!(:network_interfaces, name) do
489
+ properties.ip_configurations array!(
490
+ ->{
491
+ name 'ipconfig1'
492
+ properties do
493
+ set!('privateIPAllocationMethod', 'Dynamic')
494
+ set!('publicIPAddress').id resource_id!("#{name}_network_public_ip_addresses".to_sym)
495
+ subnet.id concat!(resource_id!("#{name}_network_virtual_networks".to_sym), '/subnets/sparkle-subnet')
496
+ end
497
+ }
498
+ )
499
+ end
500
+
501
+ dynamic!(:compute_virtual_machines, name) do
502
+ properties do
503
+ hardware_profile.vm_size parameters!("#{name}_flavor".to_sym)
504
+ os_profile do
505
+ computer_name 'sparkle'
506
+ admin_username 'sparkle'
507
+ admin_password 'SparkleFormation2016'
508
+ end
509
+ storage_profile do
510
+ image_reference do
511
+ publisher 'Canonical'
512
+ offer 'UbuntuServer'
513
+ sku parameters!("#{name}_image_id".to_sym)
514
+ version 'latest'
515
+ end
516
+ os_disk do
517
+ name 'osdisk'
518
+ vhd.uri concat!('http://', parameters!(:storage_account_name), '.blob.core.windows.net/', parameters!(:storage_container_name), "/#{name}.vhd")
519
+ caching 'ReadWrite'
520
+ create_option 'FromImage'
521
+ end
522
+ data_disks array!(
523
+ ->{
524
+ name 'datadisk1'
525
+ set!('diskSizeGB', 100)
526
+ lun 0
527
+ vhd.uri concat!('http://', parameters!(:storage_account_name), '.blob.core.windows.net/', parameters!(:storage_container_name), "/#{name}-data.vhd")
528
+ create_option 'Empty'
529
+ }
530
+ )
531
+ end
532
+ network_profile.network_interfaces array!(
533
+ ->{ id resource_id!("#{name}_network_interfaces".to_sym) }
534
+ )
535
+ end
536
+ end
537
+
538
+ outputs.set!("#{name}_public_address".to_sym) do
539
+ type 'string'
540
+ value reference!("#{name}_network_public_ip_addresses".to_sym).ipAddress
541
+ end
542
+ end
543
+ ~~~~
544
+
545
+ The first thing to note about this dynamic file is the `name` argument at the top.
546
+
547
+ `SparkleFormation.dynamic(:node) do |name, opts={}|`
548
+
549
+ This variable is used throughout the dynamic to provide uniquely named parameters, resources, and outputs. Also
550
+ note the use of they registry to define the allowed values for the flavor parameter.
551
+
552
+ `allowed_values registry!(:instance_flavor)`
553
+
554
+ _NOTE: For more information see: [Dynamic building blocks](../sparkle_formation/building-blocks.html#dynamics)_
555
+
556
+ ### Template
557
+
558
+ Now that the original template has been refactored into re-usable parts, lets update our `./sparkleformation/compute.rb`
559
+ template:
560
+
561
+ #### Template sparkles AWS
562
+
563
+ ~~~ruby
564
+ SparkleFormation.new(:compute, :provider => :aws).load(:base).overrides do
565
+ dynamic!(:node, :sparkle)
566
+ end
567
+ ~~~
568
+
569
+ #### Template sparkles HEAT
570
+
571
+ ~~~ruby
572
+ SparkleFormation.new(:compute, :provider => :heat).load(:base).overrides do
573
+ dynamic!(:node, :sparkle)
574
+ end
575
+ ~~~
576
+
577
+ #### Template sparkles Azure
578
+
579
+ ~~~ruby
580
+ SparkleFormation.new(:compute, :provider => :azure).load(:base).overrides do
581
+ parameters do
582
+ storage_account_name.type 'string'
583
+ storage_container_name.type 'string'
584
+ end
585
+
586
+ dynamic!(:network_virtual_networks, :sparkle) do
587
+ properties do
588
+ address_space.address_prefixes ['10.0.0.0/16']
589
+ subnets array!(
590
+ ->{
591
+ name 'sparkle-subnet'
592
+ properties.address_prefix '10.0.0.0/24'
593
+ }
594
+ )
595
+ end
596
+ end
597
+
598
+ dynamic!(:node, :sparkle)
599
+ end
600
+ ~~~
601
+
602
+ The template is now greatly compacted, and composed entirely of re-usable parts. The `.load(:base)` is inserting
603
+ the base component we defined above. The `dynamic!(:node, :sparkle)` is inserting the node dynamic we defined
604
+ above and using the custom name `sparkle`. We can print this template, and see the result is the same as the original
605
+ template.
606
+
607
+ ~~~
608
+ $ sfn print --file compute
609
+ ~~~
610
+
611
+ ## Stack update
612
+
613
+ ### The NO-OP update
614
+
615
+ We can verify that our new template is the same as our original template by updating the running stack and applying
616
+ our new template:
617
+
618
+ ~~~
619
+ $ sfn update sparkle-guide-compute --file compute --defaults
620
+ ~~~
621
+
622
+ The `--defaults` flag will suppress prompts for stack parameters and use the existing values defined for the running
623
+ stack. The result of this command will either explicitly state that no updates were performed, or the event stream
624
+ will show that no resources were modified depending on the provider.
625
+
626
+ ### The real update (parameters)
627
+
628
+ Now lets update the stack by modifying the paramters of the running stack. We will change the flavor of the instance
629
+ which will result in the resource being replaced within the stack. Run the update command but do not provide any
630
+ flags:
631
+
632
+ ~~~
633
+ $ sfn update sparkle-guide-compute
634
+ ~~~
635
+
636
+ Now `sfn` will prompt for parameter values. Notice that the default values are the values used when creating the
637
+ stack. When the `sparkle_flavor` parameter is prompted, choose a different value from the allowed list. After the
638
+ update has completed, the outputs displayed of the stack will have changed showing a new public address for the
639
+ compute instance.
640
+
641
+ ### The real update (template)
642
+
643
+ Since our template has been refactored and is now composed of re-usable parts, it's easy to quickly expand our stack.
644
+ Lets add an additional compute resource to our `./sparkleformation/compute.rb` template:
645
+
646
+ #### Template sparkles AWS
647
+
648
+ ~~~ruby
649
+ SparkleFormation.new(:compute, :provider => :aws).load(:base).overrides do
650
+ dynamic!(:node, :sparkle)
651
+ dynamic!(:node, :unicorn)
652
+ end
653
+ ~~~
654
+
655
+ #### Template sparkles HEAT
656
+
657
+ ~~~ruby
658
+ SparkleFormation.new(:compute, :provider => :heat).load(:base).overrides do
659
+ dynamic!(:node, :sparkle)
660
+ dynamic!(:node, :unicorn)
661
+ end
662
+ ~~~
663
+
664
+ #### Template sparkles Azure
665
+
666
+ ~~~ruby
667
+ SparkleFormation.new(:compute, :provider => :azure).load(:base).overrides do
668
+ parameters do
669
+ storage_account_name.type 'string'
670
+ storage_container_name.type 'string'
671
+ end
672
+
673
+ dynamic!(:network_virtual_networks, :sparkle) do
674
+ properties do
675
+ address_space.address_prefixes ['10.0.0.0/16']
676
+ subnets array!(
677
+ ->{
678
+ name 'sparkle-subnet'
679
+ properties.address_prefix '10.0.0.0/24'
680
+ }
681
+ )
682
+ end
683
+ end
684
+
685
+ dynamic!(:node, :sparkle)
686
+ dynamic!(:node, :unicorn)
687
+ end
688
+ ~~~
689
+
690
+
691
+ Printing the template we can see that by adding a single line we have added not only a new resource to our template,
692
+ but a new output as well.
693
+
694
+ ~~~
695
+ $ sfn print --file compute
696
+ ~~~
697
+
698
+ Now we can apply this updated template to our existing stack. On this update command, we _must_ provide the `--file`
699
+ flag so our modified template will be sent in our request:
700
+
701
+ ~~~
702
+ $ sfn update sparkle-guide-compute --file compute
703
+ ~~~
704
+
705
+ When `sfn` prompts for parameters, the previously seen `sparkle` parameters will be requested, but we will also
706
+ see new `unicorn` parameters requested for the new compute resource we added. After the update has completed the
707
+ stack outputs will be displayed and will now include two public addresses: one for `sparkle` and one for `unicorn`.
708
+
709
+ ## Stack information
710
+
711
+ With stacks currently existing on the remote provider, we can now use the inspection commands:
712
+
713
+ ~~~
714
+ $ sfn list
715
+ ~~~
716
+
717
+ Will provide a list of current stacks. This may include only the `sparkle-guide-compute` stack or it may include
718
+ other stacks that were created by other users or the provider itself.
719
+
720
+ We can also describe a specific stack. The describe command will list all the resources composing the requested
721
+ stack, as well as any stack outputs defined for the stack:
722
+
723
+ ~~~
724
+ $ sfn describe sparkle-guide-compute
725
+ ~~~
726
+
727
+ ## Clean up
728
+
729
+ To conclude this guide, we want to be sure to remove the example stack we created. This is done using the
730
+ destroy command:
731
+
732
+ ~~~
733
+ $ sfn destroy sparkle-guide-compute
734
+ ~~~
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'sparkle-guides'
3
- s.version = '0.1.2'
3
+ s.version = '0.1.4'
4
4
  s.summary = 'SparkleFormation Guides'
5
5
  s.author = 'Chris Roberts'
6
6
  s.email = 'chrisroberts.code@gmail.com'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sparkle-guides
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Roberts
@@ -18,6 +18,8 @@ extra_rdoc_files: []
18
18
  files:
19
19
  - CHANGELOG.md
20
20
  - README.md
21
+ - docs/apply-and-nested.md
22
+ - docs/getting-started.md
21
23
  - sparkle-guides.gemspec
22
24
  homepage: http://github.com/sparkleformation/sparkle-guides
23
25
  licenses: