beaker 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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