beaker 1.10.0 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  module Unix::Pkg
2
2
  include Beaker::CommandFactory
3
3
 
4
- # This method overrides {Host::pkg_initialize} to provide
4
+ # This method overrides {Beaker::Host#pkg_initialize} to provide
5
5
  # unix-specific package management setup
6
6
  def pkg_initialize
7
7
  @apt_needs_update = true
@@ -111,7 +111,7 @@ module Unix::Pkg
111
111
  # DEBIAN_PLATFORM_CODENAMES map must be kept up-to-date as
112
112
  # support for new versions is added.
113
113
  #
114
- # @note See {Beaker::DSL::Helpers::deploy_package_repo} for info on
114
+ # @note See {Beaker::DSL::Helpers#deploy_package_repo} for info on
115
115
  # params
116
116
  def deploy_apt_repo(path, name, version)
117
117
  codename = DEBIAN_PLATFORM_CODENAMES[self['platform']]
@@ -127,7 +127,7 @@ module Unix::Pkg
127
127
 
128
128
  # Deploy yum configuration generated by the packaging tooling
129
129
  #
130
- # @note See {Beaker::DSL::Helpers::deploy_package_repo} for info on
130
+ # @note See {Beaker::DSL::Helpers#deploy_package_repo} for info on
131
131
  # params
132
132
  def deploy_yum_repo(path, name, version)
133
133
  repo_file = "#{path}/rpm/pl-#{name}-#{version}-repos-pe-#{self['platform']}.repo"
@@ -136,7 +136,7 @@ module Unix::Pkg
136
136
 
137
137
  # Deploy zypper repo configuration generated by the packaging tooling
138
138
  #
139
- # @note See {Beaker::DSL::Helpers::deploy_package_repo} for info on
139
+ # @note See {Beaker::DSL::Helpers#deploy_package_repo} for info on
140
140
  # params
141
141
  def deploy_zyp_repo(path, name, version)
142
142
  repo_file = "#{path}/rpm/pl-#{name}-#{version}-repos-pe-#{self['platform']}.repo"
@@ -148,10 +148,10 @@ module Unix::Pkg
148
148
 
149
149
  # Deploy configuration generated by the packaging tooling to this host.
150
150
  #
151
- # This method calls one of {deploy_apt_repo}, {deploy_yum_repo}, or
152
- # {deploy_zyp_repo} depending on the platform of this Host.
151
+ # This method calls one of #deploy_apt_repo, #deploy_yum_repo, or
152
+ # #deploy_zyp_repo depending on the platform of this Host.
153
153
  #
154
- # @note See {Beaker::DSL::Helpers::deploy_package_repo} for info on
154
+ # @note See {Beaker::DSL::Helpers#deploy_package_repo} for info on
155
155
  # params
156
156
  def deploy_package_repo(path, name, version)
157
157
  if not File.exists? path
@@ -26,6 +26,8 @@ module Windows
26
26
  'puppetservice' => 'pe-httpd',
27
27
  'puppetpath' => '`cygpath -smF 35`/PuppetLabs/puppet/etc',
28
28
  'puppetvardir' => '`cygpath -smF 35`/PuppetLabs/puppet/var',
29
+ 'distmoduledir' => '`cygpath -smF 35`/PuppetLabs/puppet/etc/modules',
30
+ 'sitemoduledir' => 'C:/usr/share/puppet/modules',
29
31
  #if an x86 Program Files dir exists then use it, default to just Program Files
30
32
  'puppetbindir' => '$( [ -d "/cygdrive/c/Program Files (x86)" ] && echo "/cygdrive/c/Program Files (x86)" || echo "/cygdrive/c/Program Files" )/Puppet Labs/Puppet Enterprise/bin',
31
33
  'pathseparator' => ';',
@@ -39,6 +41,8 @@ module Windows
39
41
  'group' => 'Administrators',
40
42
  'puppetpath' => '`cygpath -smF 35`/PuppetLabs/puppet/etc',
41
43
  'puppetvardir' => '`cygpath -smF 35`/PuppetLabs/puppet/var',
44
+ 'distmoduledir' => '`cygpath -smF 35`/PuppetLabs/puppet/etc/modules',
45
+ 'sitemoduledir' => 'C:/usr/share/puppet/modules',
42
46
  'hieralibdir' => '`cygpath -w /opt/puppet-git-repos/hiera/lib`',
43
47
  'hierapuppetlibdir' => '`cygpath -w /opt/puppet-git-repos/hiera-puppet/lib`',
