beaker-hostgenerator 0.1.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.
- checksums.yaml +15 -0
- data/.gitignore +25 -0
- data/.rspec +3 -0
- data/.simplecov +9 -0
- data/CHANGELOG.md +33 -0
- data/CONTRIBUTING.md +54 -0
- data/Gemfile +27 -0
- data/HISTORY.md +211 -0
- data/LICENSE +202 -0
- data/README.md +112 -0
- data/Rakefile +48 -0
- data/acceptance/config/nodes/vagrant-ubuntu-1404.yml +8 -0
- data/acceptance/tests/first.rb +8 -0
- data/beaker-hostgenerator.gemspec +44 -0
- data/bin/beaker-hostgenerator +8 -0
- data/bin/genconfig2 +10 -0
- data/lib/beaker-hostgenerator.rb +8 -0
- data/lib/beaker-hostgenerator/cli.rb +126 -0
- data/lib/beaker-hostgenerator/data.rb +55 -0
- data/lib/beaker-hostgenerator/data/vmpooler.rb +397 -0
- data/lib/beaker-hostgenerator/error.rb +6 -0
- data/lib/beaker-hostgenerator/generator.rb +143 -0
- data/lib/beaker-hostgenerator/generator/vmpooler.rb +57 -0
- data/lib/beaker-hostgenerator/roles.rb +22 -0
- data/lib/beaker-hostgenerator/util.rb +64 -0
- data/lib/beaker-hostgenerator/version.rb +5 -0
- data/spec/beaker-hostgenerator/generator_spec.rb +98 -0
- data/spec/helpers.rb +109 -0
- data/spec/spec_helper.rb +10 -0
- metadata +230 -0
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'beaker-hostgenerator/util'
|
2
|
+
require 'beaker-hostgenerator/data'
|
3
|
+
require 'beaker-hostgenerator/error'
|
4
|
+
require 'beaker-hostgenerator/roles'
|
5
|
+
|
6
|
+
require 'yaml'
|
7
|
+
|
8
|
+
module BeakerHostGenerator
|
9
|
+
class Generator
|
10
|
+
include BeakerHostGenerator::Data
|
11
|
+
include BeakerHostGenerator::Errors
|
12
|
+
include BeakerHostGenerator::Utils
|
13
|
+
|
14
|
+
BASE_HOST_CONFIG = {
|
15
|
+
'pe_dir' => BeakerHostGenerator::Utils.pe_dir(PE_VERSION, PE_FAMILY),
|
16
|
+
'pe_ver' => PE_VERSION,
|
17
|
+
'pe_upgrade_dir' => BeakerHostGenerator::Utils.pe_dir(PE_UPGRADE_VERSION, PE_UPGRADE_FAMILY),
|
18
|
+
'pe_upgrade_ver' => PE_UPGRADE_VERSION,
|
19
|
+
}
|
20
|
+
attr_reader :options
|
21
|
+
|
22
|
+
def initialize options
|
23
|
+
@options = options
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.create( options )
|
27
|
+
hypervisor_type = options[:hypervisor]
|
28
|
+
|
29
|
+
hclass = case hypervisor_type
|
30
|
+
when /vmpooler/
|
31
|
+
BeakerHostGenerator::Vmpooler
|
32
|
+
when /vagrant/
|
33
|
+
BeakerHostGenerator::Vagrant
|
34
|
+
else
|
35
|
+
raise "Invalid hypervisor #{type}"
|
36
|
+
end
|
37
|
+
|
38
|
+
return hclass.new(options)
|
39
|
+
end
|
40
|
+
|
41
|
+
def generate tokens
|
42
|
+
nodeid = Hash.new(1)
|
43
|
+
ostype = nil
|
44
|
+
|
45
|
+
tokens.each do |token|
|
46
|
+
if is_ostype_token?(token)
|
47
|
+
if nodeid[ostype] == 1 and ostype != nil
|
48
|
+
raise "Error: no nodes generated for #{ostype}"
|
49
|
+
end
|
50
|
+
ostype = token
|
51
|
+
next
|
52
|
+
end
|
53
|
+
|
54
|
+
node_info = __parse_node_info_token(token)
|
55
|
+
|
56
|
+
node_info['ostype'] = ostype
|
57
|
+
node_info['nodeid'] = nodeid[ostype]
|
58
|
+
|
59
|
+
host_name, host_config = generate_node(node_info, BASE_HOST_CONFIG)
|
60
|
+
|
61
|
+
if PE_USE_WIN32 && ostype =~ /windows/ && node_info['bits'] == "64"
|
62
|
+
host_config['ruby_arch'] = 'x86'
|
63
|
+
host_config['install_32'] = true
|
64
|
+
end
|
65
|
+
|
66
|
+
if not @options[:disable_default_role]
|
67
|
+
host_config['roles'] = ['agent']
|
68
|
+
else
|
69
|
+
host_config['roles'] = []
|
70
|
+
end
|
71
|
+
|
72
|
+
host_config['roles'].concat __generate_host_roles(node_info)
|
73
|
+
host_config['roles'].uniq!
|
74
|
+
|
75
|
+
if not @options[:disable_role_config]
|
76
|
+
host_config['roles'].each do |role|
|
77
|
+
host_config.deep_merge! __get_role_config(role)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
@config['HOSTS'][host_name] = host_config
|
82
|
+
nodeid[ostype] += 1
|
83
|
+
end
|
84
|
+
|
85
|
+
return @config.to_yaml
|
86
|
+
end
|
87
|
+
|
88
|
+
def __get_role_config role
|
89
|
+
begin
|
90
|
+
r = BeakerHostGenerator::Roles.new
|
91
|
+
m = r.method(role)
|
92
|
+
rescue NameError
|
93
|
+
return {}
|
94
|
+
end
|
95
|
+
|
96
|
+
return m.call
|
97
|
+
end
|
98
|
+
|
99
|
+
def __parse_node_info_token token
|
100
|
+
node_info = NODE_REGEX.match(token)
|
101
|
+
|
102
|
+
if node_info
|
103
|
+
node_info = Hash[ node_info.names.zip( node_info.captures ) ]
|
104
|
+
else
|
105
|
+
raise InvalidNodeSpecError.new, "Invalid node_info token: #{token}"
|
106
|
+
end
|
107
|
+
|
108
|
+
if node_info['arbitrary_roles']
|
109
|
+
node_info['arbitrary_roles'] = node_info['arbitrary_roles'].split(',') || ''
|
110
|
+
else
|
111
|
+
# Default to empty list to avoid having to check for nil elsewhere
|
112
|
+
node_info['arbitrary_roles'] = []
|
113
|
+
end
|
114
|
+
|
115
|
+
return node_info
|
116
|
+
end
|
117
|
+
|
118
|
+
def __generate_host_roles node_info
|
119
|
+
roles = []
|
120
|
+
|
121
|
+
node_info['roles'].each_char do |c|
|
122
|
+
roles << ROLES[c]
|
123
|
+
end
|
124
|
+
|
125
|
+
node_info['arbitrary_roles'].each do |role|
|
126
|
+
roles << role
|
127
|
+
end
|
128
|
+
|
129
|
+
return roles
|
130
|
+
end
|
131
|
+
|
132
|
+
def is_ostype_token?
|
133
|
+
raise "Method 'is_ostype_token?' not implemented!"
|
134
|
+
end
|
135
|
+
|
136
|
+
def generate_node
|
137
|
+
raise "Method 'generate_node' not implemented!"
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
require 'beaker-hostgenerator/generator/vmpooler'
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'beaker-hostgenerator/generator'
|
2
|
+
require 'beaker-hostgenerator/data/vmpooler'
|
3
|
+
require 'beaker-hostgenerator/data'
|
4
|
+
require 'deep_merge'
|
5
|
+
|
6
|
+
module BeakerHostGenerator
|
7
|
+
class Vmpooler < BeakerHostGenerator::Generator
|
8
|
+
include BeakerHostGenerator::Data
|
9
|
+
include BeakerHostGenerator::Data::Vmpooler
|
10
|
+
|
11
|
+
def initialize(options)
|
12
|
+
@config = {}
|
13
|
+
@config.deep_merge! BASE_CONFIG
|
14
|
+
@config.deep_merge! VMPOOLER_CONFIG
|
15
|
+
super(options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def generate_node node_info, base_config
|
19
|
+
host_config = {}
|
20
|
+
host_config.deep_merge! base_config
|
21
|
+
|
22
|
+
# set hypervisor
|
23
|
+
host_config['hypervisor'] = 'vmpooler'
|
24
|
+
|
25
|
+
# generate node definition
|
26
|
+
ostype = node_info['ostype']
|
27
|
+
nodeid = node_info['nodeid']
|
28
|
+
bits = node_info['bits']
|
29
|
+
|
30
|
+
platform = "#{ostype}-#{bits}"
|
31
|
+
name = "#{platform}-#{nodeid}"
|
32
|
+
|
33
|
+
host_config.deep_merge! OSINFO[platform]
|
34
|
+
|
35
|
+
# Some vmpooler/vsphere platforms have special requirements. We munge the
|
36
|
+
# node host_config here if that is necessary.
|
37
|
+
fixup_node host_config
|
38
|
+
|
39
|
+
return name, host_config
|
40
|
+
end
|
41
|
+
|
42
|
+
def is_ostype_token? token
|
43
|
+
OSINFO.each do |key,value|
|
44
|
+
|
45
|
+
ostype = key.split('-')[0]
|
46
|
+
|
47
|
+
if ostype == token
|
48
|
+
return true
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
return false
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'beaker-hostgenerator/data'
|
2
|
+
require 'beaker-hostgenerator/data/vmpooler'
|
3
|
+
require 'deep_merge'
|
4
|
+
|
5
|
+
module BeakerHostGenerator
|
6
|
+
class Roles
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
end
|
10
|
+
|
11
|
+
def compile_master
|
12
|
+
return {
|
13
|
+
'frictionless_options' => {
|
14
|
+
'main' => {
|
15
|
+
'dns_alt_names' => 'puppet',
|
16
|
+
'environmentpath' => '/etc/puppetlabs/puppet/environments',
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'beaker-hostgenerator/data'
|
2
|
+
require 'beaker-hostgenerator/data/vmpooler'
|
3
|
+
require 'deep_merge'
|
4
|
+
|
5
|
+
module BeakerHostGenerator
|
6
|
+
module Utils
|
7
|
+
include BeakerHostGenerator::Data
|
8
|
+
include BeakerHostGenerator::Data::Vmpooler
|
9
|
+
|
10
|
+
def pe_dir(version, family)
|
11
|
+
# If our version is the same as our family, we're installing a
|
12
|
+
# released version. Use the archive path. Otherwise, we want to use
|
13
|
+
# the development build path.
|
14
|
+
if version && family
|
15
|
+
if version == family
|
16
|
+
return "http://enterprise.delivery.puppetlabs.net/archives/releases/#{family}/"
|
17
|
+
else
|
18
|
+
return "http://enterprise.delivery.puppetlabs.net/#{family}/ci-ready"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def fixup_node(cfg)
|
24
|
+
# PE 2.8 doesn't exist for EL 4. We use 2.0 instead.
|
25
|
+
if cfg['platform'] =~ /el-4/ and PE_VERSION =~ /2\.8/
|
26
|
+
cfg['pe_ver'] = "2.0.3"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def dump_hosts(hosts, path)
|
31
|
+
config = {}
|
32
|
+
config.deep_merge! BASE_CONFIG
|
33
|
+
config.deep_merge! VMPOOLER_CONFIG
|
34
|
+
|
35
|
+
hosts.each do |host|
|
36
|
+
config['HOSTS'][host.node_name] = {
|
37
|
+
'roles' => host['roles'],
|
38
|
+
'hypervisor' => "#{host['hypervisor']}",
|
39
|
+
'platform' => "#{host['platform']}",
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
File.open(path, 'w') do |file|
|
44
|
+
file.write(config.to_yaml)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_platforms(hypervisor_type='vmpooler')
|
49
|
+
case hypervisor_type
|
50
|
+
when /vmpooler/
|
51
|
+
osinfo = BeakerHostGenerator::Data::Vmpooler::OSINFO
|
52
|
+
else
|
53
|
+
raise "Invalid hypervisor #{hypervisor_type}"
|
54
|
+
end
|
55
|
+
return osinfo
|
56
|
+
end
|
57
|
+
|
58
|
+
def get_roles
|
59
|
+
return BeakerHostGenerator::Data::ROLES
|
60
|
+
end
|
61
|
+
|
62
|
+
module_function :dump_hosts, :get_platforms, :get_roles, :pe_dir
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'beaker-hostgenerator'
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module BeakerHostGenerator
|
5
|
+
describe Generator do
|
6
|
+
let(:options) {
|
7
|
+
{
|
8
|
+
list_platforms_and_roles: false,
|
9
|
+
disable_default_role: false,
|
10
|
+
disable_role_config: false,
|
11
|
+
hypervisor: 'vmpooler',
|
12
|
+
}
|
13
|
+
}
|
14
|
+
let(:generator) { BeakerHostGenerator::Generator.new(options) }
|
15
|
+
|
16
|
+
describe '__generate_host_roles' do
|
17
|
+
it 'Generates a list of roles' do
|
18
|
+
{
|
19
|
+
{
|
20
|
+
"roles" => "aulcdfm",
|
21
|
+
"arbitrary_roles" => [],
|
22
|
+
} => [
|
23
|
+
'agent',
|
24
|
+
'ca',
|
25
|
+
'classifier',
|
26
|
+
'dashboard',
|
27
|
+
'database',
|
28
|
+
'frictionless',
|
29
|
+
'master',
|
30
|
+
],
|
31
|
+
{
|
32
|
+
"roles" => "a",
|
33
|
+
"arbitrary_roles" => ["meow","hello","compile_master"],
|
34
|
+
} => [
|
35
|
+
'agent',
|
36
|
+
'meow',
|
37
|
+
'hello',
|
38
|
+
'compile_master',
|
39
|
+
],
|
40
|
+
{
|
41
|
+
"roles" => "",
|
42
|
+
"arbitrary_roles" => ["compile_master"],
|
43
|
+
} => [
|
44
|
+
'compile_master'
|
45
|
+
],
|
46
|
+
{
|
47
|
+
"roles" => "",
|
48
|
+
"arbitrary_roles" => [],
|
49
|
+
} => [],
|
50
|
+
}.each do |node_info, roles|
|
51
|
+
expect( generator.__generate_host_roles(node_info) ).to eq( roles )
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '__parse_node_info_token' do
|
57
|
+
|
58
|
+
it 'Raises InvalidNodeSpecError for invalid tokens.' do
|
59
|
+
expect{ generator.__parse_node_info_token("64compile_master") }.to \
|
60
|
+
raise_error(BeakerHostGenerator::Errors::InvalidNodeSpecError)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'Supports no roles in the spec.' do
|
64
|
+
node_info = generator.__parse_node_info_token("64")
|
65
|
+
expect( node_info ).to eq({
|
66
|
+
"roles" => "",
|
67
|
+
"arbitrary_roles" => [],
|
68
|
+
"bits" => "64",
|
69
|
+
})
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'Supports the use of arbitrary roles.' do
|
73
|
+
node_info = generator.__parse_node_info_token("64compile_master,ca,blah.mad")
|
74
|
+
expect( node_info ).to eq({
|
75
|
+
"roles" => "mad",
|
76
|
+
"arbitrary_roles" => ["compile_master", "ca", "blah"],
|
77
|
+
"bits" => "64",
|
78
|
+
})
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'When using arbitrary roles'do
|
82
|
+
it 'Fails without a role-type delimiter (a period)' do
|
83
|
+
expect{ generator.__parse_node_info_token("64compile_master,ca,blah") }.to \
|
84
|
+
raise_error(BeakerHostGenerator::Errors::InvalidNodeSpecError)
|
85
|
+
end
|
86
|
+
it 'It supports no static roles.' do
|
87
|
+
node_info = generator.__parse_node_info_token("64compile_master,ca,blah.")
|
88
|
+
expect( node_info ).to eq({
|
89
|
+
"roles" => "",
|
90
|
+
"arbitrary_roles" => ["compile_master", "ca", "blah"],
|
91
|
+
"bits" => "64",
|
92
|
+
})
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
data/spec/helpers.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
module TestFileHelpers
|
2
|
+
def create_files file_array
|
3
|
+
file_array.each do |f|
|
4
|
+
FileUtils.mkdir_p File.dirname(f)
|
5
|
+
FileUtils.touch f
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def fog_file_contents
|
10
|
+
{ :default => { :aws_access_key_id => "IMANACCESSKEY",
|
11
|
+
:aws_secret_access_key => "supersekritkey",
|
12
|
+
:aix_hypervisor_server => "aix_hypervisor.labs.net",
|
13
|
+
:aix_hypervisor_username => "aixer",
|
14
|
+
:aix_hypervisor_keyfile => "/Users/user/.ssh/id_rsa-acceptance",
|
15
|
+
:solaris_hypervisor_server => "solaris_hypervisor.labs.net",
|
16
|
+
:solaris_hypervisor_username => "harness",
|
17
|
+
:solaris_hypervisor_keyfile => "/Users/user/.ssh/id_rsa-old.private",
|
18
|
+
:solaris_hypervisor_vmpath => "rpoooool/zs",
|
19
|
+
:solaris_hypervisor_snappaths => ["rpoooool/USER/z0"],
|
20
|
+
:vsphere_server => "vsphere.labs.net",
|
21
|
+
:vsphere_username => "vsphere@labs.com",
|
22
|
+
:vsphere_password => "supersekritpassword"} }
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
module HostHelpers
|
28
|
+
HOST_DEFAULTS = { :platform => 'unix',
|
29
|
+
:snapshot => 'pe',
|
30
|
+
:box => 'box_name',
|
31
|
+
:roles => ['agent'],
|
32
|
+
:snapshot => 'snap',
|
33
|
+
:ip => 'default.ip.address',
|
34
|
+
:box => 'default_box_name',
|
35
|
+
:box_url => 'http://default.box.url',
|
36
|
+
}
|
37
|
+
|
38
|
+
HOST_NAME = "vm%d"
|
39
|
+
HOST_SNAPSHOT = "snapshot%d"
|
40
|
+
HOST_IP = "ip.address.for.%s"
|
41
|
+
HOST_BOX = "%s_of_my_box"
|
42
|
+
HOST_BOX_URL = "http://address.for.my.box.%s"
|
43
|
+
HOST_TEMPLATE = "%s_has_a_template"
|
44
|
+
|
45
|
+
def logger
|
46
|
+
double( 'logger' ).as_null_object
|
47
|
+
end
|
48
|
+
|
49
|
+
def make_opts
|
50
|
+
opts = StringifyHash.new
|
51
|
+
opts.merge( { :logger => logger,
|
52
|
+
:host_config => 'sample.config',
|
53
|
+
:type => nil,
|
54
|
+
:pooling_api => 'http://vcloud.delivery.puppetlabs.net/',
|
55
|
+
:datastore => 'instance0',
|
56
|
+
:folder => 'Delivery/Quality Assurance/Staging/Dynamic',
|
57
|
+
:resourcepool => 'delivery/Quality Assurance/Staging/Dynamic',
|
58
|
+
:gce_project => 'beaker-compute',
|
59
|
+
:gce_keyfile => '/path/to/keyfile.p12',
|
60
|
+
:gce_password => 'notasecret',
|
61
|
+
:gce_email => '12345678910@developer.gserviceaccount.com' } )
|
62
|
+
end
|
63
|
+
|
64
|
+
def generate_result (name, opts )
|
65
|
+
result = double( 'result' )
|
66
|
+
stdout = opts.has_key?(:stdout) ? opts[:stdout] : name
|
67
|
+
stderr = opts.has_key?(:stderr) ? opts[:stderr] : name
|
68
|
+
exit_code = opts.has_key?(:exit_code) ? opts[:exit_code] : 0
|
69
|
+
exit_code = [exit_code].flatten
|
70
|
+
allow( result ).to receive( :stdout ).and_return( stdout )
|
71
|
+
allow( result ).to receive( :stderr ).and_return( stderr )
|
72
|
+
allow( result ).to receive( :exit_code ).and_return( *exit_code )
|
73
|
+
result
|
74
|
+
end
|
75
|
+
|
76
|
+
def make_host_opts name, opts
|
77
|
+
make_opts.merge( { 'HOSTS' => { name => opts } } ).merge( opts )
|
78
|
+
end
|
79
|
+
|
80
|
+
def make_host name, host_hash
|
81
|
+
host_hash = StringifyHash.new.merge(HOST_DEFAULTS.merge(host_hash))
|
82
|
+
|
83
|
+
host = make_opts.merge(host_hash)
|
84
|
+
|
85
|
+
allow(host).to receive( :name ).and_return( name )
|
86
|
+
allow(host).to receive( :to_s ).and_return( name )
|
87
|
+
allow(host).to receive( :exec ).and_return( generate_result( name, host_hash ) )
|
88
|
+
host
|
89
|
+
end
|
90
|
+
|
91
|
+
def make_hosts preset_opts = {}, amt = 3
|
92
|
+
hosts = []
|
93
|
+
(1..amt).each do |num|
|
94
|
+
name = HOST_NAME % num
|
95
|
+
opts = { :snapshot => HOST_SNAPSHOT % num,
|
96
|
+
:ip => HOST_IP % name,
|
97
|
+
:template => HOST_TEMPLATE % name,
|
98
|
+
:box => HOST_BOX % name,
|
99
|
+
:box_url => HOST_BOX_URL % name }.merge( preset_opts )
|
100
|
+
hosts << make_host(name, opts)
|
101
|
+
end
|
102
|
+
hosts
|
103
|
+
end
|
104
|
+
|
105
|
+
def make_instance instance_data = {}
|
106
|
+
OpenStruct.new instance_data
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|