beaker 0.0.0

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 (88) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.simplecov +14 -0
  5. data/DOCUMENTING.md +167 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +17 -0
  8. data/README.md +332 -0
  9. data/Rakefile +121 -0
  10. data/beaker.gemspec +42 -0
  11. data/beaker.rb +10 -0
  12. data/bin/beaker +9 -0
  13. data/lib/beaker.rb +36 -0
  14. data/lib/beaker/answers.rb +29 -0
  15. data/lib/beaker/answers/version28.rb +104 -0
  16. data/lib/beaker/answers/version30.rb +194 -0
  17. data/lib/beaker/cli.rb +113 -0
  18. data/lib/beaker/command.rb +241 -0
  19. data/lib/beaker/command_factory.rb +21 -0
  20. data/lib/beaker/dsl.rb +85 -0
  21. data/lib/beaker/dsl/assertions.rb +87 -0
  22. data/lib/beaker/dsl/helpers.rb +625 -0
  23. data/lib/beaker/dsl/install_utils.rb +299 -0
  24. data/lib/beaker/dsl/outcomes.rb +99 -0
  25. data/lib/beaker/dsl/roles.rb +97 -0
  26. data/lib/beaker/dsl/structure.rb +63 -0
  27. data/lib/beaker/dsl/wrappers.rb +100 -0
  28. data/lib/beaker/host.rb +193 -0
  29. data/lib/beaker/host/aix.rb +15 -0
  30. data/lib/beaker/host/aix/file.rb +16 -0
  31. data/lib/beaker/host/aix/group.rb +35 -0
  32. data/lib/beaker/host/aix/user.rb +32 -0
  33. data/lib/beaker/host/unix.rb +54 -0
  34. data/lib/beaker/host/unix/exec.rb +15 -0
  35. data/lib/beaker/host/unix/file.rb +16 -0
  36. data/lib/beaker/host/unix/group.rb +40 -0
  37. data/lib/beaker/host/unix/pkg.rb +22 -0
  38. data/lib/beaker/host/unix/user.rb +32 -0
  39. data/lib/beaker/host/windows.rb +44 -0
  40. data/lib/beaker/host/windows/exec.rb +18 -0
  41. data/lib/beaker/host/windows/file.rb +15 -0
  42. data/lib/beaker/host/windows/group.rb +36 -0
  43. data/lib/beaker/host/windows/pkg.rb +26 -0
  44. data/lib/beaker/host/windows/user.rb +32 -0
  45. data/lib/beaker/hypervisor.rb +37 -0
  46. data/lib/beaker/hypervisor/aixer.rb +52 -0
  47. data/lib/beaker/hypervisor/blimper.rb +123 -0
  48. data/lib/beaker/hypervisor/fusion.rb +56 -0
  49. data/lib/beaker/hypervisor/solaris.rb +65 -0
  50. data/lib/beaker/hypervisor/vagrant.rb +118 -0
  51. data/lib/beaker/hypervisor/vcloud.rb +175 -0
  52. data/lib/beaker/hypervisor/vsphere.rb +80 -0
  53. data/lib/beaker/hypervisor/vsphere_helper.rb +200 -0
  54. data/lib/beaker/logger.rb +167 -0
  55. data/lib/beaker/network_manager.rb +73 -0
  56. data/lib/beaker/options_parsing.rb +323 -0
  57. data/lib/beaker/result.rb +55 -0
  58. data/lib/beaker/shared.rb +15 -0
  59. data/lib/beaker/shared/error_handler.rb +17 -0
  60. data/lib/beaker/shared/host_handler.rb +46 -0
  61. data/lib/beaker/shared/repetition.rb +28 -0
  62. data/lib/beaker/ssh_connection.rb +198 -0
  63. data/lib/beaker/test_case.rb +225 -0
  64. data/lib/beaker/test_config.rb +148 -0
  65. data/lib/beaker/test_suite.rb +288 -0
  66. data/lib/beaker/utils.rb +7 -0
  67. data/lib/beaker/utils/ntp_control.rb +42 -0
  68. data/lib/beaker/utils/repo_control.rb +92 -0
  69. data/lib/beaker/utils/setup_helper.rb +77 -0
  70. data/lib/beaker/utils/validator.rb +27 -0
  71. data/spec/beaker/command_spec.rb +94 -0
  72. data/spec/beaker/dsl/assertions_spec.rb +104 -0
  73. data/spec/beaker/dsl/helpers_spec.rb +230 -0
  74. data/spec/beaker/dsl/install_utils_spec.rb +70 -0
  75. data/spec/beaker/dsl/outcomes_spec.rb +43 -0
  76. data/spec/beaker/dsl/roles_spec.rb +86 -0
  77. data/spec/beaker/dsl/structure_spec.rb +60 -0
  78. data/spec/beaker/dsl/wrappers_spec.rb +52 -0
  79. data/spec/beaker/host_spec.rb +95 -0
  80. data/spec/beaker/logger_spec.rb +117 -0
  81. data/spec/beaker/options_parsing_spec.rb +37 -0
  82. data/spec/beaker/puppet_command_spec.rb +128 -0
  83. data/spec/beaker/ssh_connection_spec.rb +39 -0
  84. data/spec/beaker/test_case_spec.rb +6 -0
  85. data/spec/beaker/test_suite_spec.rb +44 -0
  86. data/spec/mocks_and_helpers.rb +34 -0
  87. data/spec/spec_helper.rb +15 -0
  88. metadata +359 -0
