hazetug 0.1.3 → 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: 723a5d37ca91b5fde0e80ec539e3d6a94c1acfdf
4
- data.tar.gz: 8a73f894d14f9c9a91387e0cde7fa1027d7b5a57
3
+ metadata.gz: ebdf9a9772962644f814c2215eb1a788e935a317
4
+ data.tar.gz: 5886036f41c10cc8a7efb87dbce4a3bc21c20d31
5
5
  SHA512:
6
- metadata.gz: 5c3e424bc320a7c7d0fd03fc3ddb882e32b0b265ff682b5960a726c5c079f303d9afd9c67cb810ffe2ce82a8ccdbace8df7de95259c2a6bc2ab896a8cc62ca14
7
- data.tar.gz: 7992188b72be3ea5a17224c443ba7ad71806b5736f37ab06c75d56af9c0b9f2749ee781f03c6a37be0287bbad179606d545d9ae86dfda64456c5fc503b7daf43
6
+ metadata.gz: fbf02d130984ad264c559f943be0b146e84a92ab5999d0390eafab9ff03de631cadf5cf367856c4d1fbb484bb0c547700ece2b5d8c2eeb81706002207acbd5fe
7
+ data.tar.gz: 5ba4e8ff097d31ec9d3e5685ce3a0bf9c6a8165e65bdb45ecef0f6dc1deb92dd46430e0af17e89bcf1e5bc8ed9e3dddb459570279d2e533f4eeab3fda1e825af
data/README.md CHANGED
@@ -3,7 +3,8 @@
3
3
  Cloud provisioner and bootstrapper for DigitalOcean and Linode.
