stack-kicker 0.0.11 → 0.0.12

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.
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
1
  ## 0.0.11
2
- Stack.validate() was still using the role name, not role_details[:security_group]
3
- Added doc/examples/apache-via-cloud-init, example of just using cloud-init, no chef.
4
- Fixed some issues when only using cloud-init
2
+ * Stack.validate() was still using the role name, not role_details[:security_group]
3
+ * Added doc/examples/apache-via-cloud-init, example of just using cloud-init, no chef.
4
+ * Fixed some issues when only using cloud-init
data/README.md CHANGED
@@ -61,7 +61,7 @@ This is usually only used when :chef_server = true, it stops stack-kicker from a
61
61
  #### :security_group
62
62
  security group to be assigned to this node. (set to default is you don't want to manage security groups for every role)
63
63
  #### :cloud_config_yaml
64
- defaults to a file which contains a simple template (lib/cloud-config.yaml in the github repo) that installs the http://apt.opscode.com repo & gig key, as well as installing the opscode-keyring. Can be replaced with any filename that complies with cloud-init.
64
+ defaults to a file which contains a simple template (lib/cloud-config.yaml in the github repo) that installs the http://apt.opscode.com repo & gpg key, as well as installing the opscode-keyring. Can be replaced with any filename that complies with cloud-init. If the filename supplied ends in '.erb', it will be processed with ERB. See [ERB Templates](#erbtemplates) for details on available data.
65
65
  #### :bootstrap
66
66
  Optional filename, the contents of which will get combined with :cloud_config_yaml to form the cloud-init payload (using mime encoding, supported types are #include, ) with some variable substation (chef server ip, environment, validation.pem, roles) See lib/chef-client-bootstrap-excl-validation-pem.sh as an example.
67
67
 
@@ -76,6 +76,8 @@ text/upstart-job | #upstart-job
76
76
  text/part-handler | #part-handler
77
77
  text/cloud-boothook | #cloud-boothook
78
78
 
79
+ Can be replaced with any filename that complies with cloud-init. If the filename supplied ends in '.erb', it will be processed with ERB. See [ERB Templates](id:erbtemplates) for details on available data.
80
+
79
81
  #### :data_dir
80
82
  data_dir is a hook into the optional :cloud_config_yaml template (lib/cloud-config-w-ephemeral.yaml), which formats & mounts ephemeral0 early in the boot process, allowing it to be used during the rest of the cloud-init. ephemeral0 is mounted as /mnt & then bind mounted to #{data_dir}
81
83
 
@@ -85,6 +87,54 @@ This can be an array of strings, such that node X will be assigned :floating_ips
85
87
  #### :post_install_script, :post_install_args & :post_install_cwd
86
88
  These are used to construct a command to execute, which is executed locally where you executed stack-kicker. :post_install_args can contain %PUBLIC_IP%, which will be replaced by the public IP of the just created node. :post_install_script scripts are executed as soon the the instance returns a status='ACTIVE'. They can be used delay the creation of further nodes of the same role (for example, when creating a rabbitmq cluster, you need to wait for the rabbitmq process to be running before creating the next member of the cluster, or when you are creating a chef-server, you need to wait for the packages to install & daemons to start before attempting to create Chef users & retrieve keys)
87
89
 
90
+ ## [ERB Templates](id:erbtemplates)
91
+ Both the :cloud_config_yaml & :bootstrap role attributes can point to plain files, files with simple tokens (%HOSTNAME%, %CHEF_SERVER%, %CHEF_ENVIRONMENT%, %CHEF_VALIDATION_PEM%, %SERVER_NAME%, %ROLE% and %DATA_DIR%) or [Ruby ERB](http://www.ruby-doc.org/stdlib-2.0/libdoc/erb/rdoc/ERB.html) templates.
92
+
93
+ There are 3 key data sets exposed to the ERB templates:
94
+
95
+ * instances - subset all_instances, just the nodes referenced/managed by this Stackfile
96
+ * all_instances - hash of all instances running in this account
97
+ * config - config contains all the config data from the Stackfile, as well as info about running instances in the account used by the Stackfile
98
+
99
+ instances & all_instances hashes look like this:
100
+
101
+ ```
102
+ {
103
+ "webci-az1-web0001" => {
104
+ :region => "az-1.region-a.geo-1",
105
+ :id => 1395635,
106
+ :private_ips => ["10.5.170.11"],
107
+ :public_ips => ["15.185.114.181"],
108
+ :az => "az-1.region-a.geo-1",
109
+ :role => :web},
110
+ "webci-az1-web0002" => {
111
+ :region => "az-1.region-a.geo-1",
112
+ :id => 1396181,
113
+ :private_ips => ["10.5.172.145"],
114
+ :public_ips => ["15.185.110.42"],
115
+ :az => "az-1.region-a.geo-1",
116
+ :role=>:web
117
+ }
118
+ }
119
+ ```
120
+ So say you wanted to drop the private IP address of webci-az1-web0001 into the hosts file of webci-az1-web0002 via cloud-init, this fragment in your :cloud_config_yaml could be used:
121
+
122
+ ```
123
+ bootcmd:
124
+ - echo <%=instances['webci-az1-web0001'][:private_ips][0]%> webci-az1-web0001 >> /etc/hosts
125
+ ```
126
+
127
+ As we're using ERB for these templates, you can use the standard Ruby iterators to work through the hash:
128
+
129
+ ```
130
+ bootcmd:
131
+ <% instances.each do |node_name, node_details| %>
132
+ - echo <%=node_details[:private_ips][0]%> <%=node_name%> >> /etc/hosts
133
+ <% end %>
134
+ ```
135
+
136
+
137
+
88
138
 
89
139
  ## Example workflows/models
90
140
  stack-kicker was built with the following workflows in mind:
data/doc/examples/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source 'https://rubygems.org'
2
2
  # source 'http://15.185.118.80/partial-gem-mirror/'
3
3
 
4
- gem 'stack-kicker', '>=0.0.10'
4
+ gem 'stack-kicker', '>=0.0.11'
@@ -0,0 +1,6 @@
1
+ ```
2
+ stack-kicker show-stacks
3
+ stack-kicker show-stack
4
+ stack-kicker show-running
5
+ stack-kicker build
6
+ ```
@@ -16,8 +16,8 @@ module StackConfig
16
16
  'az-2.region-a.geo-1' => { 'image_id' => 67074 },
17
17
 
18
18
  # provisioning info
19
- :key_pair => 'dnsaas-dev@hp.com',
20
- :key_public => '../credentials/ssh-keys/dnsaas-dev@hp.com.pub',
19
+ :key_pair => 'YOURKEYPAIRNAME',
20
+ :key_public => '/path/to/your/public-key.pub',
21
21
 
22
22
  :name_template => '%s-%s-%s%04d', # service-site-role0001
23
23
  :global_service_name => 'webci',
@@ -0,0 +1,6 @@
1
+ ```
2
+ stack-kicker show-stacks
3
+ stack-kicker show-stack
4
+ stack-kicker show-running
5
+ stack-kicker build
6
+ ```
@@ -0,0 +1,40 @@
1
+ module StackConfig
2
+ Stacks = Hash.new
3
+
4
+ Stacks['apache-cloud-init'] = {
5
+ # (we can access environment variable via ENV['foo'] instead of hard coding u/p here)
6
+ 'REGION' => ENV['OS_REGION_NAME'],
7
+ 'USERNAME' => ENV['OS_USERNAME'],
8
+ 'PASSWORD' => ENV['OS_PASSWORD'],
9
+ 'AUTH_URL' => ENV['OS_AUTH_URL'],
10
+ 'TENANT_NAME' => ENV['OS_TENANT_NAME'],
11
+
12
+ # generic instance info
13
+ 'flavor_id' => 103,
14
+ 'image_id' => 75845, # Ubuntu Precise 12.04 LTS Server 64-bit
15
+ # per-az image_id's
16
+ 'az-2.region-a.geo-1' => { 'image_id' => 67074 },
17
+
18
+ # provisioning info
19
+ :key_pair => 'YOURKEYPAIRNAME',
20
+ :key_public => '/path/to/your/public-key.pub',
21
+
22
+ :name_template => '%s-%s-%s%04d', # service-site-role0001
23
+ :global_service_name => 'webci',
24
+ :site_template => '%s',
25
+
26
+ # role specification
27
+ # role names & chef roles should match
28
+ :roles => {
29
+ :web => { :count => 3,
30
+ # use the default security group
31
+ :security_group => 'default',
32
+ # don't use chef as the second-stage provisioner
33
+ :skip_chef_prereg => true,
34
+ :bootstrap => 'cloud-init.sh.erb',
35
+ # use the yaml template that supports configuring the ephemeral space early in the boot process
36
+ :cloud_config_yaml => 'cloud-init.yaml.erb',
37
+ }
38
+ }
39
+ }
40
+ end
@@ -0,0 +1,15 @@
1
+ #!/bin/bash
2
+
3
+ cat > /var/www/index.html <<HEREDOC
4
+ <html><body><h1>Stack-Kicker was 'ere!</h1>
5
+ <p>This file is dropped in place by a shell script passed to the host via cloud-init</p>
6
+ <p>The web server software is running but no content has been added, yet.</p>
7
+ <h2>instances</h2>
8
+ <%=instances%>
9
+ <hr>
10
+ <h2>all_instances</h2>
11
+ <%=all_instances%>
12
+ <h2>config</h2>
13
+ <%=config%>
14
+ </body></html>
15
+ HEREDOC
@@ -0,0 +1,33 @@
1
+ #cloud-config
2
+
3
+ output:
4
+ all: ">> /var/log/cloud-init.log"
5
+
6
+ # use the HPCS Ubuntu Mirror for security-updates too
7
+ bootcmd:
8
+ - echo 127.0.1.1 %HOSTNAME% >> /etc/hosts
9
+ - echo # force security.ubuntu.com to mirror.clouds.archive.ubuntu.com >> /etc/hosts
10
+ - echo 15.185.107.200 security.ubuntu.com >> /etc/hosts
11
+ - echo <%=instances['webci-az1-web0001'][:private_ips][0]%> webci-az1-web0001 >> /etc/hosts
12
+ <% instances.each do |node_name, node_details| %>
13
+ - echo <%=node_details[:private_ips][0]%> <%=node_name%> >> /etc/hosts
14
+ <% end %>
15
+
16
+
17
+ # use the HPCS Ubuntu Mirrors
18
+ apt_mirror: http://nova.clouds.archive.ubuntu.com/ubuntu
19
+
20
+ # Run apt-get update
21
+ package_update: true
22
+
23
+ # Run apt-get update
24
+ package_upgrade: true
25
+
26
+ # Install some packages
27
+ packages:
28
+ - apache2
29
+ - php5
30
+
31
+ # Use `sudo -i` to simulate a login shell...
32
+ runcmd:
33
+ - sudo -i touch /var/log/cloud-init.complete
@@ -1,5 +1,5 @@
1
1
  module Stack
2
2
  module Kicker
3
- VERSION = "0.0.11"
3
+ VERSION = "0.0.12"
4
4
  end
5
5
  end
data/lib/stack.rb CHANGED
@@ -448,9 +448,61 @@ cookbook_path [ '<%=config[:stackhome]%>/cookbooks' ]
448
448
  end
449
449
  config[:all_instances] = servers
450
450
  end
451
+ Stack.categorise_instance_ips(config)
451
452
  config[:all_instances]
452
453
  end
453
454
 
455
+ # Add an instance to the :all_instances hash, instead of having to poll the whole lot again
456
+ def Stack.add_instance(config, hostname, region, id, addresses)
457
+ config[:all_instances][hostname] = { :region => region, :id => id, :addresses => addresses}
458
+ Stack.categorise_instance_ips(config)
459
+ end
460
+
461
+ def Stack.categorise_instance_ips(config)
462
+ Logger.debug { ">>> Stack.categorise_instance_ips" }
463
+ # create a private_ip & public_ip array on the server objects for easy retrieval
464
+ config[:all_instances].each do |hostname, instance_details|
465
+ Logger.debug { "Flushing categorised addresses for #{hostname}" }
466
+ config[:all_instances][hostname][:private_ips] = Array.new
467
+ config[:all_instances][hostname][:public_ips] = Array.new
468
+
469
+ Logger.debug { "Categorising addresses for #{hostname}" }
470
+ config[:all_instances][hostname][:addresses].each do |address|
471
+ case address.label
472
+ when 'public'
473
+ config[:all_instances][hostname][:public_ips] << address.address
474
+ when 'private'
475
+ config[:all_instances][hostname][:private_ips] << address.address
476
+ else
477
+ Logger.error "#{address.label} is an unhandled address type for #{hostname}"
478
+ end
479
+ end
480
+ end
481
+ Logger.debug { "config[:all_instances][= #{config[:all_instances]}" }
482
+ Logger.debug { "<<< Stack.categorise_instance_ips" }
483
+ end
484
+
485
+ def Stack.get_public_ip(config, hostname)
486
+ # get a public address from the instance
487
+ # (could be either the dynamic or one of our floating IPs
488
+ config[:all_instances][hostname][:addresses].each do |address|
489
+ if address.label == 'public'
490
+ return address.address
491
+ end
492
+ end
493
+ end
494
+
495
+ def Stack.get_private_ip(config, hostname)
496
+ # get the private address for an instance
497
+ config[:all_instances][hostname][:addresses].each do |address|
498
+ if address.label == 'private'
499
+ return address.address
500
+ end
501
+ end
502
+ end
503
+
504
+
505
+
454
506
  def Stack.show_running(config)
455
507
  # TODO: optionally show the hosts that are missing
456
508
  ours = Stack.get_our_instances(config)
@@ -460,11 +512,6 @@ cookbook_path [ '<%=config[:stackhome]%>/cookbooks' ]
460
512
  end
461
513
  end
462
514
 
463
- # Add an instance to the :all_instances hash, instead of having to poll the whole lot again
464
- def Stack.add_instance(config, hostname, region, id, addresses)
465
- config[:all_instances][hostname] = { :region => region, :id => id, :addresses => addresses}
466
- end
467
-
468
515
  def Stack.ssh(config, hostname = nil, user = ENV['USER'], command = nil)
469
516
  # ssh to a host, or all hosts
470
517
 
@@ -486,6 +533,7 @@ cookbook_path [ '<%=config[:stackhome]%>/cookbooks' ]
486
533
 
487
534
 
488
535
  def Stack.get_our_instances(config)
536
+ Logger.debug { ">>> Stack.get_our_instances" }
489
537
  # build an hash of running instances that match our generated hostnames
490
538
  node_details = Stack.populate_config(config)
491
539
 
@@ -504,6 +552,8 @@ cookbook_path [ '<%=config[:stackhome]%>/cookbooks' ]
504
552
  end
505
553
  end
506
554
 
555
+ Logger.debug { "#{running}" }
556
+ Logger.debug { "<<< Stack.get_our_instances" }
507
557
  running
508
558
  end
509
559
 
@@ -540,16 +590,6 @@ cookbook_path [ '<%=config[:stackhome]%>/cookbooks' ]
540
590
  end
541
591
  end
542
592
 
543
- def Stack.get_public_ip(config, hostname)
544
- # get a public address from the instance
545
- # (could be either the dynamic or one of our floating IPs
546
- config[:all_instances][hostname][:addresses].each do |address|
547
- if address.label == 'public'
548
- return address.address
549
- end
550
- end
551
- end
552
-
553
593
  def Stack.set_chef_server(config, chef_server)
554
594
  # set the private & public URLs for the chef server,
555
595
  # called either after we create the Chef Server, or skip over it
@@ -685,6 +725,7 @@ cookbook_path [ '<%=config[:stackhome]%>/cookbooks' ]
685
725
  Logger.debug { "multipart_cmd = #{multipart_cmd}" }
686
726
  multipart = `#{multipart_cmd}`
687
727
  Logger.debug { "multipart = #{multipart}" }
728
+
688
729
  # 2) replace the tokens (CHEF_SERVER, CHEF_ENVIRONMENT, SERVER_NAME, ROLE)
689
730
  Logger.debug { "Replacing %HOSTNAME% with #{hostname} in multipart" }
690
731
  multipart.gsub!(%q!%HOSTNAME%!, hostname)
@@ -714,6 +755,21 @@ cookbook_path [ '<%=config[:stackhome]%>/cookbooks' ]
714
755
  multipart.gsub!(%q!%ROLE%!, role.to_s)
715
756
  multipart.gsub!(%q!%DATA_DIR%!, role_details[:data_dir])
716
757
 
758
+ if role_details[:bootstrap].match('\.erb$') || role_details[:cloud_config_yaml].match('\.erb$')
759
+ Logger.debug { "bootstrap or cloud_config_yaml are erb files"}
760
+ instances = Hash.try_convert(Stack.get_our_instances(config))
761
+ all_instances = Hash.try_convert(Stack.get_all_instances(config))
762
+
763
+ Logger.debug { instances }
764
+ Logger.debug { all_instances }
765
+
766
+ multipart_template = ERB.new(multipart)
767
+ multipart_complete = multipart_template.result(binding)
768
+
769
+ Logger.debug { "multipart_complete after erb => #{multipart_complete} " }
770
+ multipart = multipart_complete
771
+ end
772
+
717
773
  Logger.info "Creating #{hostname} in #{node_details[hostname][:az]} with role #{role}"
718
774
 
719
775
  # this will get put in /meta.js
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stack-kicker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.11
4
+ version: 0.0.12
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-14 00:00:00.000000000 Z
12
+ date: 2013-05-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rdoc
@@ -143,9 +143,14 @@ files:
143
143
  - doc/examples/.rvmrc
144
144
  - doc/examples/Gemfile
145
145
  - doc/examples/apache-via-chef-server/Stackfile
146
+ - doc/examples/apache-via-cloud-init/README.md
146
147
  - doc/examples/apache-via-cloud-init/Stackfile
147
148
  - doc/examples/apache-via-cloud-init/cloud-init.sh
148
149
  - doc/examples/apache-via-cloud-init/cloud-init.yaml
150
+ - doc/examples/cloud-init-with-erb/README.md
151
+ - doc/examples/cloud-init-with-erb/Stackfile
152
+ - doc/examples/cloud-init-with-erb/cloud-init.sh.erb
153
+ - doc/examples/cloud-init-with-erb/cloud-init.yaml.erb
149
154
  - features/stack-kicker.feature
150
155
  - features/step_definitions/stack-kicker_steps.rb
151
156
  - features/support/env.rb
@@ -167,12 +172,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
167
172
  - - ! '>='
168
173
  - !ruby/object:Gem::Version
169
174
  version: '0'
175
+ segments:
176
+ - 0
177
+ hash: 503041037987723651
170
178
  required_rubygems_version: !ruby/object:Gem::Requirement
171
179
  none: false
172
180
  requirements:
173
181
  - - ! '>='
174
182
  - !ruby/object:Gem::Version
175
183
  version: '0'
184
+ segments:
185
+ - 0
186
+ hash: 503041037987723651
176
187
  requirements: []
177
188
  rubyforge_project:
178
189
  rubygems_version: 1.8.23