rouster 0.5

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.
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