4
4
  Hazetug uses [fog cloud library](http://fog.io) to be able to easily append other cloud computes and *tugs* (bootstraps) hosts using:
5
5
 
6
- * Knife bootstrap.
6
+ * Knife bootstrap for chef-client.
7
+ * Knife bootstrap for chef-solo.
7
8
 
8
9
  ## Options
9
10
 
@@ -64,8 +65,6 @@ Hazetug uses [fog cloud library](http://fog.io) to be able to easily append othe
64
65
 
65
66
  ## Knife tug
66
67
 
67
- ### Knife tug bootstrap options
68
-
69
68
  <table>
70
69
  <tr>
71
70
  <td><b>Option</b></td>
@@ -89,6 +88,27 @@ Hazetug uses [fog cloud library](http://fog.io) to be able to easily append othe
89
88
  </tr>
90
89
  </table>
91
90
 
91
+ ## Solo tug
92
+
93
+ <table>
94
+ <tr>
95
+ <td><b>Option</b></td>
96
+ <td><b>Description</b></td>
97
+ <td><b>Default value</b></td>
98
+ </tr>
99
+ <tr>
100
+ <td><b><i>attributes_json</i></b></td>
101
+ <td>Hash of attributes prepared for chef-solo run. (It's merged with the run_list).</td>
102
+ <td><i>{}</i><td>
103
+ </tr>
104
+ <tr>
105
+ <td><b><i>berksfile</i></b></td>
106
+ <td>Path to Berksfile.</td>
107
+ <td></td>
108
+ </tr>
109
+ </table>
110
+
111
+
92
112
  ## Installation
93
113
 
94
114
  Add this line to your application's Gemfile:
@@ -151,7 +171,7 @@ All variables are merged using this 3-level priority.
151
171
 
152
172
  ### Bootstrap using knife
153
173
 
154
- Help for linode compute is given bellow:
174
+ Help for the linode compute is given bellow:
155
175
 
156
176
  ```
157
177
  NAME
@@ -169,6 +189,40 @@ COMMAND OPTIONS
169
189
 
170
190
  All variables are passed to the bootstrap template and are available using the hazetug hash like - `hazetug[:variable_name]`. Amongst variables described here in the options sections, hazetug also passes useful variables such as ***compute_name***, ***public_ip_address***, ***private_ip_address*** if those are available.
171
191
 
192
+ #### Client and Solo modes
193
+
194
+ It's possible to use *chef-client* or *chef-solo* to bootstrap a node. The **solo** mode is almost identical with the **client** mode, but with the only difference that it uses **berkshelf** to assist during the bootstrap process. Cookbooks are packaged and uploaded to the remote node and can be used by chef
195
+
196
+ You can initiate bootstrap in the solo mode, like the following:
197
+ <pre>
198
+ hazetug digitalocean bootstrap solo -b bootstrap.erb solo-task.yaml
199
+ </pre>
200
+
201
+ Solo bootstrap also uses bootstrap.erb, but there's no need to start client or use any data like **validation_key** or **start_chef** helper.
202
+
203
+ When defining a task, there additional options **berksfile** and **attributes**
204
+
205
+ ```yaml
206
+ ...
207
+ ...
208
+ bootstrap:
209
+ - name: solo-box
210
+ location: london
211
+ flavor: 1gb
212
+ image: ubuntu-14.04-x64
213
+ run_list: ["role[api]"]
214
+ attributes: {
215
+ "mycookbook": {
216
+ "settings": {}
217
+ }
218
+ }
219
+ ```
220
+ ```
221
+
222
+ Merged *attributes* with the *run_list* form the input data for running **chef-solo** they are available inside bootstrap.erb as `hazetug[:attributes_json]`. Berksfile can be set specifically in the bootstrap task list or if it's not set local *Berksfile* is tried by default.
223
+
224
+ Another important option which is also available in the *bootstrap.erb* file is `hazetug[:cookbooks_path]` which is path to an archive of cookbooks packaged by Berkshelf.
225
+
172
226
  #### Examples
173
227
 
174
228
  * Provisioning and bootstrapping 5 nodes, each 3 of them will be processed simultaneously:
@@ -0,0 +1,82 @@
1
+ <% require 'json' -%>
2
+ bash -c '
3
+
4
+ mkdir -p /etc/chef
5
+
6
+ cat > /etc/chef/validation.pem <<'EOP'
7
+ <%= validation_key %>
8
+ EOP
9
+
10
+ chmod 0600 /etc/chef/validation.pem
11
+
12
+ cat > /etc/chef/client.rb <<'EOP'
13
+ <%= config_content %>
14
+ EOP
15
+
16
+ cat > /etc/chef/first-boot.json <<'EOP'
17
+ {
18
+ "run_list": <%= (["role[bootstrap]"] + (hazetug[:bootstrap_list] || [])).to_json %>,
19
+ "bootstrap": {
20
+ "set_run_list": <%= (hazetug[:run_list] || []).to_json %>,
21
+ "set_environment": "<%= hazetug[:chef_environment] %>"
22
+ }
23
+ }
24
+ EOP
25
+
26
+ # --------------- Update ubuntu and Install packages
27
+
28
+ export DEBIAN_FRONTEND=noninteractive
29
+ apt-get update && apt-get upgrade -yq
30
+ apt-get clean
31
+ apt-get autoremove -yq
32
+
33
+ apt-get -yq install curl build-essential
34
+
35
+
36
+ # --------------- Set hostname
37
+
38
+ HOSTNAME="<%= hazetug[:name] %>"
39
+ echo "${HOSTNAME}" > /etc/hostname
40
+ ip=`/sbin/ifconfig eth0 | grep "\<inet\>" | cut -d ":" -f2 | cut -d " " -f1`
41
+ echo -e "\n# our own entry\n${ip} ${HOSTNAME} ${HOSTNAME%%.*}" >> /etc/hosts
42
+ cp /etc/hosts /tmp/hosts.orig
43
+ cat /tmp/hosts.orig | sed "s/127.0.0.1.*/127.0.0.1 localhost localhost.localdomain/" > /etc/hosts
44
+ rm /tmp/hosts.orig
45
+ hostname -F /etc/hostname
46
+
47
+ # --------------- Set private network on Linode
48
+
49
+ if [ "<%= hazetug[:compute_name] %>" = "linode" ]; then
50
+
51
+ cat >> /etc/network/interfaces <<'EOP'
52
+
53
+ auto eth0:0
54
+ iface eth0:0 inet static
55
+ address <%= hazetug[:private_ip_address] %>
56
+ netmask 255.255.128.0
57
+ EOP
58
+ ifup eth0:0
59
+
60
+ fi
61
+
62
+
63
+ # --------------- Install Chef
64
+
65
+ curl -L https://get.rvm.io | bash -s <%= hazetug[:rvm_version] || 'stable' %>
66
+ source /etc/profile.d/rvm.sh
67
+
68
+ rvm install <%= hazetug[:ruby_version] || "ruby-2.1.2" %> --autolibs=3
69
+ rvm use <%= hazetug[:ruby_version] || "ruby-2.1.2" %> --default
70
+
71
+ cat >/tmp/chef_gemfile<<EOF
72
+ source "http://rubygems.org"
73
+ gem "chef", "<%= hazetug[:chef_version] || "11.14.6" %>"
74
+ EOF
75
+
76
+ bundle install --gemfile /tmp/chef_gemfile
77
+ rm /tmp/chef_gemfile
78
+
79
+ # --------------- Start Chef (bootstrap run)
80
+
81
+ echo "<%= start_chef %>"
82
+ <%= start_chef %>'
@@ -0,0 +1,64 @@
1
+ <% require 'json' -%>
2
+ bash -c '
3
+
4
+ mkdir -p /etc/chef
5
+
6
+ cat > /etc/chef/first-boot.json <<'EOP'
7
+ <%= hazetug[:attributes_json] %>
8
+ EOP
9
+
10
+ # --------------- Update ubuntu and Install packages
11
+
12
+ export DEBIAN_FRONTEND=noninteractive
13
+ apt-get update && apt-get upgrade -yq
14
+ apt-get clean
15
+ apt-get autoremove -yq
16
+
17
+ apt-get -yq install curl build-essential
18
+
19
+
20
+ # --------------- Set hostname
21
+
22
+ HOSTNAME="<%= hazetug[:name] %>"
23
+ echo "${HOSTNAME}" > /etc/hostname
24
+ ip=`/sbin/ifconfig eth0 | grep "\<inet\>" | cut -d ":" -f2 | cut -d " " -f1`
25
+ echo -e "\n# our own entry\n${ip} ${HOSTNAME} ${HOSTNAME%%.*}" >> /etc/hosts
26
+ cp /etc/hosts /tmp/hosts.orig
27
+ cat /tmp/hosts.orig | sed "s/127.0.0.1.*/127.0.0.1 localhost localhost.localdomain/" > /etc/hosts
28
+ rm /tmp/hosts.orig
29
+ hostname -F /etc/hostname
30
+
31
+ # --------------- Set private network on Linode
32
+
33
+ if [ "<%= hazetug[:compute_name] %>" = "linode" ]; then
34
+
35
+ cat >> /etc/network/interfaces <<'EOP'
36
+
37
+ auto eth0:0
38
+ iface eth0:0 inet static
39
+ address <%= hazetug[:private_ip_address] %>
40
+ netmask 255.255.128.0
41
+ EOP
42
+ ifup eth0:0
43
+
44
+ fi
45
+
46
+ # --------------- Install Chef
47
+
48
+ curl -L https://get.rvm.io | bash -s <%= hazetug[:rvm_version] || 'stable' %>
49
+ source /etc/profile.d/rvm.sh
50
+
51
+ rvm install <%= hazetug[:ruby_version] || "ruby-2.1.2" %> --autolibs=3
52
+ rvm use <%= hazetug[:ruby_version] || "ruby-2.1.2" %> --default
53
+
54
+ cat >/tmp/chef_gemfile<<EOF
55
+ source "http://rubygems.org"
56
+ gem "chef", "<%= hazetug[:chef_version] || "11.14.6" %>"
57
+ EOF
58
+
59
+ bundle install --gemfile /tmp/chef_gemfile
60
+ rm /tmp/chef_gemfile /tmp/chef_gemfile.lock
61
+
62
+ # --------------- Start Chef in solo mode
63
+
64
+ chef-solo -j /etc/chef/first-boot.json -r <%= hazetug[:cookbooks_file] %>'
data/hazetug.gemspec CHANGED
@@ -7,9 +7,9 @@ Gem::Specification.new do |spec|
7
7
  spec.name = "hazetug"
8
8
  spec.version = Hazetug::VERSION
9
9
  spec.authors = ["Denis Barishev"]
10
- spec.email = ["denz@twiket.com"]
10
+ spec.email = ["dennybaa@gmail.com"]
11
11
  spec.summary = %q{Cloud provisoner tool}
12
- spec.description = %q{Hazetug uses fog cloud library and he}
12
+ spec.description = %q{Provisions and bootstraps nodes using knife}
13
13
  spec.homepage = "https://github.com/dennybaa/hazetug"
14
14
  spec.license = "MIT"
15
15
 
@@ -19,10 +19,11 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.5"
22
- spec.add_development_dependency "rake"
23
- spec.add_dependency "psych"
24
- spec.add_dependency "fog"
25
- spec.add_dependency "chef", ">= 11.10.0"
26
- spec.add_dependency "gli"
27
- spec.add_dependency "agent"
22
+ spec.add_development_dependency "rake", "~> 0"
23
+ spec.add_dependency "psych", "~> 0"
24
+ spec.add_dependency "fog", "~> 0"
25
+ spec.add_dependency "chef", "~> 11.0"
26
+ spec.add_dependency "gli", "~> 2.0"
27
+ spec.add_dependency "agent", "~> 0"
28
+ spec.add_dependency "berkshelf", "~> 3.0"
28
29
  end
@@ -3,7 +3,6 @@ require 'hazetug/cli/action'
3
3
  require 'hazetug/tug'
4
4
  require 'hazetug/task'
5
5
 
6
-
7
6
  class Hazetug
8
7
  class CLI
9
8
  class Bootstrap < Action
@@ -26,11 +25,15 @@ class Hazetug
26
25
 
27
26
  def provision_and_bootstrap(haze, tug, channel, waitgroup)
28
27
  haze.provision
29
- tug.bootstrap({
30
- args: data[:args],
31
- opts: data[:opts],
32
- gopts: data[:gopts]
33
- })
28
+ if haze.ready?
29
+ tug.load_haze_config(haze.config_for_tug)
30
+
31
+ tug.bootstrap({
32
+ args: data[:args],
33
+ opts: data[:opts],
34
+ gopts: data[:gopts]
35
+ })
36
+ end
34
37
  rescue
35
38
  # Exeception will be lost, since we run inside goproc,
36
39
  # ie. as soon as waitgroup is empty all process exit.
@@ -62,7 +65,7 @@ class Hazetug
62
65
  newconf[:ssh_password] = haze.config[:ssh_password]
63
66
  end
64
67
 
65
- tug = Hazetug::Tug[data[:tug_name]].new(newconf, haze)
68
+ tug = Hazetug::Tug[data[:tug_name]].new(newconf)
66
69
  block.call(haze, tug)
67
70
  end
68
71
  end
data/lib/hazetug/cli.rb CHANGED
@@ -29,40 +29,45 @@ class Hazetug
29
29
  compute_cmd.desc 'Provisions and bootstraps server'
30
30
  compute_cmd.command :bootstrap do |op|
31
31
 
32
- op.desc 'Bootstraps server using Knife'
33
- op.arg_name 'task.yaml'
34
- op.command :knife do |tug|
35
- tug.arg_name nil
36
-
37
- tug.flag [:v, :variables], :must_match => Array,
38
- :desc => 'Set variable or comma-seperated list of variables (var1_1=hello)'
39
-
40
- tug.flag [:n, :number], :default_value => 1,
41
- :desc => 'Set number of created nodes, value from yaml is honored'
42
-
43
- tug.flag [:c, :concurrency], :default_value => 1,
44
- :desc => 'Set concurrency value, i.e. number of hosts bootstraped simultaneously'
45
-
46
- tug.flag [:b, :bootstrap], :default_value => 'bootstrap.erb',
47
- :desc => 'Set path to knife bootstrap.erb file'
48
-
49
- tug.action do |gopts, opts, args|
50
-
51
- if args.empty?
52
- commands[:help].execute({},{},tug.name_for_help)
53
- exit 0
32
+ op.flag [:v, :variables], :must_match => Array,
33
+ :desc => 'Set variable or comma-seperated list of variables (var1_1=hello)'
34
+
35
+ op.flag [:n, :number], :default_value => 1,
36
+ :desc => 'Set number of created nodes, value from yaml is honored'
37
+
38
+ op.flag [:c, :concurrency], :default_value => 1,
39
+ :desc => 'Set concurrency value, i.e. number of hosts bootstraped simultaneously'
40
+
41
+ op.flag [:b, :bootstrap], :default_value => 'bootstrap.erb',
42
+ :desc => "Set path to knife bootstrap.erb file"
43
+
44
+ [:knife, :solo].each do |tug_cmd|
45
+ mode = "#{tug_cmd == :knife ? 'client' : 'solo'}"
46
+
47
+
48
+ op.desc "Bootstraps server using Knife in #{mode} mode"
49
+ op.arg_name '<task.yaml>'
50
+ op.command tug_cmd do |tug|
51
+
52
+ tug.action do |gopts, opts, args|
53
+
54
+ if args.empty?
55
+ commands[:help].execute({},{},tug.name_for_help)
56
+ exit 0
57
+ end
58
+
59
+ act = CLI::Action[:bootstrap].new
60
+ act.pass(
61
+ tug_name: tug_cmd,
62
+ compute_name: compute,
63
+ cli: tug,
64
+ gopts: gopts,
65
+ opts: opts,
66
+ args: args
67
+ ).execute
54
68
  end
55
-
56
- act = CLI::Action[:bootstrap].new
57
- act.pass(
58
- tug_name: :knife,
59
- compute_name: compute,
60
- cli: tug,
61
- gopts: gopts,
62
- opts: opts,
63
- args: args
64
- ).execute
65
69
  end
70
+
66
71
  end
67
72
 
68
73
  end
@@ -72,4 +77,4 @@ class Hazetug
72
77
  end
73
78
 
74
79
  end
75
- end
80
+ end
@@ -15,11 +15,12 @@ class Hazetug
15
15
  ssh_options[:password] = ssh_opts[:ssh_password]
16
16
  ssh_options[:paranoid] = ssh_opts[:host_key_verify] || false
17
17
  ssh_options[:keys] = ssh_opts[:ssh_keys] || Hazetug.ssh_keys(compute_name)
18
- server.username = ssh_opts[:ssh_user]
18
+ server.username = ssh_opts[:ssh_user] || 'root'
19
19
  server.ssh_port = ssh_opts[:ssh_port]
20
20
  server.ssh_options = ssh_options
21
21
  ui.info "[#{compute_name}] waiting for active ssh on #{server.ssh_ip_address}"
22
22
  server.wait_for(30) { sshable? }
23
+ @ready = true
23
24
  rescue Fog::Errors::TimeoutError
24
25
  ui.error "[#{compute_name}] ssh failed to #{config[:name]}, ip: #{server.ssh_ip_address}"
25
26
  end
@@ -20,7 +20,7 @@ class Hazetug
20
20
  latest = /^Latest #{config[:bits]} bit/
21
21
  {
22
22
  # linode node name can't contain dots
23
- :name => config[:name].gsub(/./,'-'),
23
+ :name => config[:name].gsub(/\./,'-'),
24
24
  :data_center => lookup(:location),
25
25
  :flavor => lookup(:flavor),
26
26
  :image => lookup(:image),
data/lib/hazetug/haze.rb CHANGED
@@ -16,7 +16,7 @@ class Hazetug
16
16
  @compute = Hazetug::Compute.const_get(compute_name).new
17
17
  @config = configure(config)
18
18
  @server = nil
19
- @sshable = false
19
+ @ready = false
20
20
  end
21
21
 
22
22
  def provision
@@ -28,6 +28,10 @@ class Hazetug
28
28
  exit(1)
29
29
  end
30
30
 
31
+ def ready?
32
+ @ready
33
+ end
34
+
31
35
  def configure(config)
32
36
  input = config.keys.map(&:to_sym)
33
37
  requires = self.class.requires
@@ -67,6 +71,14 @@ class Hazetug
67
71
  def private_ip_address
68
72
  end
69
73
 
74
+ def config_for_tug
75
+ {
76
+ compute_name: compute_name.downcase,
77
+ public_ip_address: (public_ip_address || server.ssh_ip_address rescue nil),
78
+ private_ip_address: private_ip_address
79
+ }
80
+ end
81
+
70
82
  protected
71
83
 
72
84
  def provision_server
@@ -1,104 +1,26 @@
1
1
  require 'chef/knife/ssh'
2
2
  require 'chef/knife/bootstrap'
3
- require 'hazetug/tug'
4
-
5
- # Monkey Patch:)
6
- # Extend knife bootstrap context with our data
7
- class Chef::Knife::Core::BootstrapContext
8
- def hazetug; @config[:hazetug]; end
9
- end
10
-
11
- class Chef::Knife::Ssh < Chef::Knife
12
- def run
13
- extend Chef::Mixin::Command
14
- @longest = 0
15
- configure_attribute
16
- configure_user
17
- configure_password
18
- configure_identity_file
19
- configure_gateway
20
- configure_session
21
- exit_status = ssh_command(@name_args[1..-1].join(" "))
22
- session.close
23
-
24
- exit_status
25
- end
26
- end
3
+ require 'hazetug/tug/knife_base'
27
4
 
28
5
  class Hazetug
29
6
  class Tug
30
- class Knife < Tug
7
+ class Knife < KnifeBase
31
8
 
32
- def bootstrap_server
33
- [
34
- :template_file,
35
- :identity_file,
36
- :ssh_user,
37
- :ssh_password,
38
- :host_key_verify
39
- ].each do |opt|
40
- kb.config[opt] = bootstrap_options[opt]
41
- end
42
- [
9
+ def initialize(config={}, haze=nil)
10
+ super
11
+ @chef_option_list = [
43
12
  :environment,
44
13
  :chef_server_url,
45
14
  :validation_key
46
- ].each do |opt|
47
- Chef::Config[opt] = bootstrap_options[opt]
48
- end
49
- kb.name_args = [haze.server.ssh_ip_address]
50
- kb.run
51
- rescue Hazetug::Exception => e
52
- ui.error(e.message); 1
53
- ensure
54
- @kb and @kb.ui.stdout.close
15
+ ]
55
16
  end
56
17
 
57
- def kb
58
- @kb ||= begin
59
- lf = create_log_file
60
- Chef::Knife::Bootstrap.load_deps
61
- kb = Chef::Knife::Bootstrap.new
62
- kb.config[:hazetug] = config
63
- kb.ui = Chef::Knife::UI.new(lf, lf, lf, {verbosity: 2})
64
- kb
65
- end
66
- end
67
-
68
- def bootstrap_options
69
- @bootstrap_options ||= begin
70
- template = options[:opts][:bootstrap] || 'bootstrap.erb'
71
- validation = config[:chef_validation_key] || 'validation.pem'
72
-
73
- files = [template, validation].map {|f| File.expand_path(f)}
74
- notfound = files.select {|f| !File.exist?(f)}
75
- notfound.empty? or
76
- raise Hazetug::Exception, "File(s) not found: #{notfound.join(', ')}"
77
-
78
- opts = {}
79
- opts[:validation_key] = File.expand_path(validation)
80
- opts[:template_file] = File.expand_path(template)
81
- opts[:ssh_user] = config[:ssh_user] || 'root'
82
- opts[:ssh_password] = config[:ssh_password]
83
- opts[:environment] = config[:chef_environment]
84
- opts[:host_key_verify] = config[:host_key_verify] || false
85
- opts[:chef_server_url] = config[:chef_server_url]
86
- opts[:identity_file] = preferred_ssh_identity if not opts[:ssh_password]
87
- opts
88
- end
89
- end
90
-
91
- def preferred_ssh_identity
92
- @preferred_ssh_identity ||= begin
93
- compute = Hazetug.leaf_klass_name(haze.class.name).downcase
94
- identity = config[:identity_file]
95
- key_path = (Hazetug::Config["#{compute}_ssh_keys"] || []).first
96
- if identity.nil? && key_path.nil?
97
- raise Hazetug::Exception, "identity file not specified, use #{compute}_ssh_keys or identity_file"
98
- end
99
- identity ||= File.expand_path(key_path)
100
- identity
18
+ def bootstrap_config
19
+ super
20
+ @chef_option_list.each do |opt|
21
+ Chef::Config[opt] = bootstrap_options[opt]
101
22
  end
23
+ check_bootstrap_files! :validation_key, :template_file
102
24
  end
103
25
 
104
26
  end
@@ -0,0 +1,164 @@
1
+ require 'erubis'
2
+ require 'chef/knife/ssh'
3
+ require 'chef/knife/bootstrap'
4
+ require 'chef/knife/core/bootstrap_context'
5
+ require 'hazetug/tug'
6
+
7
+ # Extend knife bootstrap context with our data.
8
+ # It's monkey patching, yey :)
9
+ class Chef::Knife::Bootstrap
10
+ def render_template(template=nil)
11
+ context = Chef::Knife::Core::BootstrapContext.new(config, config[:run_list], Chef::Config)
12
+ @extended_bootstrap_variables.each do |s, v|
13
+ context.instance_variable_set(s, v)
14
+ end
15
+ Erubis::Eruby.new(template).evaluate(context)
16
+ end
17
+
18
+ def add_variable_to_bootstrap_context(variable_sym, value)
19
+ @extended_bootstrap_variables ||= {}
20
+ @extended_bootstrap_variables[variable_sym] = value if value
21
+ end
22
+ end
23
+
24
+ class Chef::Knife::Core::BootstrapContext
25
+ def hazetug; @hazetug; end
26
+ end
27
+
28
+ class Chef::Knife::Ssh < Chef::Knife
29
+ def run
30
+ extend Chef::Mixin::Command
31
+ @longest = 0
32
+ configure_attribute
33
+ configure_user
34
+ configure_password
35
+ configure_identity_file
36
+ configure_gateway
37
+ configure_session
38
+ exit_status = ssh_command(@name_args[1..-1].join(" "))
39
+ session.close
40
+
41
+ exit_status
42
+ end
43
+ end
44
+
45
+ class Hazetug
46
+ class Tug
47
+ class KnifeBase < Tug
48
+
49
+ def initialize(config={})
50
+ super
51
+ @bootstrap_option_list = [
52
+ :template_file,
53
+ :identity_file,
54
+ :ssh_user,
55
+ :ssh_password,
56
+ :host_key_verify
57
+ ]
58
+ end
59
+
60
+ # Bootstraps the remote server using knife bootstrap.
61
+ def bootstrap_server
62
+ bootstrap_config
63
+
64
+ bootstrap_init
65
+ bootstrap_run
66
+ bootstrap_cleanup
67
+ rescue Hazetug::Exception => e
68
+ ui.error(e.message); 1
69
+ ensure
70
+ knife and knife.ui.stdout.close
71
+ end
72
+
73
+ # Configures knife bootstrap by setting the necessary options.
74
+ def bootstrap_config
75
+ knife.add_variable_to_bootstrap_context(:@hazetug, config)
76
+
77
+ @bootstrap_option_list.each do |opt|
78
+ knife.config[opt] = bootstrap_options[opt]
79
+ end
80
+
81
+ # Check ssh identity
82
+ cred = [
83
+ hazetug_identity, bootstrap_options[:identity_file],
84
+ bootstrap_options[:ssh_password]
85
+ ]
86
+
87
+ if cred.all?(&:nil?)
88
+ msg = "No identity (inc. ssh_password) found. Check ssh_password," <<
89
+ " identity_file options or #{config[:compute_name]}_ssh_keys " <<
90
+ "hazetug parameter."
91
+
92
+ raise Hazetug::Exception, msg
93
+ end
94
+ end
95
+
96
+ # Pre knife bootstrap hook.
97
+ def bootstrap_init
98
+ end
99
+
100
+ # After knife bootstrap hook.
101
+ def bootstrap_cleanup
102
+ end
103
+
104
+ # Initiates knife bootstrap run.
105
+ def bootstrap_run
106
+ knife.name_args = [config[:public_ip_address]]
107
+ knife.run
108
+ end
109
+
110
+ # Initializes knife bootstrap, default output is redirected into file.
111
+ def knife
112
+ @knife ||= begin
113
+ lf = create_log_file
114
+ Chef::Knife::Bootstrap.load_deps
115
+ kb = Chef::Knife::Bootstrap.new
116
+ kb.ui = Chef::Knife::UI.new(lf, lf, lf, {verbosity: 2})
117
+ kb
118
+ end
119
+ end
120
+
121
+ # Extracts bootstrap options from the hazetug configuration.
122
+ def bootstrap_options
123
+ @bootstrap_options ||= begin
124
+ opts = {}
125
+
126
+ opts[:ssh_user] = config[:ssh_user] || 'root'
127
+ opts[:ssh_password] = config[:ssh_password]
128
+ if opts[:ssh_password].nil?
129
+ opts[:identity_file] = config[:identity_file] || hazetug_identity
130
+ end
131
+
132
+ template = options[:opts][:bootstrap] || 'bootstrap.erb'
133
+ validation = config[:chef_validation_key] || 'validation.pem'
134
+
135
+ opts[:validation_key] = File.expand_path(validation)
136
+ opts[:template_file] = File.expand_path(template)
137
+ opts[:environment] = config[:chef_environment]
138
+ opts[:host_key_verify] = config[:host_key_verify] || false
139
+ opts[:chef_server_url] = config[:chef_server_url]
140
+ opts
141
+ end
142
+ end
143
+
144
+ # Lookup ssh identity key(s) in the hazetug configuration.
145
+ def hazetug_identity
146
+ @hazetug_identity ||= begin
147
+ compute = config[:compute_name]
148
+ key_path = (Hazetug::Config["#{compute}_ssh_keys"] || []).first
149
+ key_path and File.expand_path(key_path)
150
+ end
151
+ end
152
+
153
+ # Check if files exist otherwise fail.
154
+ def check_bootstrap_files!(*list_of_opts)
155
+ list = list_of_opts.map {|k| bootstrap_options[k]}
156
+ files = list.map {|f| File.expand_path(f.to_s)}
157
+ notfound = files.select {|f| !File.exist?(f)}
158
+ notfound.empty? or
159
+ raise Hazetug::Exception, "File(s) not found: #{notfound.join(', ')}"
160
+ end
161
+
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,90 @@
1
+ require 'chef/knife/ssh'
2
+ require 'chef/knife/bootstrap'
3
+ require 'berkshelf'
4
+ require 'berkshelf/berksfile'
5
+ require 'json'
6
+ require 'hazetug/tug/knife_base'
7
+
8
+ class Hazetug
9
+ class Tug
10
+ class Solo < KnifeBase
11
+
12
+ def initialize(config={})
13
+ super
14
+ @upload_dest = '/tmp/hazetug-cookbooks.tar.gz'
15
+ end
16
+
17
+ def bootstrap_config
18
+ super
19
+ config[:attributes_json] = json_attributes
20
+ config[:cookbooks_file] = @upload_dest
21
+
22
+ check_bootstrap_files! :template_file, :berksfile
23
+ end
24
+
25
+ def bootstrap_init
26
+ upload_berks_package
27
+ end
28
+
29
+ def bootstrap_cleanup
30
+ ssh = Fog::SSH.new(config[:public_ip_address], ssh_username, ssh_options)
31
+ ssh.run("test -f #{@upload_dest} && rm #{@upload_dest}")
32
+ end
33
+
34
+ def berks_package(&block)
35
+ berks = Berkshelf::Berksfile.from_file(bootstrap_options[:berksfile])
36
+
37
+ Dir::Tmpname.create('hazetug-cookbooks') do |path|
38
+ begin
39
+ berks.package(path)
40
+ block.call(path)
41
+ ensure
42
+ File.unlink(path) if File.exist?(path)
43
+ end
44
+ end
45
+ end
46
+
47
+ def upload_berks_package
48
+ berks_package do |path|
49
+ scp = Fog::SCP.new(config[:public_ip_address], ssh_username, ssh_options)
50
+ scp.upload(path, @upload_dest)
51
+ end
52
+ end
53
+
54
+ def bootstrap_options
55
+ @bootstrap_options ||= begin
56
+ opts = super
57
+ opts[:berksfile] = config[:berksfile] || 'Berksfile'
58
+ opts
59
+ end
60
+ end
61
+
62
+ def json_attributes
63
+ @json_attributes ||= begin
64
+ hash = config[:attributes] || {}
65
+ hash[:run_list] = config[:run_list] || []
66
+ JSON.pretty_generate(hash)
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def ssh_options
73
+ @ssh_options ||= begin
74
+ ssh_options = {}
75
+ ssh_opts = Hazetug::Tug.ssh_options_from(config)
76
+ ssh_options[:password] = ssh_opts[:ssh_password]
77
+ ssh_options[:paranoid] = ssh_opts[:host_key_verify] || false
78
+ ssh_options[:keys] = ssh_opts[:ssh_keys] || Hazetug.ssh_keys(config[:compute_name])
79
+ ssh_options[:port] = ssh_opts[:ssh_port]
80
+ ssh_options
81
+ end
82
+ end
83
+
84
+ def ssh_username
85
+ config[:ssh_user] || 'root'
86
+ end
87
+
88
+ end
89
+ end
90
+ end
data/lib/hazetug/tug.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'hazetug/tug/knife'
2
+ require 'hazetug/tug/solo'
2
3
  require 'hazetug/ui'
3
4
  require 'chef/mash'
4
5
 
@@ -12,10 +13,9 @@ class Hazetug
12
13
  ]
13
14
  LOGDIR = "#{Dir.pwd}/logs"
14
15
 
15
- attr_reader :haze, :config, :options
16
+ attr_reader :config, :options
16
17
 
17
- def initialize(config={}, haze=nil)
18
- @haze = haze
18
+ def initialize(config={})
19
19
  @config = config
20
20
  end
21
21
 
@@ -24,22 +24,23 @@ class Hazetug
24
24
  end
25
25
 
26
26
  def bootstrap(options={})
27
- if haze && haze.server && haze.server.sshable?
28
- @options = options
29
- haztug_set_variables
30
- ip = config[:public_ip_address]
31
- ui.msg "[#{tug_name}] bootstraping server #{haze.config[:name]}, ip: #{ip}"
32
- exit_status = bootstrap_server
33
- if exit_status.is_a?(Fixnum) && exit_status != 0
34
- ui.error "[#{tug_name}] bootstraping server #{haze.config[:name]} failed."
35
- else
36
- ui.msg "[#{tug_name}] bootstraping server #{haze.config[:name]} done."
37
- end
27
+ @options = options
28
+ ip = config[:public_ip_address]
29
+
30
+ ui.msg "[#{tug_name}] bootstraping server #{config[:name]}, ip: #{ip}"
31
+ exit_status = bootstrap_server
32
+
33
+ if exit_status.is_a?(Fixnum) && exit_status != 0
34
+ ui.error "[#{tug_name}] bootstraping server #{config[:name]} failed."
38
35
  else
39
- ui.error "#{haze.compute_name} skipping bootstrap, server #{haze.config[:name]} not ready"
36
+ ui.msg "[#{tug_name}] bootstraping server #{config[:name]} done."
40
37
  end
41
38
  rescue Hazetug::Exception => e
42
- ui.error "[#{haze.compute_name}] #{e.message}"
39
+ ui.error "[#{config[:compute_name]}] #{e.message}"
40
+ end
41
+
42
+ def load_haze_config(hash)
43
+ @config.merge!(hash)
43
44
  end
44
45
 
45
46
  class << self
@@ -58,25 +59,13 @@ class Hazetug
58
59
  end
59
60
  end
60
61
 
61
- private
62
-
63
- def haztug_set_variables
64
- {
65
- compute_name: haze.compute_name.downcase,
66
- public_ip_address: (haze.public_ip_address || haze.server.ssh_ip_address rescue nil),
67
- private_ip_address: haze.private_ip_address
68
- }.each do |key, value|
69
- config[key] = value if value
70
- end
71
- end
72
-
73
62
  protected
74
63
 
75
64
  def create_log_file
76
65
  unless File.directory?(LOGDIR)
77
66
  Dir.mkdir(LOGDIR)
78
67
  end
79
- log = File.new("#{LOGDIR}/#{haze.config[:name]}", "w+")
68
+ log = File.new("#{LOGDIR}/#{config[:name]}", "w+")
80
69
  log.sync = true
81
70
  log
82
71
  end
@@ -1,3 +1,3 @@
1
1
  class Hazetug
2
- VERSION = "0.1.3"
2
+ VERSION = "0.1.4"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hazetug
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Barishev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-28 00:00:00.000000000 Z
11
+ date: 2014-09-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -28,89 +28,103 @@ dependencies:
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: psych
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: fog
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: chef
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 11.10.0
75
+ version: '11.0'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">="
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 11.10.0
82
+ version: '11.0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: gli
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ">="
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0'
89
+ version: '2.0'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ">="
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0'
96
+ version: '2.0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: agent
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ">="
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
103
  version: '0'
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ">="
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
- description: Hazetug uses fog cloud library and he
111
+ - !ruby/object:Gem::Dependency
112
+ name: berkshelf
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '3.0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '3.0'
125
+ description: Provisions and bootstraps nodes using knife
112
126
  email:
113
- - denz@twiket.com
127
+ - dennybaa@gmail.com
114
128
  executables:
115
129
  - hazetug
116
130
  extensions: []
@@ -122,6 +136,8 @@ files:
122
136
  - README.md
123
137
  - Rakefile
124
138
  - bin/hazetug
139
+ - examples/bootstrap-client.erb
140
+ - examples/bootstrap-solo.erb
125
141
  - hazetug.gemspec
126
142
  - lib/hazetug.rb
127
143
  - lib/hazetug/cli.rb
@@ -137,6 +153,8 @@ files:
137
153
  - lib/hazetug/task.rb
138
154
  - lib/hazetug/tug.rb
139
155
  - lib/hazetug/tug/knife.rb
156
+ - lib/hazetug/tug/knife_base.rb
157
+ - lib/hazetug/tug/solo.rb
140
158
  - lib/hazetug/ui.rb
141
159
  - lib/hazetug/version.rb
142
160
  homepage: https://github.com/dennybaa/hazetug