Skip to content

Commit

Permalink
knife bootstrap in solo mode
Browse files Browse the repository at this point in the history
  • Loading branch information
Denis Barishev committed Sep 4, 2014
1 parent f2287e9 commit f841bf1
Show file tree
Hide file tree
Showing 14 changed files with 561 additions and 174 deletions.
62 changes: 58 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
Cloud provisioner and bootstrapper for DigitalOcean and Linode.
Hazetug uses [fog cloud library](http://fog.io) to be able to easily append other cloud computes and *tugs* (bootstraps) hosts using:

* Knife bootstrap.
* Knife bootstrap for chef-client.
* Knife bootstrap for chef-solo.

## Options

Expand Down Expand Up @@ -64,8 +65,6 @@ Hazetug uses [fog cloud library](http://fog.io) to be able to easily append othe

## Knife tug

### Knife tug bootstrap options

<table>
<tr>
<td><b>Option</b></td>
Expand All @@ -89,6 +88,27 @@ Hazetug uses [fog cloud library](http://fog.io) to be able to easily append othe
</tr>
</table>

## Solo tug

<table>
<tr>
<td><b>Option</b></td>
<td><b>Description</b></td>
<td><b>Default value</b></td>
</tr>
<tr>
<td><b><i>attributes_json</i></b></td>
<td>Hash of attributes prepared for chef-solo run. (It's merged with the run_list).</td>
<td><i>{}</i><td>
</tr>
<tr>
<td><b><i>berksfile</i></b></td>
<td>Path to Berksfile.</td>
<td></td>
</tr>
</table>


## Installation

Add this line to your application's Gemfile:
Expand Down Expand Up @@ -151,7 +171,7 @@ All variables are merged using this 3-level priority.
### Bootstrap using knife
Help for linode compute is given bellow:
Help for the linode compute is given bellow:
```
NAME
Expand All @@ -169,6 +189,40 @@ COMMAND OPTIONS
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.

#### Client and Solo modes

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

You can initiate bootstrap in the solo mode, like the following:
<pre>
hazetug digitalocean bootstrap solo -b bootstrap.erb solo-task.yaml
</pre>

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.

When defining a task, there additional options **berksfile** and **attributes**

```yaml
...
...
bootstrap:
- name: solo-box
location: london
flavor: 1gb
image: ubuntu-14.04-x64
run_list: ["role[api]"]
attributes: {
"mycookbook": {
"settings": {}
}
}
```
```

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.

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.

#### Examples

* Provisioning and bootstrapping 5 nodes, each 3 of them will be processed simultaneously:
Expand Down
82 changes: 82 additions & 0 deletions examples/bootstrap-client.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<% require 'json' -%>
bash -c '

mkdir -p /etc/chef

cat > /etc/chef/validation.pem <<'EOP'
<%= validation_key %>
EOP

chmod 0600 /etc/chef/validation.pem

cat > /etc/chef/client.rb <<'EOP'
<%= config_content %>
EOP

cat > /etc/chef/first-boot.json <<'EOP'
{
"run_list": <%= (["role[bootstrap]"] + (hazetug[:bootstrap_list] || [])).to_json %>,
"bootstrap": {
"set_run_list": <%= (hazetug[:run_list] || []).to_json %>,
"set_environment": "<%= hazetug[:chef_environment] %>"
}
}
EOP

# --------------- Update ubuntu and Install packages

export DEBIAN_FRONTEND=noninteractive
apt-get update && apt-get upgrade -yq
apt-get clean
apt-get autoremove -yq

apt-get -yq install curl build-essential


# --------------- Set hostname

HOSTNAME="<%= hazetug[:name] %>"
echo "${HOSTNAME}" > /etc/hostname
ip=`/sbin/ifconfig eth0 | grep "\<inet\>" | cut -d ":" -f2 | cut -d " " -f1`
echo -e "\n# our own entry\n${ip} ${HOSTNAME} ${HOSTNAME%%.*}" >> /etc/hosts
cp /etc/hosts /tmp/hosts.orig
cat /tmp/hosts.orig | sed "s/127.0.0.1.*/127.0.0.1 localhost localhost.localdomain/" > /etc/hosts
rm /tmp/hosts.orig
hostname -F /etc/hostname

# --------------- Set private network on Linode

if [ "<%= hazetug[:compute_name] %>" = "linode" ]; then

cat >> /etc/network/interfaces <<'EOP'

auto eth0:0
iface eth0:0 inet static
address <%= hazetug[:private_ip_address] %>
netmask 255.255.128.0
EOP
ifup eth0:0

fi


# --------------- Install Chef

curl -L https://get.rvm.io | bash -s <%= hazetug[:rvm_version] || 'stable' %>
source /etc/profile.d/rvm.sh

rvm install <%= hazetug[:ruby_version] || "ruby-2.1.2" %> --autolibs=3
rvm use <%= hazetug[:ruby_version] || "ruby-2.1.2" %> --default

cat >/tmp/chef_gemfile<<EOF
source "http://rubygems.org"
gem "chef", "<%= hazetug[:chef_version] || "11.14.6" %>"
EOF

bundle install --gemfile /tmp/chef_gemfile
rm /tmp/chef_gemfile

# --------------- Start Chef (bootstrap run)

echo "<%= start_chef %>"
<%= start_chef %>'
64 changes: 64 additions & 0 deletions examples/bootstrap-solo.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<% require 'json' -%>
bash -c '

mkdir -p /etc/chef

cat > /etc/chef/first-boot.json <<'EOP'
<%= hazetug[:attributes_json] %>
EOP

# --------------- Update ubuntu and Install packages

export DEBIAN_FRONTEND=noninteractive
apt-get update && apt-get upgrade -yq
apt-get clean
apt-get autoremove -yq

apt-get -yq install curl build-essential


# --------------- Set hostname

HOSTNAME="<%= hazetug[:name] %>"
echo "${HOSTNAME}" > /etc/hostname
ip=`/sbin/ifconfig eth0 | grep "\<inet\>" | cut -d ":" -f2 | cut -d " " -f1`
echo -e "\n# our own entry\n${ip} ${HOSTNAME} ${HOSTNAME%%.*}" >> /etc/hosts
cp /etc/hosts /tmp/hosts.orig
cat /tmp/hosts.orig | sed "s/127.0.0.1.*/127.0.0.1 localhost localhost.localdomain/" > /etc/hosts
rm /tmp/hosts.orig
hostname -F /etc/hostname

# --------------- Set private network on Linode

if [ "<%= hazetug[:compute_name] %>" = "linode" ]; then

cat >> /etc/network/interfaces <<'EOP'

auto eth0:0
iface eth0:0 inet static
address <%= hazetug[:private_ip_address] %>
netmask 255.255.128.0
EOP
ifup eth0:0

fi

# --------------- Install Chef

curl -L https://get.rvm.io | bash -s <%= hazetug[:rvm_version] || 'stable' %>
source /etc/profile.d/rvm.sh

rvm install <%= hazetug[:ruby_version] || "ruby-2.1.2" %> --autolibs=3
rvm use <%= hazetug[:ruby_version] || "ruby-2.1.2" %> --default

cat >/tmp/chef_gemfile<<EOF
source "http://rubygems.org"
gem "chef", "<%= hazetug[:chef_version] || "11.14.6" %>"
EOF

bundle install --gemfile /tmp/chef_gemfile
rm /tmp/chef_gemfile /tmp/chef_gemfile.lock

# --------------- Start Chef in solo mode

chef-solo -j /etc/chef/first-boot.json -r <%= hazetug[:cookbooks_file] %>'
17 changes: 9 additions & 8 deletions hazetug.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ Gem::Specification.new do |spec|
spec.name = "hazetug"
spec.version = Hazetug::VERSION
spec.authors = ["Denis Barishev"]
spec.email = ["denz@twiket.com"]
spec.email = ["dennybaa@gmail.com"]
spec.summary = %q{Cloud provisoner tool}
spec.description = %q{Hazetug uses fog cloud library and he}
spec.description = %q{Provisions and bootstraps nodes using knife}
spec.homepage = "https://github.com/dennybaa/hazetug"
spec.license = "MIT"

Expand All @@ -19,10 +19,11 @@ Gem::Specification.new do |spec|
spec.require_paths = ["lib"]

spec.add_development_dependency "bundler", "~> 1.5"
spec.add_development_dependency "rake"
spec.add_dependency "psych"
spec.add_dependency "fog"
spec.add_dependency "chef", ">= 11.10.0"
spec.add_dependency "gli"
spec.add_dependency "agent"
spec.add_development_dependency "rake", "~> 0"
spec.add_dependency "psych", "~> 0"
spec.add_dependency "fog", "~> 0"
spec.add_dependency "chef", "~> 11.0"
spec.add_dependency "gli", "~> 2.0"
spec.add_dependency "agent", "~> 0"
spec.add_dependency "berkshelf", "~> 3.0"
end
71 changes: 38 additions & 33 deletions lib/hazetug/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,40 +29,45 @@ def define_cli
compute_cmd.desc 'Provisions and bootstraps server'
compute_cmd.command :bootstrap do |op|

op.desc 'Bootstraps server using Knife'
op.arg_name 'task.yaml'
op.command :knife do |tug|
tug.arg_name nil

tug.flag [:v, :variables], :must_match => Array,
:desc => 'Set variable or comma-seperated list of variables (var1_1=hello)'

tug.flag [:n, :number], :default_value => 1,
:desc => 'Set number of created nodes, value from yaml is honored'

tug.flag [:c, :concurrency], :default_value => 1,
:desc => 'Set concurrency value, i.e. number of hosts bootstraped simultaneously'

tug.flag [:b, :bootstrap], :default_value => 'bootstrap.erb',
:desc => 'Set path to knife bootstrap.erb file'

tug.action do |gopts, opts, args|

if args.empty?
commands[:help].execute({},{},tug.name_for_help)
exit 0
op.flag [:v, :variables], :must_match => Array,
:desc => 'Set variable or comma-seperated list of variables (var1_1=hello)'

op.flag [:n, :number], :default_value => 1,
:desc => 'Set number of created nodes, value from yaml is honored'

op.flag [:c, :concurrency], :default_value => 1,
:desc => 'Set concurrency value, i.e. number of hosts bootstraped simultaneously'

op.flag [:b, :bootstrap], :default_value => 'bootstrap.erb',
:desc => "Set path to knife bootstrap.erb file"

[:knife, :solo].each do |tug_cmd|
mode = "#{tug_cmd == :knife ? 'client' : 'solo'}"


op.desc "Bootstraps server using Knife in #{mode} mode"
op.arg_name '<task.yaml>'
op.command tug_cmd do |tug|

tug.action do |gopts, opts, args|

if args.empty?
commands[:help].execute({},{},tug.name_for_help)
exit 0
end

act = CLI::Action[:bootstrap].new
act.pass(
tug_name: tug_cmd,
compute_name: compute,
cli: tug,
gopts: gopts,
opts: opts,
args: args
).execute
end

act = CLI::Action[:bootstrap].new
act.pass(
tug_name: :knife,
compute_name: compute,
cli: tug,
gopts: gopts,
opts: opts,
args: args
).execute
end

end

end
Expand All @@ -72,4 +77,4 @@ def define_cli
end

end
end
end
17 changes: 10 additions & 7 deletions lib/hazetug/cli/bootstrap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
require 'hazetug/tug'
require 'hazetug/task'


class Hazetug
class CLI
class Bootstrap < Action
Expand All @@ -26,11 +25,15 @@ def task

def provision_and_bootstrap(haze, tug, channel, waitgroup)
haze.provision
tug.bootstrap({
args: data[:args],
opts: data[:opts],
gopts: data[:gopts]
})
if haze.ready?
tug.load_haze_config(haze.config_for_tug)

tug.bootstrap({
args: data[:args],
opts: data[:opts],
gopts: data[:gopts]
})
end
rescue
# Exeception will be lost, since we run inside goproc,
# ie. as soon as waitgroup is empty all process exit.
Expand Down Expand Up @@ -62,7 +65,7 @@ def bootstrap_list(&block)
newconf[:ssh_password] = haze.config[:ssh_password]
end

tug = Hazetug::Tug[data[:tug_name]].new(newconf, haze)
tug = Hazetug::Tug[data[:tug_name]].new(newconf)
block.call(haze, tug)
end
end
Expand Down
Loading

0 comments on commit f841bf1

Please sign in to comment.