rouster 0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/LICENSE +9 -0
- data/README.md +175 -0
- data/Rakefile +65 -0
- data/Vagrantfile +23 -0
- data/examples/bootstrap.rb +113 -0
- data/examples/demo.rb +71 -0
- data/examples/error.rb +30 -0
- data/lib/rouster.rb +737 -0
- data/lib/rouster/deltas.rb +481 -0
- data/lib/rouster/puppet.rb +398 -0
- data/lib/rouster/testing.rb +743 -0
- data/lib/rouster/tests.rb +596 -0
- data/path_helper.rb +21 -0
- data/rouster.gemspec +30 -0
- data/test/basic.rb +10 -0
- data/test/functional/deltas/test_get_crontab.rb +99 -0
- data/test/functional/deltas/test_get_groups.rb +48 -0
- data/test/functional/deltas/test_get_packages.rb +71 -0
- data/test/functional/deltas/test_get_ports.rb +119 -0
- data/test/functional/deltas/test_get_services.rb +43 -0
- data/test/functional/deltas/test_get_users.rb +45 -0
- data/test/functional/puppet/test_facter.rb +59 -0
- data/test/functional/test_caching.rb +124 -0
- data/test/functional/test_destroy.rb +51 -0
- data/test/functional/test_dirs.rb +88 -0
- data/test/functional/test_files.rb +64 -0
- data/test/functional/test_get.rb +76 -0
- data/test/functional/test_inspect.rb +31 -0
- data/test/functional/test_is_dir.rb +118 -0
- data/test/functional/test_is_file.rb +119 -0
- data/test/functional/test_new.rb +92 -0
- data/test/functional/test_put.rb +81 -0
- data/test/functional/test_rebuild.rb +49 -0
- data/test/functional/test_restart.rb +44 -0
- data/test/functional/test_run.rb +77 -0
- data/test/functional/test_status.rb +38 -0
- data/test/functional/test_suspend.rb +31 -0
- data/test/functional/test_up.rb +27 -0
- data/test/functional/test_validate_file.rb +30 -0
- data/test/puppet/manifests/default.pp +9 -0
- data/test/puppet/manifests/hiera.yaml +12 -0
- data/test/puppet/manifests/hieradata/common.json +3 -0
- data/test/puppet/manifests/hieradata/vagrant.json +3 -0
- data/test/puppet/manifests/manifest.pp +78 -0
- data/test/puppet/modules/role/manifests/ui.pp +5 -0
- data/test/puppet/test_apply.rb +149 -0
- data/test/puppet/test_roles.rb +186 -0
- data/test/tunnel_vs_scp.rb +41 -0
- data/test/unit/puppet/test_get_puppet_star.rb +68 -0
- data/test/unit/test_generate_unique_mac.rb +43 -0
- data/test/unit/test_new.rb +31 -0
- data/test/unit/test_parse_ls_string.rb +334 -0
- data/test/unit/test_traverse_up.rb +43 -0
- data/test/unit/testing/test_meets_constraint.rb +55 -0
- data/test/unit/testing/test_validate_file.rb +112 -0
- data/test/unit/testing/test_validate_group.rb +72 -0
- data/test/unit/testing/test_validate_package.rb +69 -0
- data/test/unit/testing/test_validate_port.rb +98 -0
- data/test/unit/testing/test_validate_service.rb +73 -0
- data/test/unit/testing/test_validate_user.rb +92 -0
- metadata +203 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
require sprintf('%s/../../path_helper', File.dirname(File.expand_path(__FILE__)))
|
2
|
+
|
3
|
+
require 'rouster'
|
4
|
+
require 'rouster/puppet'
|
5
|
+
require 'test/unit'
|
6
|
+
|
7
|
+
class TestRestart < Test::Unit::TestCase
|
8
|
+
|
9
|
+
def setup
|
10
|
+
assert_nothing_raised do
|
11
|
+
@app = Rouster.new(:name => 'app', :verbosity => 4)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_happy_path
|
16
|
+
@app.up()
|
17
|
+
|
18
|
+
assert_equal(true, @app.is_available_via_ssh?)
|
19
|
+
sleep 10
|
20
|
+
original_uptime = @app.facter()['uptime_seconds'].to_i
|
21
|
+
|
22
|
+
assert_nothing_raised do
|
23
|
+
@app.restart()
|
24
|
+
end
|
25
|
+
|
26
|
+
count = 0
|
27
|
+
until @app.is_available_via_ssh?()
|
28
|
+
count += 1
|
29
|
+
break if count > 12 # wait up to 2 minutes
|
30
|
+
sleep 10
|
31
|
+
end
|
32
|
+
|
33
|
+
new_uptime = @app.facter(false)['uptime_seconds'].to_i
|
34
|
+
|
35
|
+
assert_not_equal(original_uptime, new_uptime)
|
36
|
+
assert(original_uptime > new_uptime)
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
def teardown
|
41
|
+
# noop
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require sprintf('%s/../../path_helper', File.dirname(File.expand_path(__FILE__)))
|
2
|
+
|
3
|
+
require 'rouster'
|
4
|
+
require 'test/unit'
|
5
|
+
|
6
|
+
class TestRun < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@app = Rouster.new({:name => 'app', :verbose => 4})
|
10
|
+
@app_no_sudo = Rouster.new({:name => 'app', :verbose => 4, :sudo => false})
|
11
|
+
|
12
|
+
@app.up()
|
13
|
+
@app_no_sudo.up()
|
14
|
+
|
15
|
+
assert_equal(@app.is_available_via_ssh?, true, 'app is available via SSH')
|
16
|
+
assert_equal(@app.is_available_via_ssh?, true, 'app_no_sudo is available via SSH')
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_happy_path
|
20
|
+
|
21
|
+
assert_nothing_raised do
|
22
|
+
@app.run('ls -l')
|
23
|
+
end
|
24
|
+
|
25
|
+
assert_equal(0, @app.exitcode, 'got expected exit code')
|
26
|
+
assert_not_nil(@app.get_output(), 'output is populated')
|
27
|
+
assert_match(/^total\s\d/, @app.get_output(), 'output matches expectations')
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_bad_exit_codes
|
31
|
+
|
32
|
+
assert_raise Rouster::RemoteExecutionError do
|
33
|
+
@app.run('fizzbang')
|
34
|
+
end
|
35
|
+
|
36
|
+
assert_not_equal(0, @app.exitcode, 'got expected non-0 exit code')
|
37
|
+
assert_not_nil(@app.get_output(), 'output is populated')
|
38
|
+
assert_match(/fizzbang/, @app.get_output(), 'output matches expectations')
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_bad_custom_exit_codes
|
42
|
+
|
43
|
+
assert_raise Rouster::RemoteExecutionError do
|
44
|
+
@app.run('ls -l', 2)
|
45
|
+
end
|
46
|
+
|
47
|
+
assert_equal(0, @app.exitcode)
|
48
|
+
assert_not_nil(@app.get_output())
|
49
|
+
assert_match(/total/, @app.get_output())
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_sudo_enabled
|
53
|
+
|
54
|
+
assert_nothing_raised do
|
55
|
+
@app.run('ls -l /root')
|
56
|
+
end
|
57
|
+
|
58
|
+
assert_equal(0, @app.exitcode, 'got expected exit code')
|
59
|
+
assert_no_match(/Permission denied/i, @app.get_output(), 'output matches expectations 1of2')
|
60
|
+
assert_match(/^total\s\d/, @app.get_output(), 'output matches expectations 2of2')
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_sudo_disabled
|
64
|
+
|
65
|
+
assert_raise Rouster::RemoteExecutionError do
|
66
|
+
@app_no_sudo.run('ls -l /root')
|
67
|
+
end
|
68
|
+
|
69
|
+
assert_not_equal(0, @app_no_sudo.exitcode, 'got expected non-0 exit code')
|
70
|
+
assert_match(/Permission denied/i, @app_no_sudo.get_output(), 'output matches expectations')
|
71
|
+
end
|
72
|
+
|
73
|
+
def teardown
|
74
|
+
@app.suspend()
|
75
|
+
@app_no_sudo.suspend()
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require sprintf('%s/../../path_helper', File.dirname(File.expand_path(__FILE__)))
|
2
|
+
|
3
|
+
require 'rouster'
|
4
|
+
require 'test/unit'
|
5
|
+
|
6
|
+
class TestStatus < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def setup
|
9
|
+
assert_nothing_raised do
|
10
|
+
@app = Rouster.new(:name => 'app')
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_1_destroyed
|
16
|
+
@app.destroy()
|
17
|
+
|
18
|
+
assert_equal('not created', @app.status())
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_2_upped
|
22
|
+
@app.up()
|
23
|
+
|
24
|
+
assert_equal('running', @app.status())
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_3_suspended
|
28
|
+
@app.up unless @app.status.eql?('running')
|
29
|
+
@app.suspend()
|
30
|
+
|
31
|
+
assert_equal('saved', @app.status())
|
32
|
+
end
|
33
|
+
|
34
|
+
def teardown
|
35
|
+
# noop
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require sprintf('%s/../../path_helper', File.dirname(File.expand_path(__FILE__)))
|
2
|
+
|
3
|
+
require 'rouster'
|
4
|
+
require 'test/unit'
|
5
|
+
|
6
|
+
class TestSuspend < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def setup
|
9
|
+
assert_nothing_raised do
|
10
|
+
@app = Rouster.new(:name => 'app')
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_happy_path
|
16
|
+
@app.up()
|
17
|
+
|
18
|
+
assert_equal(true, @app.is_available_via_ssh?)
|
19
|
+
|
20
|
+
assert_nothing_raised do
|
21
|
+
@app.suspend()
|
22
|
+
end
|
23
|
+
|
24
|
+
assert_equal(false, @app.is_available_via_ssh?)
|
25
|
+
end
|
26
|
+
|
27
|
+
def teardown
|
28
|
+
# noop
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require sprintf('%s/../../path_helper', File.dirname(File.expand_path(__FILE__)))
|
2
|
+
|
3
|
+
require 'rouster'
|
4
|
+
require 'test/unit'
|
5
|
+
|
6
|
+
class TestUp < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def setup
|
9
|
+
assert_nothing_raised do
|
10
|
+
@app = Rouster.new(:name => 'app')
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_happy_path
|
16
|
+
assert_nothing_raised do
|
17
|
+
@app.up()
|
18
|
+
end
|
19
|
+
|
20
|
+
assert_equal(true, @app.is_available_via_ssh?)
|
21
|
+
end
|
22
|
+
|
23
|
+
def teardown
|
24
|
+
# noop
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require sprintf('%s/../../path_helper', File.dirname(File.expand_path(__FILE__)))
|
2
|
+
|
3
|
+
require 'rouster'
|
4
|
+
require 'rouster/puppet'
|
5
|
+
require 'rouster/testing'
|
6
|
+
require 'test/unit'
|
7
|
+
|
8
|
+
class TestValidateFileFunctional < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def setup
|
11
|
+
# expose private methods
|
12
|
+
Rouster.send(:public, *Rouster.private_instance_methods)
|
13
|
+
Rouster.send(:public, *Rouster.protected_instance_methods)
|
14
|
+
|
15
|
+
@app = Rouster.new(:name => 'app')
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_negative_functional_fallthrough
|
19
|
+
|
20
|
+
assert_equal(false, @app.validate_file('/foo', {}, false, true))
|
21
|
+
assert_equal(false, @app.validate_file('/fizzy', { :ensure => 'directory' }, false, true))
|
22
|
+
assert_equal(false, @app.validate_file('/bang', { :ensure => 'file' }, false, true))
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def teardown
|
27
|
+
# noop
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# == manifest.pp
|
2
|
+
|
3
|
+
node default {
|
4
|
+
include baseclass
|
5
|
+
}
|
6
|
+
node 'app.hsd1.ca.comcast.net' {
|
7
|
+
include app_role
|
8
|
+
}
|
9
|
+
|
10
|
+
node 'db' {
|
11
|
+
include db_role
|
12
|
+
}
|
13
|
+
|
14
|
+
notify { 'test':
|
15
|
+
message => 'this is a test',
|
16
|
+
}
|
17
|
+
|
18
|
+
class baseclass {
|
19
|
+
|
20
|
+
file { '/etc/passwd':
|
21
|
+
ensure => file,
|
22
|
+
owner => 'root',
|
23
|
+
group => 'root',
|
24
|
+
mode => '0644',
|
25
|
+
}
|
26
|
+
|
27
|
+
file { '/tmp/foo':
|
28
|
+
ensure => directory,
|
29
|
+
owner => 'root',
|
30
|
+
group => 'root',
|
31
|
+
}
|
32
|
+
|
33
|
+
package { ['puppet', 'facter'] :
|
34
|
+
ensure => installed,
|
35
|
+
}
|
36
|
+
|
37
|
+
}
|
38
|
+
|
39
|
+
class app_role {
|
40
|
+
|
41
|
+
package { 'rsync':
|
42
|
+
ensure => installed,
|
43
|
+
}
|
44
|
+
|
45
|
+
user { 'foo':
|
46
|
+
ensure => present,
|
47
|
+
groups => 'bar',
|
48
|
+
}
|
49
|
+
|
50
|
+
group { 'bar':
|
51
|
+
ensure => present,
|
52
|
+
before => User['foo'],
|
53
|
+
}
|
54
|
+
|
55
|
+
service { 'snmpd':
|
56
|
+
ensure => stopped,
|
57
|
+
}
|
58
|
+
|
59
|
+
}
|
60
|
+
|
61
|
+
class db_role {
|
62
|
+
|
63
|
+
package { 'httpd':
|
64
|
+
ensure => installed,
|
65
|
+
}
|
66
|
+
|
67
|
+
file { '/tmp/fizzy':
|
68
|
+
ensure => file,
|
69
|
+
content => 'this is a test',
|
70
|
+
owner => 'vagrant',
|
71
|
+
group => 'vagrant',
|
72
|
+
mode => '0444',
|
73
|
+
}
|
74
|
+
|
75
|
+
service { 'httpd':
|
76
|
+
ensure => running,
|
77
|
+
}
|
78
|
+
}
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require sprintf('%s/../../path_helper', File.dirname(File.expand_path(__FILE__)))
|
2
|
+
|
3
|
+
require 'rouster'
|
4
|
+
require 'rouster/puppet'
|
5
|
+
require 'rouster/testing'
|
6
|
+
|
7
|
+
require 'test/unit'
|
8
|
+
|
9
|
+
class TestPuppetApply < Test::Unit::TestCase
|
10
|
+
|
11
|
+
def setup
|
12
|
+
@app = Rouster.new(:name => 'app', :verbose => 1)
|
13
|
+
|
14
|
+
## TODO teach put() how to use -R (directories)
|
15
|
+
required_files = {
|
16
|
+
'test/puppet/manifests/default.pp' => 'manifests/default.pp',
|
17
|
+
'test/puppet/manifests/hiera.yaml' => 'manifests/hiera.yaml',
|
18
|
+
'test/puppet/manifests/manifest.pp' => 'manifests/manifest.pp',
|
19
|
+
'test/puppet/manifests/hieradata/common.json' => 'manifests/hieradata/common.json',
|
20
|
+
'test/puppet/manifests/hieradata/vagrant.json' => 'manifests/hieradata/vagrant.json',
|
21
|
+
'test/puppet/modules/role/manifests/ui.pp' => 'modules/role/manifests/ui.pp',
|
22
|
+
}
|
23
|
+
|
24
|
+
## TODO figure out a better pattern here -- scp tunnel is under 'vagrant' context, but dirs created with 'root'
|
25
|
+
@app.sudo = false
|
26
|
+
@app.run('mkdir -p manifests/hieradata')
|
27
|
+
@app.run('mkdir -p modules/role/manifests')
|
28
|
+
@app.sudo = true
|
29
|
+
|
30
|
+
required_files.each_pair do |source,dest|
|
31
|
+
@app.put(source, dest)
|
32
|
+
end
|
33
|
+
|
34
|
+
assert_nothing_raised do
|
35
|
+
@app.run_puppet('masterless', {
|
36
|
+
:expected_exitcode => [0,2],
|
37
|
+
:hiera_config => 'manifests/hiera.yaml',
|
38
|
+
:manifest_file => 'manifests/manifest.pp',
|
39
|
+
:module_dir => 'modules/'
|
40
|
+
})
|
41
|
+
end
|
42
|
+
|
43
|
+
assert_match(/this is a test/, @app.get_output())
|
44
|
+
assert_match(/Finished catalog run in/, @app.get_output())
|
45
|
+
|
46
|
+
# define base here
|
47
|
+
@expected_packages = {
|
48
|
+
'puppet' => { :ensure => true },
|
49
|
+
'facter' => { :ensure => 'present' }
|
50
|
+
}
|
51
|
+
|
52
|
+
@expected_files = {
|
53
|
+
'/etc/passwd' => {
|
54
|
+
:contains => [ 'vagrant', 'root'],
|
55
|
+
:ensure => 'file',
|
56
|
+
:group => 'root',
|
57
|
+
:mode => '0644',
|
58
|
+
:owner => 'root'
|
59
|
+
},
|
60
|
+
|
61
|
+
'/tmp' => {
|
62
|
+
:ensure => 'directory',
|
63
|
+
:group => 'root',
|
64
|
+
:owner => 'root',
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
@expected_groups = {
|
69
|
+
'root' => { :ensure => 'true' }
|
70
|
+
}
|
71
|
+
|
72
|
+
@expected_services = Hash.new()
|
73
|
+
@expected_users = {
|
74
|
+
'root' => {
|
75
|
+
:ensure => 'present',
|
76
|
+
:group => 'root',
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_app
|
83
|
+
app_expected_packages = {
|
84
|
+
'rsync' => { :ensure => 'present' }
|
85
|
+
}.merge(@expected_packages)
|
86
|
+
|
87
|
+
app_expected_files = {
|
88
|
+
'/etc/hosts' => {
|
89
|
+
:contains => [ 'localhost', 'app' ],
|
90
|
+
:ensure => 'present',
|
91
|
+
:group => 'root',
|
92
|
+
:owner => 'root',
|
93
|
+
},
|
94
|
+
}.merge(@expected_files)
|
95
|
+
|
96
|
+
app_expected_groups = {
|
97
|
+
'vagrant' => {
|
98
|
+
:ensure => 'present',
|
99
|
+
}
|
100
|
+
}.merge(@expected_groups)
|
101
|
+
|
102
|
+
app_expected_services = {}.merge(@expected_services)
|
103
|
+
|
104
|
+
app_expected_users = {
|
105
|
+
'vagrant' => {
|
106
|
+
:ensure => 'present',
|
107
|
+
},
|
108
|
+
}.merge(@expected_users)
|
109
|
+
|
110
|
+
assert_nothing_raised do
|
111
|
+
@app.run_puppet('masterless', {
|
112
|
+
:expected_exitcode => [0,2],
|
113
|
+
:hiera_config => 'manifests/hiera.yaml',
|
114
|
+
:manifest_dir => 'manifests/',
|
115
|
+
:module_dir => 'modules/'
|
116
|
+
})
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
assert_match(/Finished catalog run in/, @app.get_output()) # this only examines the last manifest run
|
121
|
+
|
122
|
+
# manually specified testing
|
123
|
+
app_expected_files.each_pair do |f,e|
|
124
|
+
assert_equal(true, @app.validate_file(f,e), "file[#{f}] expectation[#{e}]")
|
125
|
+
end
|
126
|
+
|
127
|
+
app_expected_groups.each_pair do |g,e|
|
128
|
+
assert_equal(true, @app.validate_group(g,e), "group[#{g}] expectation[#{e}]")
|
129
|
+
end
|
130
|
+
|
131
|
+
app_expected_packages.each_pair do |p,e|
|
132
|
+
assert_equal(true, @app.validate_package(p, e), "package[#{p}] expectation[#{e}]")
|
133
|
+
end
|
134
|
+
|
135
|
+
app_expected_services.each_pair do |s,e|
|
136
|
+
assert_equal(true, @app.validate_service(s,e), "service[#{s}] expectation[#{e}]")
|
137
|
+
end
|
138
|
+
|
139
|
+
app_expected_users.each_pair do |u,e|
|
140
|
+
assert_equal(true, @app.validate_user(u,e), "user[#{u}] expectation[#{e}]")
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
def teardown
|
146
|
+
# noop
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|