beaker-hostgenerator 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ module BeakerHostGenerator
2
+ module Errors
3
+ class Error < RuntimeError ; end
4
+ class InvalidNodeSpecError < BeakerHostGenerator::Errors::Error ; end
5
+ end
6
+ end
@@ -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,5 @@
1
+ module BeakerHostGenerator
2
+ module Version
3
+ STRING = '0.1.0'
4
+ end
5
+ 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