rouster 0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/LICENSE +9 -0
  4. data/README.md +175 -0
  5. data/Rakefile +65 -0
  6. data/Vagrantfile +23 -0
  7. data/examples/bootstrap.rb +113 -0
  8. data/examples/demo.rb +71 -0
  9. data/examples/error.rb +30 -0
  10. data/lib/rouster.rb +737 -0
  11. data/lib/rouster/deltas.rb +481 -0
  12. data/lib/rouster/puppet.rb +398 -0
  13. data/lib/rouster/testing.rb +743 -0
  14. data/lib/rouster/tests.rb +596 -0
  15. data/path_helper.rb +21 -0
  16. data/rouster.gemspec +30 -0
  17. data/test/basic.rb +10 -0
  18. data/test/functional/deltas/test_get_crontab.rb +99 -0
  19. data/test/functional/deltas/test_get_groups.rb +48 -0
  20. data/test/functional/deltas/test_get_packages.rb +71 -0
  21. data/test/functional/deltas/test_get_ports.rb +119 -0
  22. data/test/functional/deltas/test_get_services.rb +43 -0
  23. data/test/functional/deltas/test_get_users.rb +45 -0
  24. data/test/functional/puppet/test_facter.rb +59 -0
  25. data/test/functional/test_caching.rb +124 -0
  26. data/test/functional/test_destroy.rb +51 -0
  27. data/test/functional/test_dirs.rb +88 -0
  28. data/test/functional/test_files.rb +64 -0
  29. data/test/functional/test_get.rb +76 -0
  30. data/test/functional/test_inspect.rb +31 -0
  31. data/test/functional/test_is_dir.rb +118 -0
  32. data/test/functional/test_is_file.rb +119 -0
  33. data/test/functional/test_new.rb +92 -0
  34. data/test/functional/test_put.rb +81 -0
  35. data/test/functional/test_rebuild.rb +49 -0
  36. data/test/functional/test_restart.rb +44 -0
  37. data/test/functional/test_run.rb +77 -0
  38. data/test/functional/test_status.rb +38 -0
  39. data/test/functional/test_suspend.rb +31 -0
  40. data/test/functional/test_up.rb +27 -0
  41. data/test/functional/test_validate_file.rb +30 -0
  42. data/test/puppet/manifests/default.pp +9 -0
  43. data/test/puppet/manifests/hiera.yaml +12 -0
  44. data/test/puppet/manifests/hieradata/common.json +3 -0
  45. data/test/puppet/manifests/hieradata/vagrant.json +3 -0
  46. data/test/puppet/manifests/manifest.pp +78 -0
  47. data/test/puppet/modules/role/manifests/ui.pp +5 -0
  48. data/test/puppet/test_apply.rb +149 -0
  49. data/test/puppet/test_roles.rb +186 -0
  50. data/test/tunnel_vs_scp.rb +41 -0
  51. data/test/unit/puppet/test_get_puppet_star.rb +68 -0
  52. data/test/unit/test_generate_unique_mac.rb +43 -0
  53. data/test/unit/test_new.rb +31 -0
  54. data/test/unit/test_parse_ls_string.rb +334 -0
  55. data/test/unit/test_traverse_up.rb +43 -0
  56. data/test/unit/testing/test_meets_constraint.rb +55 -0
  57. data/test/unit/testing/test_validate_file.rb +112 -0
  58. data/test/unit/testing/test_validate_group.rb +72 -0
  59. data/test/unit/testing/test_validate_package.rb +69 -0
  60. data/test/unit/testing/test_validate_port.rb +98 -0
  61. data/test/unit/testing/test_validate_service.rb +73 -0
  62. data/test/unit/testing/test_validate_user.rb +92 -0
  63. 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,9 @@
1
+ ## common entry-point into Puppet config.
2
+
3
+ if ($::virtual == 'virtualbox') {
4
+ $data_center = 'vagrant'
5
+ } else {
6
+ $data_center = 'amazon'
7
+ }
8
+
9
+ include role::ui
@@ -0,0 +1,12 @@
1
+ ---
2
+ :hierarchy:
3
+ - %{::data_center}
4
+ - common
5
+
6
+ :backends:
7
+ - json
8
+
9
+ :json:
10
+ # @todo replace with absolute path for non-Vagrant use
11
+ # alternatively set a top-level variable in default.pp and do %{::var_name}
12
+ :datadir: 'hieradata'
@@ -0,0 +1,3 @@
1
+ {
2
+ "role::ui::public_hostname": "%{::ipaddress_eth1}"
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "role::ui::public_hostname": "%{::ipaddress_eth1}-vagrant"
3
+ }
@@ -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,5 @@
1
+ class role::ui {
2
+ notify {'role::ui::notify':
3
+ message => "role::ui configured for ${::data_center}",
4
+ }
5
+ }
@@ -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