@@ -0,0 +1,63 @@
1
+ module Beaker
2
+ module DSL
3
+ # These are simple structural elements necessary for writing
4
+ # understandable tests and ensuring cleanup actions happen. If using a
5
+ # third party test runner they are unnecessary.
6
+ #
7
+ # To include this in your own test runner a method #logger should be
8
+ # available to yield a logger that implements
9
+ # {Beaker::Logger}'s interface. As well as a method
10
+ # #teardown_procs that yields an array.
11
+ #
12
+ # @example Structuring a test case.
13
+ # test_name 'Look at me testing things!' do
14
+ # teardown do
15
+ # ...clean up actions...
16
+ # end
17
+ #
18
+ # step 'Prepare the things' do
19
+ # ...setup steps...
20
+ # end
21
+ #
22
+ # step 'Test the things' do
23
+ # ...tests...
24
+ # end
25
+ # end
26
+ #
27
+ module Structure
28
+
29
+ # Provides a method to help structure tests into coherent steps.
30
+ # @param [String] step_name The name of the step to be logged.
31
+ # @param [Proc] block The actions to be performed in this step.
32
+ # @api dsl
33
+ def step step_name, &block
34
+ logger.notify "\n * #{step_name}\n"
35
+ yield if block_given?
36
+ end
37
+
38
+ # Provides a method to name tests.
39
+ #
40
+ # @param [String] my_name The name of the test to be logged.
41
+ # @param [Proc] block The actions to be performed during this test.
42
+ #
43
+ # @api dsl
44
+ def test_name my_name, &block
45
+ logger.notify "\n#{my_name}\n"
46
+ yield if block_given?
47
+ end
48
+
49
+ # Declare a teardown process that will be called after a test case is
50
+ # complete.
51
+ #
52
+ # @param block [Proc] block of code to execute during teardown
53
+ # @example Always remove /etc/puppet/modules
54
+ # teardown do
55
+ # on(master, puppet_resource('file', '/etc/puppet/modules',
56
+ # 'ensure=absent', 'purge=true'))
57
+ # end
58
+ def teardown &block
59
+ @teardown_procs << block
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,100 @@
1
+ module Beaker
2
+ module DSL
3
+ # These are wrappers to equivalent {Beaker::Command} objects
4
+ # so that command line actions are executed within an appropriate and
5
+ # configurable environment.
6
+ #
7
+ # I find most of these adapters of suspicious value and have deprecated
8
+ # many of them.
9
+ module Wrappers
10
+
11
+ # This is hairy and because of legacy code it will take a bit more
12
+ # work to disentangle all of the things that are being passed into
13
+ # this catchall param.
14
+ #
15
+ # @api dsl
16
+ def facter(*args)
17
+ options = args.last.is_a?(Hash) ? args.pop : {}
18
+ options['ENV'] ||= {}
19
+ options['ENV'] = options['ENV'].merge( Command::DEFAULT_GIT_ENV )
20
+ Command.new('facter', args, options )
21
+ end
22
+
23
+ # This is hairy and because of legacy code it will take a bit more
24
+ # work to disentangle all of the things that are being passed into
25
+ # this catchall param.
26
+ #
27
+ # @api dsl
28
+ def hiera(*args)
29
+ options = args.last.is_a?(Hash) ? args.pop : {}
30
+ options['ENV'] ||= {}
31
+ options['ENV'] = options['ENV'].merge( Command::DEFAULT_GIT_ENV )
32
+ Command.new('hiera', args, options )
33
+ end
34
+
35
+ # @param [String] command_string A string of to be interpolated
36
+ # within the context of a host in
37
+ # question
38
+ # @example Usage
39
+ # @!visibility private
40
+ def host_command(command_string)
41
+ HostCommand.new(command_string)
42
+ end
43
+
44
+ # This is hairy and because of legacy code it will take a bit more
45
+ # work to disentangle all of the things that are being passed into
46
+ # this catchall param.
47
+ #
48
+ # @api dsl
49
+ def puppet(*args)
50
+ options = args.last.is_a?(Hash) ? args.pop : {}
51
+ options['ENV'] ||= {}
52
+ options['ENV'] = options['ENV'].merge( Command::DEFAULT_GIT_ENV )
53
+ # we assume that an invocation with `puppet()` will have it's first argument
54
+ # a face or sub command
55
+ cmd = "puppet #{args.shift}"
56
+ Command.new( cmd, args, options )
57
+ end
58
+
59
+ # @!visibility private
60
+ def puppet_resource(*args)
61
+ puppet( 'resource', *args )
62
+ end
63
+
64
+ # @!visibility private
65
+ def puppet_doc(*args)
66
+ puppet( 'doc', *args )
67
+ end
68
+
69
+ # @!visibility private
70
+ def puppet_kick(*args)
71
+ puppet( 'kick', *args )
72
+ end
73
+
74
+ # @!visibility private
75
+ def puppet_cert(*args)
76
+ puppet( 'cert', *args )
77
+ end
78
+
79
+ # @!visibility private
80
+ def puppet_apply(*args)
81
+ puppet( 'apply', *args )
82
+ end
83
+
84
+ # @!visibility private
85
+ def puppet_master(*args)
86
+ puppet( 'master', *args )
87
+ end
88
+
89
+ # @!visibility private
90
+ def puppet_agent(*args)
91
+ puppet( 'agent', *args )
92
+ end
93
+
94
+ # @!visibility private
95
+ def puppet_filebucket(*args)
96
+ puppet( 'filebucket', *args )
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,193 @@
1
+ require 'socket'
2
+ require 'timeout'
3
+
4
+ %w(command ssh_connection).each do |lib|
5
+ begin
6
+ require "beaker/#{lib}"
7
+ rescue LoadError
8
+ require File.expand_path(File.join(File.dirname(__FILE__), lib))
9
+ end
10
+ end
11
+
12
+ module Beaker
13
+ class Host
14
+
15
+ # This class providers array syntax for using puppet --configprint on a host
16
+ class PuppetConfigReader
17
+ def initialize(host, command)
18
+ @host = host
19
+ @command = command
20
+ end
21
+
22
+ def [](k)
23
+ cmd = PuppetCommand.new(@command, "--configprint #{k.to_s}")
24
+ @host.exec(cmd).stdout.strip
25
+ end
26
+ end
27
+
28
+ def self.create name, options, config
29
+ case config['HOSTS'][name]['platform']
30
+ when /windows/
31
+ Windows::Host.new name, options, config
32
+ when /aix/
33
+ Aix::Host.new name, options, config
34
+ else
35
+ Unix::Host.new name, options, config
36
+ end
37
+ end
38
+
39
+ attr_accessor :logger
40
+ attr_reader :name, :defaults
41
+ def initialize name, options, config
42
+ @logger = options[:logger]
43
+ @name, @options, @config = name, options.dup, config
44
+
45
+ # This is annoying and its because of drift/lack of enforcement/lack of having
46
+ # a explict relationship between our defaults, our setup steps and how they're
47
+ # related through 'type' and the differences between the assumption of our two
48
+ # configurations we have for many of our products
49
+ type = is_pe? ? :pe : :foss
50
+ @defaults = merge_defaults_for_type @config, type
51
+ end
52
+
53
+ def merge_defaults_for_type config, type
54
+ defaults = self.class.send "#{type}_defaults".to_sym
55
+ defaults.merge(config['CONFIG']).merge(config['HOSTS'][name])
56
+ end
57
+
58
+ def node_name
59
+ # TODO: might want to consider caching here; not doing it for now because
60
+ # I haven't thought through all of the possible scenarios that could
61
+ # cause the value to change after it had been cached.
62
+ result = puppet['node_name_value'].strip
63
+ end
64
+
65
+ def port_open? port
66
+ Timeout.timeout 1 do
67
+ begin
68
+ TCPSocket.new(reachable_name, port).close
69
+ return true
70
+ rescue Errno::ECONNREFUSED
71
+ return false
72
+ end
73
+ end
74
+ end
75
+
76
+ def up?
77
+ require 'socket'
78
+ begin
79
+ Socket.getaddrinfo( reachable_name, nil )
80
+ return true
81
+ rescue SocketError
82
+ return false
83
+ end
84
+ end
85
+
86
+ def reachable_name
87
+ self['ip'] || self['vmhostname'] || name
88
+ end
89
+
90
+ # Returning our PuppetConfigReader here allows users of the Host
91
+ # class to do things like `host.puppet['vardir']` to query the
92
+ # 'main' section or, if they want the configuration for a
93
+ # particular run type, `host.puppet('agent')['vardir']`
94
+ def puppet(command='agent')
95
+ PuppetConfigReader.new(self, command)
96
+ end
97
+
98
+ def []= k, v
99
+ @defaults[k] = v
100
+ end
101
+
102
+ def [] k
103
+ @defaults[k]
104
+ end
105
+
106
+ def has_key? k
107
+ @defaults.has_key?(k)
108
+ end
109
+
110
+ def to_str
111
+ @defaults['vmhostname'] || @name
112
+ end
113
+
114
+ def to_s
115
+ @defaults['vmhostname'] || @name
116
+ end
117
+
118
+ def + other
119
+ @name + other
120
+ end
121
+
122
+ def is_pe?
123
+ @config.is_pe?
124
+ end
125
+
126
+ def connection
127
+ @connection ||= SshConnection.connect( reachable_name,
128
+ self['user'],
129
+ self['ssh'] )
130
+ end
131
+
132
+ def close
133
+ @connection.close if @connection
134
+ @connection = nil
135
+ end
136
+
137
+ def exec command, options={}
138
+ # I've always found this confusing
139
+ cmdline = command.cmd_line(self)
140
+
141
+ if options[:silent]
142
+ output_callback = nil
143
+ else
144
+ if @defaults['vmhostname']
145
+ @logger.debug "\n#{self} (#{@name}) $ #{cmdline}"
146
+ else
147
+ @logger.debug "\n#{self} $ #{cmdline}"
148
+ end
149
+ output_callback = logger.method(:host_output)
150
+ end
151
+
152
+ unless $dry_run
153
+ # is this returning a result object?
154
+ # the options should come at the end of the method signature (rubyism)
155
+ # and they shouldn't be ssh specific
156
+ result = connection.execute(cmdline, options, output_callback)
157
+
158
+ unless options[:silent]
159
+ # What?
160
+ result.log(@logger)
161
+ # No, TestCase has the knowledge about whether its failed, checking acceptable
162
+ # exit codes at the host level and then raising...
163
+ # is it necessary to break execution??
164
+ unless result.exit_code_in?(options[:acceptable_exit_codes] || [0])
165
+ limit = 10
166
+ raise "Host '#{self}' exited with #{result.exit_code} running:\n #{cmdline}\nLast #{limit} lines of output were:\n#{result.formatted_output(limit)}"
167
+ end
168
+ end
169
+ # Danger, so we have to return this result?
170
+ result
171
+ end
172
+ end
173
+
174
+ def do_scp_to source, target, options
175
+
176
+ @logger.debug "localhost $ scp #{source} #{@name}:#{target} #{options.to_s}"
177
+ result = connection.scp_to(source, target, options, $dry_run)
178
+ return result
179
+ end
180
+
181
+ def do_scp_from source, target, options
182
+
183
+ @logger.debug "localhost $ scp #{@name}:#{source} #{target} #{options.to_s}"
184
+ result = connection.scp_from(source, target, options, $dry_run)
185
+ return result
186
+ end
187
+
188
+ end
189
+
190
+ require File.expand_path(File.join(File.dirname(__FILE__), 'host/windows'))
191
+ require File.expand_path(File.join(File.dirname(__FILE__), 'host/unix'))
192
+ require File.expand_path(File.join(File.dirname(__FILE__), 'host/aix'))
193
+ end
@@ -0,0 +1,15 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'host'))
2
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'command_factory'))
3
+
4
+ module Aix
5
+ class Host < Unix::Host
6
+ require File.expand_path(File.join(File.dirname(__FILE__), 'aix', 'user'))
7
+ require File.expand_path(File.join(File.dirname(__FILE__), 'aix', 'group'))
8
+ require File.expand_path(File.join(File.dirname(__FILE__), 'aix', 'file'))
9
+
10
+ include Aix::User
11
+ include Aix::Group
12
+ include Aix::File
13
+
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ module Aix::File
2
+ include Beaker::CommandFactory
3
+
4
+ def tmpfile(name)
5
+ execute("rndnum=${RANDOM} && touch /tmp/#{name}.${rndnum} && echo /tmp/#{name}.${rndnum}")
6
+ end
7
+
8
+ def tmpdir(name)
9
+ execute("rndnum=${RANDOM} && mkdir /tmp/#{name}.${rndnum} && echo /tmp/#{name}.${rndnum}")
10
+ end
11
+
12
+ def path_split(paths)
13
+ paths.split(':')
14
+ end
15
+
16
+ end
@@ -0,0 +1,35 @@
1
+ module Aix::Group
2
+ include Beaker::CommandFactory
3
+
4
+ def group_list(&block)
5
+ execute("lsgroup -a ALL") do |result|
6
+ yield result if block_given?
7
+
8
+ result.stdout.lines.map(&:strip)
9
+ end
10
+ end
11
+
12
+ def group_get(name, &block)
13
+ execute("lsgroup #{name}") do |result|
14
+ fail_test "failed to get group #{name}" unless result.stdout =~ /^#{name} id/
15
+
16
+ yield result if block_given?
17
+ end
18
+ end
19
+
20
+ def group_gid(name)
21
+ execute("lsgroup -a id #{name}") do |result|
22
+ # Format is:
23
+ # staff id=500
24
+ result.stdout.split('=').last.strip
25
+ end
26
+ end
27
+
28
+ def group_present(name, &block)
29
+ execute("if ! lsgroup #{name}; then mkgroup #{name}; fi", {}, &block)
30
+ end
31
+
32
+ def group_absent(name, &block)
33
+ execute("if lsgroup #{name}; then rmgroup #{name}; fi", {}, &block)
34
+ end
35
+ end
@@ -0,0 +1,32 @@
1
+ module Aix::User
2
+ include Beaker::CommandFactory
3
+
4
+ def user_list(&block)
5
+ execute("lsuser ALL") do |result|
6
+ users = []
7
+ result.stdout.each_line do |line|
8
+ users << line.split(' ')[0]
9
+ end
10
+
11
+ yield result if block_given?
12
+
13
+ users
14
+ end
15
+ end
16
+
17
+ def user_get(name, &block)
18
+ execute("lsuser #{name}") do |result|
19
+ fail_test "failed to get user #{name}" unless result.stdout =~ /^#{name} id/
20
+
21
+ yield result if block_given?
22
+ end
23
+ end
24
+
25
+ def user_present(name, &block)
26
+ execute("if ! lsuser #{name}; then mkuser #{name}; fi", {}, &block)
27
+ end
28
+
29
+ def user_absent(name, &block)
30
+ execute("if lsuser #{name}; then rmuser #{name}; fi", {}, &block)
31
+ end
32
+ end