44
48
  # PATH related variables need to be Unix, which cygwin converts
@@ -107,13 +107,19 @@ module Beaker
107
107
  end
108
108
 
109
109
  #Update /etc/hosts on the master node to include a rule for lookup of the master by name/ip.
110
- # @param [Host, Array<Host>, String, Symbol] host One or more hosts to act upon
110
+ # @param [Host, Array<Host>, String, Symbol] hosts One or more hosts to act upon
111
111
  # @param [Hash{Symbol=>String}] opts Options to alter execution.
112
112
  # @option opts [Beaker::Logger] :logger A {Beaker::Logger} object
113
113
  def add_master_entry hosts, opts
114
114
  logger = opts[:logger]
115
115
  master = only_host_with_role(hosts, :master)
116
116
  logger.notify "Add Master entry to /etc/hosts on #{master.name}"
117
+ if master["hypervisor"] and master["hypervisor"] =~ /docker/
118
+ # skip on docker because as of 0.8.1 /etc/hosts isn't modifiable
119
+ # https://github.com/dotcloud/docker/issues/2267
120
+ logger.debug "Don't update master entry on docker masters"
121
+ return
122
+ end
117
123
  if master["hypervisor"] and master["hypervisor"] =~ /vagrant/
118
124
  logger.debug "Don't update master entry on vagrant masters"
119
125
  return
@@ -325,7 +331,8 @@ module Beaker
325
331
  else
326
332
  logger.debug "Give root a copy of current user's keys, on #{host.name}"
327
333
  if host['platform'] =~ /windows/
328
- host.exec(Command.new('sudo su -c "cp -r .ssh /home/Administrator/."'))
334
+ host.exec(Command.new('cp -r .ssh /cygdrive/c/Users/Administrator/.'))
335
+ host.exec(Command.new('chown -R Administrator /cygdrive/c/Users/Administrator/.ssh'))
329
336
  else
330
337
  host.exec(Command.new('sudo su -c "cp -r .ssh /root/."'), {:pty => true})
331
338
  end
@@ -46,6 +46,8 @@ module Beaker
46
46
  Beaker::Vagrant
47
47
  when /google/
48
48
  Beaker::GoogleCompute
49
+ when /docker/
50
+ Beaker::Docker
49
51
  when /none/
50
52
  Beaker::Hypervisor
51
53
  else
@@ -104,7 +106,7 @@ module Beaker
104
106
  end
105
107
  end
106
108
 
107
- %w( vsphere_helper vagrant fusion blimper aws_sdk vsphere vcloud vcloud_pooled aixer solaris google_compute_helper google_compute).each do |lib|
109
+ %w( vsphere_helper vagrant fusion blimper aws_sdk vsphere vcloud vcloud_pooled aixer solaris docker google_compute_helper google_compute).each do |lib|
108
110
  begin
109
111
  require "hypervisor/#{lib}"
110
112
  rescue LoadError
