toft 0.0.3 → 0.0.4
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/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)
|