controlrepo 1.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.
@@ -0,0 +1,185 @@
1
+ class Controlrepo
2
+ class Beaker
3
+ def self.facts_to_vagrant_box(facts)
4
+ # Gets the most similar vagrant box to the facts set provided, will accept a single fact
5
+ # se or an array
6
+
7
+ if facts.is_a?(Array)
8
+ returnval = []
9
+ facts.each do |fact|
10
+ returnval << self.facts_to_vagrant_box(fact)
11
+ end
12
+ return returnval
13
+ end
14
+
15
+ begin
16
+ if facts['os']['distro']['id'] == 'Ubuntu'
17
+ os = 'ubuntu'
18
+ version = facts['os']['distro']['release']['major']
19
+ end
20
+ rescue
21
+ # Do nothing, this is the easiest way to handle the hash bing in different formats
22
+ end
23
+
24
+ begin
25
+ if facts['os']['distro']['id'] == 'Debian'
26
+ os = 'Debian'
27
+ version = facts['os']['distro']['release']['major']
28
+ end
29
+ rescue
30
+ # Do nothing
31
+ end
32
+
33
+ begin
34
+ if facts['os']['family'] == "RedHat"
35
+ os = 'centos'
36
+ version = "#{facts['os']['release']['major']}.#{facts['os']['release']['minor']}"
37
+ end
38
+ rescue
39
+ # Do nothing
40
+ end
41
+
42
+ return "UNKNOWN" unless os.is_a?(String)
43
+
44
+ if facts['os']['architecture'] =~ /64/
45
+ arch = '64'
46
+ else
47
+ arch = '32'
48
+ end
49
+
50
+ "puppetlabs/#{os}-#{version}-#{arch}-puppet"
51
+ end
52
+
53
+ # This will take a fact set and return the beaker platform of that machine
54
+ # This is necissary as beaker needs the platform set up correctly to know which
55
+ # commands to run when we do stuff. Personally I would prefer beaker to detect the
56
+ # platform as it would not be that hard, especially once puppet is installed, oh well.
57
+ def self.facts_to_platform(facts)
58
+ if facts.is_a?(Array)
59
+ returnval = []
60
+ facts.each do |fact|
61
+ returnval << self.facts_to_platform(fact)
62
+ end
63
+ return returnval
64
+ end
65
+
66
+ begin
67
+ if facts['os']['family'] == 'RedHat'
68
+ platform = 'el'
69
+ version = facts['os']['release']['major']
70
+ end
71
+ rescue
72
+ # Do nothing, this is the easiest way to handle the hash bing in different formats
73
+ end
74
+
75
+ begin
76
+ if facts['os']['distro']['id'] == 'Ubuntu'
77
+ platform = 'ubuntu'
78
+ version = facts['os']['distro']['release']['major']
79
+ end
80
+ rescue
81
+ # Do nothing, this is the easiest way to handle the hash bing in different formats
82
+ end
83
+
84
+ begin
85
+ if facts['os']['distro']['id'] == 'Debian'
86
+ platform = 'Debian'
87
+ version = facts['os']['distro']['release']['major']
88
+ end
89
+ rescue
90
+ # Do nothing
91
+ end
92
+
93
+ if facts['os']['architecture'] =~ /64/
94
+ arch = '64'
95
+ else
96
+ arch = '32'
97
+ end
98
+
99
+ "#{platform}-#{version}-#{arch}"
100
+ end
101
+
102
+ # This little method will deploy a Controlrepo object to a host, just using r10k deploy
103
+ def self.deploy_controlrepo_on(host, repo = Controlrepo.new())
104
+ require 'beaker-rspec'
105
+ require 'controlrepo'
106
+
107
+ if host.is_a?(Array)
108
+ hosts.each do |single_host|
109
+ deploy_controlrepo_on(single_host)
110
+ end
111
+ end
112
+
113
+ # Use a beaker helper to do the install (*nix only)
114
+ install_r10k_on(host)
115
+
116
+ # Use beaker to install git
117
+ host.install_package('git')
118
+
119
+ # copy the file over to the host (Maybe I should be changing the directory here??)
120
+ scp_to(host,repo.r10k_config_file,'/tmp/r10k.yaml')
121
+
122
+ # Do an r10k deploy
123
+ r10k_deploy(host,{
124
+ :puppetfile => true,
125
+ :configfile => '/tmp/r10k.yaml',
126
+ })
127
+ end
128
+
129
+ # This actually provisions a node and checks that puppet will be able to run and
130
+ # be idempotent. It hacks the beaker NetworkManager object to do this. The reason
131
+ # is that beaker is designed to run in the following order:
132
+ # 1. Spin up nodes
133
+ # 2. Run all tests
134
+ # 3. Kill all nodes
135
+ #
136
+ # This is not helpful for us. We want to be able to test all of our classes on
137
+ # all of our nodes, this could be a lot of vms and having them all running at once
138
+ # would be a real kick in the dick for whatever system was running it.
139
+ def self.provision_and_test(host,puppet_class,opts = {},repo = Controlrepo.new)
140
+ opts = {:runs_before_idempotency => 1}.merge(opts)
141
+ opts = {:check_idempotency => true}.merge(opts)
142
+ opts = {:deploy_controlrepo => true}.merge(opts)
143
+
144
+
145
+ raise "Hosts must be a single host object, not an array" if host.is_a?(Array)
146
+ raise "Class must be a single Class [String], not an array" unless puppet_class.is_a?(String)
147
+
148
+ # Create our own NWM object that we are going to interact with
149
+ # Note here that 'options', 'logger' and are exposed within the rspec tests
150
+ # if this is run outside of that context it will fail
151
+ network_manager = ::Beaker::NetworkManager.new(options,logger)
152
+
153
+ # Hack the network manager to smash our host in there without provisioning
154
+ network_manager.instance_variable_set(:@hosts,host)
155
+
156
+ # Now that we have a working network manager object, we can provision, but only if
157
+ # we need to, ahhh smart...
158
+ unless host.up?
159
+ network_manager.provision
160
+ network_manager.proxy_package_manager
161
+ network_manager.validate
162
+ network_manager.configure
163
+ end
164
+
165
+ if opts[:deploy_controlrepo]
166
+ # Get the code onto the host
167
+ Controlrepo::Beaker.deploy_controlrepo_on(host,repo)
168
+ end
169
+
170
+ # Actually run the tests
171
+ manifest = "include #{puppet_class}"
172
+
173
+ opts[:runs_before_idempotency].times do
174
+ apply_manifest_on(host,manifest,{:catch_failures => true})
175
+ end
176
+
177
+ if opts[:check_idempotency]
178
+ apply_manifest_on(host,manifest,{:catch_changes => true})
179
+ end
180
+
181
+ network_manager.cleanup
182
+ end
183
+
184
+ end
185
+ end
@@ -0,0 +1,28 @@
1
+ class Controlrepo
2
+ class Class
3
+ @@all = []
4
+
5
+ attr_accessor :name
6
+ def initialize(name)
7
+ @name = name
8
+ @@all << self
9
+ end
10
+
11
+ def self.find(class_name)
12
+ @@all.each do |cls|
13
+ if class_name.is_a?(Controlrepo::Class)
14
+ if cls = class_name
15
+ return cls
16
+ end
17
+ elsif cls.name == class_name
18
+ return cls
19
+ end
20
+ end
21
+ nil
22
+ end
23
+
24
+ def self.all
25
+ @@all
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,61 @@
1
+ require 'controlrepo/class'
2
+ require 'controlrepo/node'
3
+
4
+ class Controlrepo
5
+ class Group
6
+ @@all = []
7
+
8
+ # Work out how to do class veriables so that I can keep track of all the groups easily
9
+ attr_accessor :name
10
+ attr_accessor :members
11
+
12
+ # You need to pass in an array of strings for members, not objects, it will find the objects
13
+ # by itself, and yes it will reference them, not just create additional ones, woo!
14
+ def initialize(name = nil, members = [])
15
+ @name = name
16
+ @members = []
17
+
18
+ if members.any?
19
+ member_objects = []
20
+ members.each do |member|
21
+ # Try to find the type for each member
22
+ if Controlrepo::Class.find(member)
23
+ member_objects << Controlrepo::Class.find(member)
24
+ elsif Controlrepo::Node.find(member)
25
+ member_objects << Controlrepo::Node.find(member)
26
+ else
27
+ raise "#{member} was not found in the list of nodes or classes!"
28
+ end
29
+ end
30
+
31
+ # Check that they are all the same type
32
+ if member_objects.all? { |item| item.is_a?(Controlrepo::Class) }
33
+ type = Controlrepo::Class
34
+ elsif member_objects.all? { |item| item.is_a?(Controlrepo::Node) }
35
+ type = Controlrepo::Node
36
+ else
37
+ raise 'Groups must contain either all nodes or all classes. Either there was a mix, or something was spelled wrong'
38
+ end
39
+
40
+ # Smash it into the instance variable
41
+ @members = member_objects
42
+ end
43
+
44
+ # Finally add it to the list of all grops
45
+ @@all << self
46
+ end
47
+
48
+ def self.find(group_name)
49
+ @@all.each do |group|
50
+ if group.name == group_name
51
+ return group
52
+ end
53
+ end
54
+ nil
55
+ end
56
+
57
+ def self.all
58
+ @@all
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,43 @@
1
+ require 'controlrepo'
2
+
3
+ class Controlrepo
4
+ class Node
5
+ @@all = []
6
+
7
+
8
+ attr_accessor :name
9
+ attr_accessor :beaker_node
10
+ attr_accessor :fact_set
11
+
12
+ def initialize(name)
13
+ @name = name
14
+ @beaker_node = nil
15
+
16
+ # If we can't find the factset it will fail, so just catch that error and ignore it
17
+ begin
18
+ @fact_set = Controlrepo.facts[(Controlrepo.facts_files.index{|facts_file| File.basename(facts_file,'.json') == name})]
19
+ rescue TypeError
20
+ @fact_set = nil
21
+ end
22
+ @@all << self
23
+
24
+ end
25
+
26
+ def self.find(node_name)
27
+ @@all.each do |node|
28
+ if node_name.is_a?(Controlrepo::Node)
29
+ if node = node_name
30
+ return node
31
+ end
32
+ elsif node.name == node_name
33
+ return node
34
+ end
35
+ end
36
+ nil
37
+ end
38
+
39
+ def self.all
40
+ @@all
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,150 @@
1
+ require 'controlrepo'
2
+ require 'pathname'
3
+
4
+ @repo = nil
5
+ @config = nil
6
+
7
+ task :generate_fixtures do
8
+ repo = Controlrepo.new
9
+ raise ".fixtures.yml already exits, we won't overwrite because we are scared" if File.exists?(File.expand_path('./.fixtures.yml',repo.root))
10
+ File.write(File.expand_path('./.fixtures.yml',repo.root),repo.fixtures)
11
+ end
12
+
13
+ task :hiera_setup do
14
+ repo = Controlrepo.new
15
+ current_config = repo.hiera_config
16
+ current_config.each do |key, value|
17
+ if value.is_a?(Hash)
18
+ if value.has_key?(:datadir)
19
+ current_config[key][:datadir] = Pathname.new(repo.hiera_data).relative_path_from(Pathname.new(File.expand_path('..',repo.hiera_config_file))).to_s
20
+ end
21
+ end
22
+ end
23
+ puts "Changing hiera config from \n#{repo.hiera_config}\nto\n#{current_config}"
24
+ repo.hiera_config = current_config
25
+ end
26
+
27
+ task :generate_nodesets do
28
+ require 'controlrepo/beaker'
29
+ require 'net/http'
30
+ require 'json'
31
+
32
+ repo = Controlrepo.new
33
+
34
+ begin
35
+ Dir.mkdir("#{repo.root}/spec/acceptance")
36
+ puts "Created #{repo.root}/spec/acceptance"
37
+ rescue Errno::EEXIST
38
+ # Do nothing, this is okay
39
+ end
40
+
41
+ begin
42
+ Dir.mkdir("#{repo.root}/spec/acceptance/nodesets")
43
+ puts "Created #{repo.root}/spec/acceptance/nodesets"
44
+ rescue Errno::EEXIST
45
+ # Do nothing, this is okay
46
+ end
47
+
48
+ facts = repo.facts
49
+ facts.each do |fact_set|
50
+ boxname = Controlrepo_beaker.facts_to_vagrant_box(fact_set)
51
+ platform = Controlrepo_beaker.facts_to_platform(fact_set)
52
+ response = Net::HTTP.get(URI.parse("https://atlas.hashicorp.com/api/v1/box/#{boxname}"))
53
+ url = 'URL goes here'
54
+
55
+ unless response =~ /404 Not Found/
56
+ box_info = JSON.parse(response)
57
+ box_info['current_version']['providers'].each do |provider|
58
+ if provider['name'] == 'virtualbox'
59
+ url = provider['original_url']
60
+ end
61
+ end
62
+ end
63
+
64
+ # Use an ERB template to write the files
65
+ template_dir = File.expand_path('../../templates',File.dirname(__FILE__))
66
+ fixtures_template = File.read(File.expand_path('./nodeset.yaml.erb',template_dir))
67
+ output_file = File.expand_path("spec/acceptance/nodesets/#{fact_set['fqdn']}.yml",repo.root)
68
+ if File.exists?(output_file) == false
69
+ File.write(output_file,ERB.new(fixtures_template, nil, '-').result(binding))
70
+ puts "Created #{output_file}"
71
+ else
72
+ puts "#{output_file} already exists, not going to overwrite because scared"
73
+ end
74
+ end
75
+ end
76
+
77
+ task :controlrepo_autotest_prep do
78
+ require 'controlrepo/testconfig'
79
+ @repo = Controlrepo.new
80
+ @config = Controlrepo::TestConfig.new("#{@repo.spec_dir}/controlrepo.yaml")
81
+
82
+ # Deploy r10k to a temp dir
83
+ @config.r10k_deploy_local(@repo)
84
+
85
+ # Create the other directories we need
86
+ FileUtils.mkdir_p("#{@repo.tempdir}/spec/classes")
87
+ FileUtils.mkdir_p("#{@repo.tempdir}/spec/acceptance/nodesets")
88
+
89
+ # Copy our nodesets over
90
+ FileUtils.cp_r("#{@repo.spec_dir}/acceptance/nodesets","#{@repo.tempdir}/spec/acceptance")
91
+
92
+ # Create the Rakefile so that we can take advantage of the existing tasks
93
+ @config.write_rakefile(@repo.tempdir, "spec/classes/**/*_spec.rb")
94
+
95
+ # Create spec_helper.rb
96
+ @config.write_spec_helper("#{@repo.tempdir}/spec",@repo)
97
+
98
+ # Create spec_helper_accpetance.rb
99
+ @config.write_spec_helper_acceptance("#{@repo.tempdir}/spec",@repo)
100
+
101
+ # Create Gemfile
102
+ @config.write_gemfile(@repo.tempdir)
103
+
104
+ # Deduplicate and write the tests (Spec and Acceptance)
105
+ Controlrepo::Test.deduplicate(@config.tests).each do |test|
106
+ @config.write_spec_test("#{@repo.tempdir}/spec/classes",test)
107
+ @config.write_acceptance_test("#{@repo.tempdir}/spec/acceptance",test)
108
+ end
109
+
110
+ # Parse the current hiera config, modify, and write it to the temp dir
111
+ hiera_config = @repo.hiera_config
112
+ hiera_config.each do |setting,value|
113
+ if value.is_a?(Hash)
114
+ if value.has_key?(:datadir)
115
+ hiera_config[setting][:datadir] = "#{@repo.temp_environmentpath}/#{@config.environment}/#{value[:datadir]}"
116
+ end
117
+ end
118
+ end
119
+ File.write("#{@repo.temp_environmentpath}/#{@config.environment}/hiera.yaml",hiera_config.to_yaml)
120
+
121
+ @config.create_fixtures_symlinks(@repo)
122
+ end
123
+
124
+ task :controlrepo_autotest_spec do
125
+ Dir.chdir(@repo.tempdir) do
126
+ #`bundle install --binstubs`
127
+ #`bin/rake spec_standalone`
128
+ exec("bundle install --binstubs; bundle exec rake spec_standalone")
129
+ end
130
+ # TODO: Look at how this outputs and see if it needs to be improved
131
+ end
132
+
133
+ task :controlrepo_spec => [
134
+ :controlrepo_autotest_prep,
135
+ :controlrepo_autotest_spec
136
+ ]
137
+
138
+ task :r10k_deploy_local do
139
+ require 'controlrepo/testconfig'
140
+ @repo = Controlrepo.new
141
+ @config = Controlrepo::TestConfig.new("#{repo.spec_dir}/controlrepo.yaml")
142
+
143
+ # Deploy r10k to a temp dir
144
+ config.r10k_deploy_local(repo)
145
+ end
146
+
147
+ # TODO: We could use rspec's tagging abilities to choose which if the acceptance tests to run.
148
+
149
+
150
+