toft 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +6 -1
- data/Rakefile +93 -4
- data/features/checker.feature +2 -2
- data/features/chef.feature +23 -5
- data/features/command.feature +4 -3
- data/features/node.feature +21 -6
- data/features/step_definitions/chef.rb +11 -3
- data/features/step_definitions/command.rb +1 -3
- data/features/step_definitions/node.rb +19 -3
- data/features/support/env.rb +6 -3
- data/fixtures/chef/attributes.json +9 -0
- data/lib/toft/chef/chef_attributes.rb +2 -2
- data/lib/toft/chef/chef_runner.rb +17 -4
- data/lib/toft/node.rb +64 -20
- data/lib/toft/node_controller.rb +2 -2
- data/lib/toft/version.rb +1 -1
- data/lib/toft.rb +5 -2
- data/scripts/bin/centos/lxc-prepare-host +175 -0
- data/scripts/{ubuntu/bin → bin/share}/install-chef-ubuntu.sh +0 -0
- data/scripts/{ubuntu/bin → bin/share}/install-rvm.sh +0 -0
- data/scripts/{ubuntu/bin → bin/share}/lxc-create-centos-image +6 -5
- data/scripts/{ubuntu/bin → bin/ubuntu}/lxc-create-ubuntu-image +3 -3
- data/scripts/bin/ubuntu/lxc-prepare-host +190 -0
- data/scripts/cookbooks/lxc/recipes/default.rb +8 -14
- data/scripts/{ubuntu/lxc-templates → cookbooks/lxc/templates/default}/lxc-centos-6 +7 -3
- data/scripts/cookbooks/lxc/templates/default/{lxc-lucid-chef → lxc-lucid} +2 -2
- data/scripts/cookbooks/lxc/templates/default/{lxc-natty-chef → lxc-natty} +2 -2
- data/scripts/lxc-templates/files/rc.local +38 -0
- data/scripts/lxc-templates/lxc-centos-6 +279 -0
- data/scripts/{ubuntu/lxc-templates → lxc-templates}/lxc-lucid +3 -12
- data/scripts/{ubuntu/lxc-templates → lxc-templates}/lxc-natty +51 -61
- data/spec/fixtures/illegal_syntax.json +1 -0
- data/spec/spec_helper.rb +3 -1
- data/spec/tuft/chef_attributes_spec.rb +6 -0
- data/spec/tuft/chef_runner_spec.rb +34 -0
- metadata +55 -21
- data/scripts/centos/bin/lxc-prepare-host +0 -39
- data/scripts/cookbooks/lxc/files/default/lxc-create-ubuntu-image +0 -75
- data/scripts/ubuntu/bin/lxc-prepare-host +0 -24
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
toft (0.0.
|
4
|
+
toft (0.0.3)
|
5
5
|
net-ssh
|
6
6
|
|
7
7
|
GEM
|
@@ -18,6 +18,8 @@ GEM
|
|
18
18
|
diff-lcs (1.1.3)
|
19
19
|
erubis (2.7.0)
|
20
20
|
ffi (1.0.9)
|
21
|
+
fpm (0.3.10)
|
22
|
+
json
|
21
23
|
gherkin (2.5.1)
|
22
24
|
json (>= 1.4.6)
|
23
25
|
i18n (0.6.0)
|
@@ -25,6 +27,7 @@ GEM
|
|
25
27
|
net-scp (1.0.4)
|
26
28
|
net-ssh (>= 1.99.1)
|
27
29
|
net-ssh (2.1.4)
|
30
|
+
rake (0.9.2)
|
28
31
|
rspec (2.6.0)
|
29
32
|
rspec-core (~> 2.6.0)
|
30
33
|
rspec-expectations (~> 2.6.0)
|
@@ -52,6 +55,8 @@ PLATFORMS
|
|
52
55
|
|
53
56
|
DEPENDENCIES
|
54
57
|
cucumber
|
58
|
+
fpm
|
59
|
+
rake
|
55
60
|
rspec
|
56
61
|
toft!
|
57
62
|
vagrant (>= 0.8.7)
|
data/Rakefile
CHANGED
@@ -1,14 +1,103 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
|
3
|
+
PROJECT_ROOT = File.dirname(__FILE__)
|
4
|
+
LXC_PACKAGE_NAME = "toft-lxc"
|
5
|
+
|
3
6
|
desc "clean artifacts"
|
4
7
|
task :clean do
|
5
8
|
`rm -rf pkg`
|
9
|
+
`rm -rf tmp`
|
6
10
|
end
|
7
11
|
|
8
12
|
desc "build gem and scripts package"
|
9
|
-
task :package => [:build, :
|
13
|
+
task :package => [:build, :package_deb, :package_rpm]
|
14
|
+
|
15
|
+
task :package_deb do
|
16
|
+
src_dir = "#{PROJECT_ROOT}/scripts"
|
17
|
+
content_dir = "#{PROJECT_ROOT}/pkg/#{LXC_PACKAGE_NAME}"
|
18
|
+
mkdir_p content_dir
|
19
|
+
mkdir_p "#{content_dir}/usr/local/bin"
|
20
|
+
mkdir_p "#{content_dir}/var/cache/lxc"
|
21
|
+
mkdir_p "#{content_dir}/usr/lib/lxc/templates"
|
22
|
+
cp_r Dir.glob("#{src_dir}/bin/share/*"), "#{content_dir}/usr/local/bin"
|
23
|
+
cp_r Dir.glob("#{src_dir}/bin/ubuntu/*"), "#{content_dir}/usr/local/bin"
|
24
|
+
cp_r Dir.glob("#{src_dir}/lxc-templates/*"), "#{content_dir}/usr/lib/lxc/templates"
|
25
|
+
|
26
|
+
post_install_script = <<-eos
|
27
|
+
#!/bin/sh -e
|
28
|
+
/usr/local/bin/lxc-prepare-host
|
29
|
+
eos
|
30
|
+
File.open("#{PROJECT_ROOT}/pkg/toft-lxc-post-install.sh", 'w') { |f| f.write(post_install_script) }
|
31
|
+
|
32
|
+
Dir.chdir("pkg") do
|
33
|
+
system <<-EOF
|
34
|
+
fpm -s dir \
|
35
|
+
-t deb \
|
36
|
+
-C #{content_dir} \
|
37
|
+
-a all \
|
38
|
+
-n #{LXC_PACKAGE_NAME} \
|
39
|
+
-v #{Toft::VERSION} \
|
40
|
+
-m "Huang Liang<exceedhl@gmail.com>" \
|
41
|
+
--description "lxc templates and helper provided by toft" \
|
42
|
+
-d rpm \
|
43
|
+
-d dnsutils \
|
44
|
+
-d lxc \
|
45
|
+
-d bridge-utils \
|
46
|
+
-d debootstrap \
|
47
|
+
-d dhcp3-server \
|
48
|
+
-d bind9 \
|
49
|
+
-d ntp \
|
50
|
+
--replaces lxc \
|
51
|
+
--conflicts apparmor \
|
52
|
+
--conflicts apparmor-utils \
|
53
|
+
--post-install "#{PROJECT_ROOT}/pkg/toft-lxc-post-install.sh" \
|
54
|
+
.
|
55
|
+
EOF
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
task :package_rpm do
|
60
|
+
src_dir = "#{PROJECT_ROOT}/scripts"
|
61
|
+
content_dir = "#{PROJECT_ROOT}/pkg/#{LXC_PACKAGE_NAME}"
|
62
|
+
mkdir_p content_dir
|
63
|
+
mkdir_p "#{content_dir}/usr/local/bin"
|
64
|
+
mkdir_p "#{content_dir}/usr/var/lib/lxc"
|
65
|
+
mkdir_p "#{content_dir}/var/cache/lxc"
|
66
|
+
mkdir_p "#{content_dir}/usr/lib/lxc/templates"
|
67
|
+
cp_r Dir.glob("#{src_dir}/bin/share/*"), "#{content_dir}/usr/local/bin"
|
68
|
+
cp_r Dir.glob("#{src_dir}/bin/centos/*"), "#{content_dir}/usr/local/bin"
|
69
|
+
cp_r Dir.glob("#{src_dir}/lxc-templates/*"), "#{content_dir}/usr/lib/lxc/templates"
|
70
|
+
|
71
|
+
post_install_script = <<-eos
|
72
|
+
#!/bin/sh -e
|
73
|
+
/usr/local/bin/lxc-prepare-host
|
74
|
+
eos
|
75
|
+
File.open("#{PROJECT_ROOT}/pkg/toft-lxc-post-install.sh", 'w') { |f| f.write(post_install_script) }
|
76
|
+
|
77
|
+
Dir.chdir("pkg") do
|
78
|
+
system <<-EOF
|
79
|
+
fpm -s dir \
|
80
|
+
-t rpm \
|
81
|
+
-C #{content_dir} \
|
82
|
+
-a all \
|
83
|
+
-n #{LXC_PACKAGE_NAME} \
|
84
|
+
-v #{Toft::VERSION} \
|
85
|
+
-m "Huang Liang<exceedhl@gmail.com>" \
|
86
|
+
--description "lxc templates and helper provided by toft" \
|
87
|
+
-d bind-utils \
|
88
|
+
-d bridge-utils \
|
89
|
+
-d dhcp \
|
90
|
+
-d bind \
|
91
|
+
-d ntp \
|
92
|
+
-d libcap-devel \
|
93
|
+
--post-install "#{PROJECT_ROOT}/pkg/toft-lxc-post-install.sh" \
|
94
|
+
.
|
95
|
+
EOF
|
96
|
+
end
|
97
|
+
end
|
10
98
|
|
11
|
-
|
12
|
-
|
13
|
-
`
|
99
|
+
desc "run all tests and features"
|
100
|
+
task :test do
|
101
|
+
`rspec spec`
|
102
|
+
`sudo cucumber features`
|
14
103
|
end
|
data/features/checker.feature
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
Feature: Checkers provided by Toft to help you verify system state
|
2
2
|
|
3
3
|
Scenario: Dir checker
|
4
|
-
Given I have a clean running node
|
4
|
+
Given I have a clean running node n1
|
5
5
|
Then Node "n1" should have "directory" "/tmp" owned by user "root" and group "root" with permission "1777"
|
6
6
|
Then Node "n1" should have not file or directory "/non-exist-dir"
|
7
7
|
Then Node "n1" should have file or directory "tmp"
|
8
8
|
|
9
9
|
Scenario: File checker
|
10
|
-
Given I have a clean running node
|
10
|
+
Given I have a clean running node n1
|
11
11
|
When Running ssh command "if getent passwd n1; then userdel -fr n1; fi; useradd -m n1" on "n1" should succeed
|
12
12
|
And Running ssh command "sudo -u n1 touch /tmp/a" on "n1" should succeed
|
13
13
|
Then Node "n1" should have file or directory "/tmp/a"
|
data/features/chef.feature
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
Feature: Chef support
|
2
2
|
|
3
3
|
Scenario: Run chef recipe on nodes
|
4
|
-
Given I have a clean running node
|
4
|
+
Given I have a clean running node n1
|
5
5
|
When I run "recipe[test]" on node "n1"
|
6
6
|
Then Node "n1" should have file or directory "/tmp/stub/dir"
|
7
7
|
|
8
8
|
Scenario: Run chef recipe with attributes
|
9
|
-
Given I have a clean running node
|
9
|
+
Given I have a clean running node n1
|
10
10
|
When I run "recipe[test::attribute]" on node "n1" and overwrite attributes with:
|
11
11
|
|key|value|
|
12
12
|
|one|one|
|
@@ -19,7 +19,7 @@ Scenario: Run chef recipe with attributes
|
|
19
19
|
Then Node "n1" should have file or directory "/tmp/stub/three"
|
20
20
|
|
21
21
|
Scenario: Run multiple chef recipes
|
22
|
-
Given I have a clean running node
|
22
|
+
Given I have a clean running node n1
|
23
23
|
When I run recipes on node "n1":
|
24
24
|
|recipe|
|
25
25
|
|recipe[test::role]|
|
@@ -28,16 +28,34 @@ Scenario: Run multiple chef recipes
|
|
28
28
|
Then Node "n1" should have file or directory "/tmp/stub/role"
|
29
29
|
|
30
30
|
Scenario: Run chef role
|
31
|
-
Given I have a clean running node
|
31
|
+
Given I have a clean running node n1
|
32
32
|
When I run "role[test]" on node "n1"
|
33
33
|
Then Node "n1" should have file or directory "/tmp/stub/role"
|
34
34
|
|
35
35
|
Scenario: Toft should not deal with empty cookbook and role path
|
36
|
-
Given I have a clean running node
|
36
|
+
Given I have a clean running node n1
|
37
37
|
When I set the role path to empty
|
38
38
|
Then Running chef "recipe[test]" on node "n1" should succeed
|
39
39
|
When I set the cookbook path to empty
|
40
40
|
Then Running chef "recipe[test]" on node "n1" should fail
|
41
|
+
|
42
|
+
Scenario: Run chef recipe with json attributes file
|
43
|
+
Given I have a clean running node n1
|
44
|
+
When I run "recipe[test::attribute]" on node "n1" and overwrite attributes with json file "attributes.json"
|
45
|
+
Then Node "n1" should have file or directory "/tmp/stub/one"
|
46
|
+
Then Node "n1" should have file or directory "/tmp/stub/two_one"
|
47
|
+
Then Node "n1" should have file or directory "/tmp/stub/two_two"
|
48
|
+
Then Node "n1" should have file or directory "/tmp/stub/three"
|
49
|
+
|
50
|
+
Scenario: Attributes table should override attributes in json file
|
51
|
+
Given I have a clean running node n1
|
52
|
+
When I run "recipe[test::attribute]" on node "n1" and overwrite attributes with json file "attributes.json" and chef attributes:
|
53
|
+
|key|value|
|
54
|
+
|one|override|
|
55
|
+
Then Node "n1" should have file or directory "/tmp/stub/override"
|
56
|
+
Then Node "n1" should have file or directory "/tmp/stub/two_one"
|
57
|
+
Then Node "n1" should have file or directory "/tmp/stub/two_two"
|
58
|
+
Then Node "n1" should have file or directory "/tmp/stub/three"
|
41
59
|
|
42
60
|
Scenario: Run non-exist recipe
|
43
61
|
|
data/features/command.feature
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
Feature: Run ssh command on node
|
2
2
|
|
3
3
|
Scenario: Run command on node successfully
|
4
|
-
Given I have a clean running node
|
4
|
+
Given I have a clean running node n1
|
5
5
|
Then Running ssh command "" on "n1" should fail
|
6
6
|
And Running ssh command "ps" on "n1" should succeed
|
7
7
|
And Running ssh command "non-exist-command" on "n1" should fail
|
8
8
|
And Running ssh command "netstat -nr" on "n1" should succeed
|
9
9
|
|
10
10
|
Scenario: Test rm
|
11
|
-
Given I have a clean running node
|
11
|
+
Given I have a clean running node n1
|
12
12
|
Then Rm "" on "n1" should fail
|
13
13
|
And Rm "tmp/*" on "n1" should fail
|
14
14
|
And Rm "/tmp/*" on "n1" should succeed
|
15
15
|
|
16
16
|
Scenario: Check ssh command result
|
17
|
-
Given I have a clean running node
|
17
|
+
Given I have a clean running node n1
|
18
18
|
Then the result of running ssh command "ps" on "n1" should contain "sshd"
|
19
|
+
Then the result of running ssh command "chef-solo" on "n1" should contain "No cookbook found"
|
19
20
|
|
data/features/node.feature
CHANGED
@@ -1,21 +1,36 @@
|
|
1
1
|
Feature: Node management
|
2
2
|
|
3
3
|
Scenario: Start or stop node
|
4
|
-
Given I have a clean running node
|
4
|
+
Given I have a clean running node n1
|
5
5
|
Then the node "n1" should be running
|
6
6
|
When I stop node "n1"
|
7
7
|
Then the node "n1" should be stopped
|
8
8
|
When I start node "n1"
|
9
9
|
Then the node "n1" should be running
|
10
|
+
|
11
|
+
Scenario: Add and remove cnames for a node
|
12
|
+
Given I have a clean running node n1
|
13
|
+
And I add another node "n2"
|
14
|
+
When I add cname "cn1" to "n1"
|
15
|
+
Then Running ssh command "ping -c 1 cn1" on "n2" should succeed
|
16
|
+
When I remove cname "cn1" from "n1"
|
17
|
+
Then Running ssh command "ping -c 1 cn1" on "n2" should fail
|
18
|
+
And Node "n2" is destroyed
|
10
19
|
|
20
|
+
Scenario: Create node only by name and fetch their info
|
21
|
+
Given I have a clean running node n1
|
22
|
+
When I add another node "n3"
|
23
|
+
Then Running ssh command "ping -c 1 n1" on "n3" should succeed
|
24
|
+
And Running ssh command "ping -c 1 n3" on "n1" should succeed
|
25
|
+
And Node "n1" should have ip address same with that obtained from inside it through ssh
|
26
|
+
And Node "n3" should have ip address same with that obtained from inside it through ssh
|
27
|
+
And Node "n3" is destroyed
|
28
|
+
|
11
29
|
Scenario: Create or destroy node
|
12
|
-
Given I have a clean running node
|
30
|
+
Given I have a clean running node n1
|
13
31
|
Then There should be 1 nodes in the environment
|
14
32
|
When I add another node "n2" with ip "192.168.20.3"
|
15
33
|
Then There should be 2 nodes in the environment
|
16
34
|
When I destroy node "n2"
|
17
|
-
Then There should be 1 nodes in the environment
|
18
|
-
When Node "n1" is destroyed
|
19
|
-
Then There should be 0 nodes in the environment
|
35
|
+
Then There should be 1 nodes in the environment
|
20
36
|
|
21
|
-
|
@@ -7,11 +7,11 @@ When /^I run recipes on node "([^"]*)":$/ do |node, recipes_table|
|
|
7
7
|
recipes_table.hashes.each do |row|
|
8
8
|
recipes << row[:recipe]
|
9
9
|
end
|
10
|
-
find(node).run_chef recipes
|
10
|
+
find(node).run_chef recipes
|
11
11
|
end
|
12
12
|
|
13
13
|
When /^I run "([^"]*)" on node "([^"]*)" and overwrite attributes with:$/ do |run_list, node, table|
|
14
|
-
find(node).run_chef run_list, Toft::ChefAttributes.new(table)
|
14
|
+
find(node).run_chef run_list, {:attributes => Toft::ChefAttributes.new(table)}
|
15
15
|
end
|
16
16
|
|
17
17
|
When /^I set the role path to empty$/ do
|
@@ -32,4 +32,12 @@ end
|
|
32
32
|
|
33
33
|
Then /^Running chef "([^"]*)" on node "([^"]*)" should fail$/ do |run_list, node|
|
34
34
|
lambda { find(node).run_chef(run_list) }.should raise_error
|
35
|
-
end
|
35
|
+
end
|
36
|
+
|
37
|
+
When /^I run "([^"]*)" on node "([^"]*)" and overwrite attributes with json file "([^"]*)"$/ do |run_list, node, json_file|
|
38
|
+
find(node).run_chef run_list, :json => CHEF_FIXTURE_PATH + '/attributes.json'
|
39
|
+
end
|
40
|
+
|
41
|
+
When /^I run "([^"]*)" on node "([^"]*)" and overwrite attributes with json file "([^"]*)" and chef attributes:$/ do |run_list, node, json_file, table|
|
42
|
+
find(node).run_chef run_list, {:json => CHEF_FIXTURE_PATH + '/attributes.json', :attributes => Toft::ChefAttributes.new(table)}
|
43
|
+
end
|
@@ -15,7 +15,5 @@ Then /^Rm "([^"]*)" on "([^"]*)" should succeed$/ do |dir, node|
|
|
15
15
|
end
|
16
16
|
|
17
17
|
Then /^the result of running ssh command "([^"]*)" on "([^"]*)" should contain "([^"]*)"$/ do |cmd, node, s|
|
18
|
-
find(node).
|
19
|
-
output.should include(s)
|
20
|
-
end
|
18
|
+
find(node).get_ssh_result(cmd).should include(s)
|
21
19
|
end
|
@@ -1,11 +1,15 @@
|
|
1
|
-
Given /^I have a clean running node
|
2
|
-
create_node node, ip, "centos-6"
|
1
|
+
Given /^I have a clean running node n1$/ do
|
3
2
|
@n1.start
|
4
3
|
@n1.rm "/tmp/stub"
|
5
4
|
end
|
6
5
|
|
7
6
|
When /^I add another node "([^"]*)" with ip "([^"]*)"$/ do |node, ip|
|
8
|
-
create_node node, ip,
|
7
|
+
create_node node, {:ip => ip, :type => CONTAINER_TYPE}
|
8
|
+
end
|
9
|
+
|
10
|
+
When /^I add another node "([^"]*)"$/ do |node|
|
11
|
+
n = create_node node, {:type => CONTAINER_TYPE}
|
12
|
+
n.start
|
9
13
|
end
|
10
14
|
|
11
15
|
When /^I destroy node "([^"]*)"$/ do |node|
|
@@ -36,3 +40,15 @@ Then /^the node "([^"]*)" should be running$/ do |node|
|
|
36
40
|
find(node).should be_running
|
37
41
|
end
|
38
42
|
|
43
|
+
Then /^Node "([^"]*)" should have ip address same with that obtained from inside it through ssh$/ do |node|
|
44
|
+
n = find(node)
|
45
|
+
n.get_ssh_result("ifconfig eth0 | grep 'inet addr:'").should include(n.ip)
|
46
|
+
end
|
47
|
+
|
48
|
+
When /^I add cname "([^"]*)" to "([^"]*)"$/ do |cname, node|
|
49
|
+
find(node).add_cname cname
|
50
|
+
end
|
51
|
+
|
52
|
+
When /^I remove cname "([^"]*)" from "([^"]*)"$/ do |cname, node|
|
53
|
+
find(node).remove_cname cname
|
54
|
+
end
|
data/features/support/env.rb
CHANGED
@@ -2,15 +2,18 @@ $:.unshift(File.dirname(__FILE__) + '/../../lib')
|
|
2
2
|
require 'rspec/expectations'
|
3
3
|
require 'toft'
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
CHEF_FIXTURE_PATH = File.dirname(__FILE__) + '/../../fixtures/chef'
|
6
|
+
CONTAINER_TYPE = "centos-6"
|
7
7
|
|
8
8
|
World(Toft)
|
9
9
|
|
10
10
|
include Toft
|
11
|
-
n1 = create_node "n1", "192.168.20.2",
|
11
|
+
n1 = create_node "n1", {:ip => "192.168.20.2", :type => CONTAINER_TYPE}
|
12
12
|
|
13
13
|
Before do
|
14
|
+
Toft.cookbook_path = CHEF_FIXTURE_PATH + '/cookbooks'
|
15
|
+
Toft.role_path = CHEF_FIXTURE_PATH + '/roles'
|
16
|
+
|
14
17
|
@n1 = n1
|
15
18
|
end
|
16
19
|
|
@@ -4,11 +4,11 @@ module Toft
|
|
4
4
|
class ChefAttributes
|
5
5
|
attr_reader :attributes
|
6
6
|
|
7
|
-
def initialize(cuke_ast_table)
|
7
|
+
def initialize(cuke_ast_table = nil)
|
8
8
|
@attributes = {}
|
9
9
|
cuke_ast_table.hashes.each do |row|
|
10
10
|
add_attribute row[:key], row[:value]
|
11
|
-
end
|
11
|
+
end unless cuke_ast_table.nil?
|
12
12
|
end
|
13
13
|
|
14
14
|
def add_attribute(key, value)
|
@@ -17,15 +17,28 @@ module Toft
|
|
17
17
|
@command_runner = command_runner
|
18
18
|
end
|
19
19
|
|
20
|
-
def run(run_list,
|
20
|
+
def run(run_list, params = {})
|
21
|
+
override_attributes_hash = parse_attributes params[:json], params[:attributes]
|
21
22
|
copy_chef_material
|
22
23
|
generate_solo_rb
|
23
|
-
generate_json ([] << run_list).flatten,
|
24
|
+
generate_json ([] << run_list).flatten, override_attributes_hash
|
24
25
|
@command_runner.call "chef-solo -c #{DEST_CHEF_SOLO_PATH} -j #{DEST_CHEF_JSON_PATH}"
|
25
26
|
end
|
26
27
|
|
27
28
|
private
|
29
|
+
def parse_attributes(json_file_path, chef_attributes)
|
30
|
+
chef_attributes = chef_attributes || ChefAttributes.new
|
31
|
+
chef_attributes_from_json ||= {}
|
32
|
+
chef_attributes_from_json = read_json_file json_file_path unless json_file_path.blank?
|
33
|
+
chef_attributes_from_json.merge chef_attributes.attributes
|
34
|
+
end
|
35
|
+
|
36
|
+
def read_json_file(json_file_path)
|
37
|
+
JSON.parse(File.read(json_file_path))
|
38
|
+
end
|
39
|
+
|
28
40
|
def copy_chef_material
|
41
|
+
raise ArgumentError, "Toft.cookbook_path can not be empty!" if Toft.cookbook_path.blank?
|
29
42
|
rm_rf "#{@root_dir}#{DEST_CHEF_TMP}"
|
30
43
|
mkdir_p "#{@root_dir}#{DEST_CHEF_TMP}"
|
31
44
|
cp_r Toft.cookbook_path, "#{@root_dir}#{DEST_COOKBOOK_PATH}"
|
@@ -48,9 +61,9 @@ cookbook_path ["#{DEST_COOKBOOK_PATH}"]
|
|
48
61
|
end
|
49
62
|
end
|
50
63
|
|
51
|
-
def generate_json(run_list,
|
64
|
+
def generate_json(run_list, override_attributes_hash)
|
52
65
|
run_list = {"run_list" => run_list}
|
53
|
-
run_list.merge!(
|
66
|
+
run_list.merge!(override_attributes_hash)
|
54
67
|
File.open("#{@root_dir}#{DEST_CHEF_JSON_PATH}", 'w') do |f|
|
55
68
|
f.write(run_list.to_json);
|
56
69
|
end
|
data/lib/toft/node.rb
CHANGED
@@ -35,14 +35,19 @@ CQWv13UgQjiHgQILXSb7xdzpWK1wpDoqIEWQugRyPQDeZhPWVbB4Lg==
|
|
35
35
|
-----END RSA PRIVATE KEY-----
|
36
36
|
EOF
|
37
37
|
|
38
|
+
TIMOUT_IN_SECONDS = 60
|
39
|
+
TRY_INTERVAL = 0.5
|
40
|
+
|
38
41
|
include Observable
|
39
42
|
|
40
|
-
def initialize(hostname,
|
43
|
+
def initialize(hostname, options)
|
44
|
+
options = {:ip => DYNAMIC_IP, :netmask => "24", :type => "natty"}.merge(options)
|
41
45
|
@hostname = hostname
|
42
|
-
@ip = ip
|
46
|
+
@ip = options[:ip]
|
47
|
+
@netmask = options[:netmask]
|
43
48
|
unless exists?
|
44
49
|
conf_file = generate_lxc_config
|
45
|
-
system "lxc-create -n #{hostname} -f #{conf_file} -t #{type.to_s}"
|
50
|
+
system "lxc-create -n #{hostname} -f #{conf_file} -t #{options[:type].to_s}"
|
46
51
|
end
|
47
52
|
@chef_runner = Toft::Chef::ChefRunner.new("#{rootfs}") do |chef_command|
|
48
53
|
run_ssh chef_command
|
@@ -74,62 +79,97 @@ CQWv13UgQjiHgQILXSb7xdzpWK1wpDoqIEWQugRyPQDeZhPWVbB4Lg==
|
|
74
79
|
def running?
|
75
80
|
`lxc-info -n #{@hostname}` =~ /RUNNING/
|
76
81
|
end
|
82
|
+
|
83
|
+
def add_cname(cname)
|
84
|
+
run_ssh "echo -e 'update add #{cname}.#{Toft::DOMAIN} 86400 CNAME #{@hostname}.#{Toft::DOMAIN}\\nsend' | nsupdate"
|
85
|
+
end
|
86
|
+
|
87
|
+
def remove_cname(cname)
|
88
|
+
run_ssh "echo -e 'update delete #{cname}.#{Toft::DOMAIN}\\nsend' | nsupdate"
|
89
|
+
end
|
77
90
|
|
78
91
|
def run_ssh(command)
|
79
92
|
raise ArgumentError, "Trying to run empty command on node #{@hostname}", caller if command.blank?
|
80
|
-
output = ""
|
81
93
|
error = false
|
82
|
-
Net::SSH.start(
|
94
|
+
Net::SSH.start(fqdn, "root", :key_data => [PRIVATE_KEY]) do |ssh|
|
83
95
|
ssh.exec! command do |ch, stream, data|
|
84
96
|
if stream == :stderr
|
85
97
|
error = true
|
86
98
|
end
|
87
|
-
|
99
|
+
yield data if block_given?
|
100
|
+
puts data
|
88
101
|
end
|
89
102
|
end
|
90
|
-
raise RuntimeError,
|
91
|
-
puts output
|
92
|
-
return yield output if block_given?
|
103
|
+
raise RuntimeError, "Error happened while executing command [#{command}]!", caller if error
|
93
104
|
return true
|
94
105
|
end
|
106
|
+
|
107
|
+
def get_ssh_result(cmd)
|
108
|
+
output = ""
|
109
|
+
run_ssh(cmd) do |data|
|
110
|
+
output += data
|
111
|
+
end
|
112
|
+
return output
|
113
|
+
end
|
95
114
|
|
96
115
|
def rm(dir)
|
97
116
|
raise ArgumentError, "Illegal dir path: [#{dir}]", caller if dir.blank? || dir[0] != ?/
|
98
117
|
system "rm -rf #{rootfs}#{dir}"
|
99
118
|
end
|
100
119
|
|
101
|
-
def run_chef(run_list,
|
102
|
-
@chef_runner.run run_list,
|
120
|
+
def run_chef(run_list, params = {})
|
121
|
+
@chef_runner.run run_list, params
|
103
122
|
end
|
104
123
|
|
105
124
|
def file(path)
|
106
125
|
FileChecker.new(rootfs, path)
|
107
126
|
end
|
127
|
+
|
128
|
+
def ip
|
129
|
+
@ip == Toft::DYNAMIC_IP ? `dig +short #{fqdn}`.strip : @ip
|
130
|
+
end
|
108
131
|
|
109
132
|
private
|
110
133
|
def rootfs
|
111
134
|
"/var/lib/lxc/#{@hostname}/rootfs"
|
112
135
|
end
|
113
|
-
|
114
|
-
def
|
136
|
+
|
137
|
+
def wait_sshd_running
|
115
138
|
while true
|
116
139
|
netstat = `lxc-netstat --name #{@hostname} -ta`
|
117
|
-
|
140
|
+
return if netstat =~ /ssh/
|
118
141
|
end
|
119
|
-
|
120
|
-
|
121
|
-
|
142
|
+
end
|
143
|
+
|
144
|
+
def wait_remote_host_reachable
|
145
|
+
max_try = TIMOUT_IN_SECONDS / TRY_INTERVAL
|
146
|
+
try = 0
|
147
|
+
while try < max_try
|
148
|
+
begin
|
149
|
+
break if Ping.pingecho fqdn, 0.1
|
150
|
+
rescue Exception
|
151
|
+
# fix the strange pingcho exception
|
152
|
+
end
|
153
|
+
sleep TRY_INTERVAL
|
154
|
+
try += 1
|
122
155
|
end
|
123
|
-
|
156
|
+
raise RuntimeError, "Remote machine not responding." if try >= max_try
|
157
|
+
end
|
158
|
+
|
159
|
+
def wait_ssh_ready
|
160
|
+
wait_sshd_running
|
161
|
+
wait_remote_host_reachable
|
162
|
+
puts "SSH connection on '#{@hostname}/#{@ip}' is ready..."
|
124
163
|
end
|
125
164
|
|
126
165
|
def generate_lxc_config
|
166
|
+
full_ip = @ip == Toft::DYNAMIC_IP ? "#{@ip}" : "#{@ip}/#{@netmask}"
|
127
167
|
conf = <<-EOF
|
128
168
|
lxc.network.type = veth
|
129
169
|
lxc.network.flags = up
|
130
170
|
lxc.network.link = br0
|
131
171
|
lxc.network.name = eth0
|
132
|
-
lxc.network.ipv4 = #{
|
172
|
+
lxc.network.ipv4 = #{full_ip}
|
133
173
|
EOF
|
134
174
|
conf_file = "/tmp/#{@hostname}-conf"
|
135
175
|
File.open(conf_file, 'w') do |f|
|
@@ -137,5 +177,9 @@ lxc.network.ipv4 = #{@ip}/24
|
|
137
177
|
end
|
138
178
|
return conf_file
|
139
179
|
end
|
180
|
+
|
181
|
+
def fqdn
|
182
|
+
"#{@hostname}.#{Toft::DOMAIN}"
|
183
|
+
end
|
140
184
|
end
|
141
|
-
end
|
185
|
+
end
|
data/lib/toft/node_controller.rb
CHANGED
data/lib/toft/version.rb
CHANGED
data/lib/toft.rb
CHANGED
@@ -4,12 +4,15 @@ require 'toft/chef/chef_attributes'
|
|
4
4
|
require 'toft/chef/chef_runner'
|
5
5
|
|
6
6
|
module Toft
|
7
|
+
DYNAMIC_IP = "0.0.0.0"
|
8
|
+
DOMAIN = "foo"
|
9
|
+
|
7
10
|
class << self
|
8
11
|
attr_accessor :cookbook_path, :role_path
|
9
12
|
end
|
10
13
|
|
11
|
-
def create_node(hostname,
|
12
|
-
NodeController.instance.create_node(hostname,
|
14
|
+
def create_node(hostname, options = {})
|
15
|
+
NodeController.instance.create_node(hostname, options)
|
13
16
|
end
|
14
17
|
|
15
18
|
def find(hostname)
|