@@ -0,0 +1,154 @@
1
+ module Beaker
2
+ class Docker < Beaker::Hypervisor
3
+
4
+ def initialize(hosts, options)
5
+ require 'docker'
6
+ @options = options
7
+ @logger = options[:logger]
8
+ @hosts = hosts
9
+
10
+ # increase the http timeouts as provisioning images can be slow
11
+ ::Docker.options = { :write_timeout => 300, :read_timeout => 300 }
12
+ # assert that the docker-api gem can talk to your docker
13
+ # enpoint. Will raise if there is a version mismatch
14
+ ::Docker.validate_version!
15
+ # Pass on all the logging from docker-api to the beaker logger instance
16
+ ::Docker.logger = @logger
17
+ end
18
+
19
+ def provision
20
+ @logger.notify "Provisioning docker"
21
+
22
+ @hosts.each do |host|
23
+ @logger.notify "provisioning #{host.name}"
24
+
25
+ @logger.debug("Creating image")
26
+ image = ::Docker::Image.build(dockerfile_for(host), { :rm => true })
27
+ @logger.debug("Tagging image #{image.id} as #{host.name}")
28
+ image.tag({
29
+ :repo => host.name,
30
+ :force => true,
31
+ })
32
+
33
+ @logger.debug("Creating container from image")
34
+ container = ::Docker::Container.create({
35
+ 'Image' => host.name,
36
+ 'Hostname' => host.name,
37
+ })
38
+
39
+ @logger.debug("Starting container #{container.id}")
40
+ container.start({"PublishAllPorts" => true})
41
+
42
+ # Find out where the ssh port is from the container
43
+ ip = container.json["NetworkSettings"]["Ports"]["22/tcp"][0]["HostIp"]
44
+ port = container.json["NetworkSettings"]["Ports"]["22/tcp"][0]["HostPort"]
45
+
46
+ # Update host metadata
47
+ host['ip'] = ip
48
+ host['port'] = port
49
+ host['ssh'] = {
50
+ :password => root_password,
51
+ :port => port,
52
+ }
53
+
54
+ @logger.debug("node available as ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@#{ip} -p #{port}")
55
+ host['docker_container'] = container
56
+ host['docker_image'] = image
57
+ end
58
+ end
59
+
60
+ def cleanup
61
+ @logger.notify "Cleaning up docker"
62
+ @hosts.each do |host|
63
+ if container = host['docker_container']
64
+ @logger.debug("stop container #{container.id}")
65
+ begin
66
+ container.stop
67
+ rescue Excon::Errors::ClientError => e
68
+ @logger.warn("stop of container #{container.id} failed: #{e.response.body}")
69
+ end
70
+ @logger.debug("delete container #{container.id}")
71
+ begin
72
+ container.delete
73
+ rescue Excon::Errors::ClientError => e
74
+ @logger.warn("deletion of container #{container.id} failed: #{e.response.body}")
75
+ end
76
+ end
77
+
78
+ if image = host['docker_image']
79
+ @logger.debug("delete image #{image.id}")
80
+ begin
81
+ image.delete
82
+ rescue Excon::Errors::ClientError => e
83
+ @logger.warn("deletion of image #{image.id} failed: #{e.response.body}")
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def root_password
92
+ 'root'
93
+ end
94
+
95
+ def dockerfile_for(host)
96
+ # specify base image
97
+ dockerfile = <<-EOF
98
+ FROM #{host['image']}
99
+ EOF
100
+
101
+ # additional options to specify to the sshd
102
+ # may vary by platform
103
+ sshd_options = ''
104
+
105
+ # add platform-specific actions
106
+ case host['platform']
107
+ when /ubuntu/, /debian/
108
+ dockerfile += <<-EOF
109
+ RUN apt-get update
110
+ RUN apt-get install -y openssh-server openssh-client
111
+ EOF
112
+ when /^el-/, /centos/, /fedora/, /redhat/
113
+ dockerfile += <<-EOF
114
+ RUN yum clean all
115
+ RUN yum install -y sudo openssh-server openssh-clients
116
+ RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
117
+ RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key
118
+ EOF
119
+ when /opensuse/, /sles/
120
+ sshd_options = '-o "PermitRootLogin yes" -o "PasswordAuthentication yes" -o "UsePAM no"'
121
+ dockerfile += <<-EOF
122
+ RUN zypper -n in openssh
123
+ RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
124
+ RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key
125
+ EOF
126
+ else
127
+ # TODO add more platform steps here
128
+ raise "platform #{host['platform']} not yet supported on docker"
129
+ end
130
+
131
+ # Make sshd directory, set root password
132
+ dockerfile += <<-EOF
133
+ RUN mkdir -p /var/run/sshd
134
+ RUN echo root:#{root_password} | chpasswd
135
+ EOF
136
+
137
+ # Any extra commands specified for the host
138
+ dockerfile += (host['docker_image_commands'] || []).map { |command|
139
+ "RUN #{command}\n"
140
+ }.join('')
141
+
142
+ # How to start a sshd on port 22. May be an init for more supervision
143
+ cmd = host['docker_cmd'] || "/usr/sbin/sshd -D #{sshd_options}"
144
+ dockerfile += <<-EOF
145
+ EXPOSE 22
146
+ CMD #{cmd}
147
+ EOF
148
+
149
+ @logger.debug("Dockerfile is #{dockerfile}")
150
+ return dockerfile
151
+ end
152
+
153
+ end
154
+ end
@@ -3,7 +3,7 @@ module Beaker
3
3
  # Return a list of open ports for testing based on a hosts role
4
4
  #
5
5
  # @todo horribly hard-coded
6
- # @param [Array<String>] an array of roles
6
+ # @param [Array<String>] roles An array of roles
7
7
  # @return [Array<Number>] array of port numbers
8
8
  # @api private
9
9
  def self.amiports(roles)
@@ -513,7 +513,7 @@ module Beaker
513
513
  end
514
514
 
515
515
  #Set tags on a Google Compute instance
516
- #@param [Array<String>] tags An array of tags to be added to an instance
516
+ #@param [Array<String>] data An array of tags to be added to an instance
517
517
  #@return [Hash] A correctly formatted Google Compute request hash
518
518
  def instance_setMetadata_req(name, fingerprint, data)
519
519
  { :api_method => @compute.instances.set_metadata,
@@ -195,6 +195,7 @@ module Beaker
195
195
  # - --fail-mode is one of 'fast', 'stop' or nil
196
196
  # - if using blimpy hypervisor an EC2 YAML file exists
197
197
  # - if using the aix, solaris, or vcloud hypervisors a .fog file exists
198
+ # - if using docker hypervisor are using RUBY 1.9+
198
199
  # - that one and only one master is defined per set of hosts
199
200
  # - that solaris/windows/aix hosts are agent only for PE tests OR
200
201
  # - that windows/aix host are agent only if type is not 'pe'
@@ -260,6 +261,12 @@ module Beaker
260
261
  check_yaml_file(@options[:dot_fog], "required by #{visor}")
261
262
  end
262
263
  end
264
+ #if using docker need ruby 1.9+
265
+ if hypervisors.include?('docker')
266
+ if RUBY_VERSION < '1.9'
267
+ parser_error "Cannot use the 'docker' hypervisor on Ruby < 1.9 (using #{RUBY_VERSION})"
268
+ end
269
+ end
263
270
 
264
271
  #check that roles of hosts make sense
265
272
  # - must be one and only one master
@@ -5,73 +5,93 @@ module Beaker
5
5
  module Presets
6
6
 
7
7
  # Generates an OptionsHash of the environment variables of interest to Beaker
8
- #
9
- # Currently supports:
10
- #
11
- # consoleport, IS_PE, pe_dist_dir, pe_version_file, pe_version_file_win, pe_ver
12
8
  #
13
9
  # @return [OptionsHash] The supported environment variables in an OptionsHash,
14
10
  # empty or nil environment variables are removed from the OptionsHash
15
11
  def self.env_vars
16
12
  h = Beaker::Options::OptionsHash.new
13
+ consoleport = ENV['BEAKER_CONSOLEPORT'] || ENV['consoleport']
17
14
  h.merge({
18
- :consoleport => ENV['consoleport'] ? ENV['consoleport'].to_i : nil,
19
- :type => ENV['IS_PE'] ? 'pe' : nil,
20
- :pe_dir => ENV['pe_dist_dir'],
21
- :pe_version_file => ENV['pe_version_file'],
22
- :pe_version_file_win => ENV['pe_version_file'],
23
- :pe_ver => ENV['pe_ver'],
24
- :project => ENV['BEAKER_project'],
25
- :department => ENV['BEAKER_department'],
26
- :jenkins_build_url => ENV['BUILD_URL'],
15
+ :home => ENV['HOME'],
16
+ :project => ENV['BEAKER_PROJECT'] || ENV['BEAKER_project'],
17
+ :department => ENV['BEAKER_DEPARTMENT'] || ENV['BEAKER_department'],
18
+ :jenkins_build_url => ENV['BEAKER_BUILD_URL'] || ENV['BUILD_URL'],
19
+ :consoleport => consoleport ? consoleport.to_i : nil,
20
+ :type => (ENV['BEAKER_IS_PE'] || ENV['IS_PE']) ? 'pe' : nil,
21
+ :pe_dir => ENV['BEAKER_PE_DIR'] || ENV['pe_dist_dir'],
22
+ :pe_version_file => ENV['BEAKER_PE_VERSION_FILE'] || ENV['pe_version_file'],
23
+ :pe_version_file_win => ENV['BEAKER_PE_VERSION_FILE'] || ENV['pe_version_file'],
24
+ :pe_ver => ENV['BEAKER_PE_VER'] || ENV['pe_ver'],
25
+ :forge_host => ENV['BEAKER_FORGE_HOST'] || ENV['forge_host'],
26
+ :answers => {
27
+ :q_puppet_enterpriseconsole_auth_user_email =>
28
+ ENV['q_puppet_enterpriseconsole_auth_user_email'] || 'admin@example.com',
29
+ :q_puppet_enterpriseconsole_auth_password =>
30
+ ENV['q_puppet_enterpriseconsole_auth_password'] || '~!@#$%^*-/ aZ',
31
+ :q_puppet_enterpriseconsole_smtp_host =>
32
+ ENV['q_puppet_enterpriseconsole_smtp_host'],
33
+ :q_puppet_enterpriseconsole_smtp_port =>
34
+ ENV['q_puppet_enterpriseconsole_smtp_port'] || 25,
35
+ :q_puppet_enterpriseconsole_smtp_username =>
36
+ ENV['q_puppet_enterpriseconsole_smtp_username'],
37
+ :q_puppet_enterpriseconsole_smtp_password =>
38
+ ENV['q_puppet_enterpriseconsole_smtp_password'],
39
+ :q_puppet_enterpriseconsole_smtp_use_tls =>
40
+ ENV['q_puppet_enterpriseconsole_smtp_use_tls'] || 'n',
41
+ :q_verify_packages =>
42
+ ENV['q_verify_packages'] || 'y',
43
+ :q_puppetdb_password =>
44
+ ENV['q_puppetdb_password'] || '~!@#$%^*-/ aZ',
45
+ }
27
46
  }.delete_if {|key, value| value.nil? or value.empty? })
28
47
  end
29
48
 
30
49
  # Generates an OptionsHash of preset values for arguments supported by Beaker
31
- #
50
+ #
32
51
  # @return [OptionsHash] The supported arguments in an OptionsHash
33
52
  def self.presets
34
53
  h = Beaker::Options::OptionsHash.new
35
54
  h.merge({
36
- :project => 'Beaker',
37
- :department => ENV['USER'] || ENV['USERNAME'] || 'unknown',
38
- :validate => true,
39
- :jenkins_build_url => nil,
40
- :log_level => 'verbose',
41
- :trace_limit => 10,
42
- :hosts_file => 'sample.cfg',
43
- :options_file => nil,
44
- :type => 'pe',
45
- :provision => true,
46
- :preserve_hosts => 'never',
47
- :root_keys => false,
48
- :quiet => false,
49
- :xml => false,
50
- :color => true,
51
- :dry_run => false,
52
- :timeout => 300,
53
- :fail_mode => 'slow',
54
- :timesync => false,
55
- :repo_proxy => false,
56
- :add_el_extras => false,
57
- :add_master_entry => false,
58
- :consoleport => 443,
59
- :pe_dir => '/opt/enterprise/dists',
60
- :pe_version_file => 'LATEST',
55
+ :project => 'Beaker',
56
+ :department => ENV['USER'] || ENV['USERNAME'] || 'unknown',
57
+ :validate => true,
58
+ :jenkins_build_url => nil,
59
+ :forge_host => 'vulcan-acceptance.delivery.puppetlabs.net',
60
+ :log_level => 'verbose',
61
+ :trace_limit => 10,
62
+ :hosts_file => 'sample.cfg',
63
+ :options_file => nil,
64
+ :type => 'pe',
65
+ :provision => true,
66
+ :preserve_hosts => 'never',
67
+ :root_keys => false,
68
+ :quiet => false,
69
+ :xml => false,
70
+ :color => true,
71
+ :dry_run => false,
72
+ :timeout => 300,
73
+ :fail_mode => 'slow',
74
+ :timesync => false,
75
+ :repo_proxy => false,
76
+ :add_el_extras => false,
77
+ :add_master_entry => false,
78
+ :consoleport => 443,
79
+ :pe_dir => '/opt/enterprise/dists',
80
+ :pe_version_file => 'LATEST',
61
81
  :pe_version_file_win => 'LATEST-win',
62
- :dot_fog => File.join(ENV['HOME'], '.fog'),
63
- :ec2_yaml => 'config/image_templates/ec2.yaml',
64
- :help => false,
65
- :ssh => {
66
- :config => false,
67
- :paranoid => false,
68
- :timeout => 300,
69
- :auth_methods => ["publickey"],
70
- :port => 22,
71
- :forward_agent => true,
72
- :keys => ["#{ENV['HOME']}/.ssh/id_rsa"],
73
- :user_known_hosts_file => "#{ENV['HOME']}/.ssh/known_hosts",
74
- }
82
+ :dot_fog => File.join(ENV['HOME'], '.fog'),
83
+ :ec2_yaml => 'config/image_templates/ec2.yaml',
84
+ :help => false,
85
+ :ssh => {
86
+ :config => false,
87
+ :paranoid => false,
88
+ :timeout => 300,
89
+ :auth_methods => ["publickey"],
90
+ :port => 22,
91
+ :forward_agent => true,
92
+ :keys => ["#{ENV['HOME']}/.ssh/id_rsa"],
93
+ :user_known_hosts_file => "#{ENV['HOME']}/.ssh/known_hosts",
94
+ }
75
95
  })
76
96
  end
77
97