stack-kicker 0.0.11 → 0.0.12

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