sparkle-guides 0.1.2 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
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: