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,124 @@
1
+ class Controlrepo
2
+ class Test
3
+ @@all =[]
4
+
5
+ attr_accessor :nodes
6
+ attr_accessor :classes
7
+ attr_accessor :options
8
+
9
+ # This can accept a bunch of stuff. It can accept nodes, classes or groups anywhere
10
+ # it will then detect them and expand them out into their respective objects so that
11
+ # we just end up with a list of nodes and classes
12
+ def initialize(on_these,test_this,options = {})
13
+ @nodes = []
14
+ @classes = []
15
+ @options = options
16
+
17
+ # Get the nodes we are working on
18
+ if Controlrepo::Group.find(on_these)
19
+ @nodes << Controlrepo::Group.find(on_these).members
20
+ elsif Controlrepo::Node.find(on_these)
21
+ @nodes << Controlrepo::Node.find(on_these)
22
+ else
23
+ raise "#{on_these} was not found in the list of nodes or groups!"
24
+ end
25
+
26
+ @nodes.flatten!
27
+
28
+ # Check that our nodes list contains only nodes
29
+ raise "#{@nodes} contained a non-node object." unless @nodes.all? { |item| item.is_a?(Controlrepo::Node) }
30
+
31
+ if test_this.is_a?(String)
32
+ # If we have just given a string then grab all the classes it corresponds to
33
+ if Controlrepo::Group.find(test_this)
34
+ @classes << Controlrepo::Group.find(test_this).members
35
+ elsif Controlrepo::Class.find(test_this)
36
+ @classes << Controlrepo::Class.find(test_this)
37
+ else
38
+ raise "#{test_this} was not found in the list of classes or groups!"
39
+ end
40
+ @classes.flatten!
41
+ elsif test_this.is_a?(Hash)
42
+ # If it is a hash we need to get creative
43
+
44
+ # Get all of the included classes and add them
45
+ if Controlrepo::Group.find(test_this['include'])
46
+ @classes << Controlrepo::Group.find(test_this['include']).members
47
+ elsif Controlrepo::Class.find(test_this['include'])
48
+ @classes << Controlrepo::Class.find(test_this['include'])
49
+ else
50
+ raise "#{test_this['include']} was not found in the list of classes or groups!"
51
+ end
52
+ @classes.flatten!
53
+
54
+ # Then remove any excluded ones
55
+ if Controlrepo::Group.find(test_this['exclude'])
56
+ Controlrepo::Group.find(test_this['exclude']).members.each do |clarse|
57
+ @classes.delete(clarse)
58
+ end
59
+ elsif Controlrepo::Class.find(test_this['exclude'])
60
+ @classes.delete(Controlrepo::Class.find(test_this['exclude']))
61
+ else
62
+ raise "#{test_this['exclude']} was not found in the list of classes or groups!"
63
+ end
64
+ elsif test_this.is_a?(Controlrepo::Class)
65
+ @classes << test_this
66
+ end
67
+ end
68
+
69
+ def eql?(other)
70
+ (@nodes.sort.eql?(other.nodes.sort)) and (@classes.sort.eql?(other.classes.sort))
71
+ end
72
+
73
+ def to_s
74
+ class_msg = ""
75
+ node_msg = ""
76
+ if classes.count > 1
77
+ class_msg = "#{classes.count}_classes"
78
+ else
79
+ class_msg = classes[0].name
80
+ end
81
+
82
+ if nodes.count > 1
83
+ node_msg = "#{nodes.count}_nodes"
84
+ else
85
+ node_msg = nodes[0].name
86
+ end
87
+
88
+ "#{class_msg}_on_#{node_msg}"
89
+ end
90
+
91
+ def self.deduplicate(tests)
92
+ # This should take an array of tests and remove any duplicates from them
93
+
94
+ # this will be an array of arrays, or maybe hashes
95
+ combinations = []
96
+ new_tests = []
97
+ tests.each do |test|
98
+ test.nodes.each do |node|
99
+ test.classes.each do |cls|
100
+ combo = {node => cls}
101
+ unless combinations.member?(combo)
102
+ combinations << combo
103
+ new_tests << Controlrepo::Test.new(node,cls,test.options)
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ # The array that this returns should be ephemeral, it does not
110
+ # represent anything defined in a controlrepo and should just
111
+ # be passed into the thing doing the testing and then killed,
112
+ # we don't want too many copies of the same shit going around
113
+ #
114
+ # Actually based on the way things are written I don't think this
115
+ # will duplicated node or class objects, just test objects,
116
+ # everything else is passed by reference
117
+ new_tests
118
+ end
119
+
120
+ def self.all
121
+ @@all
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,136 @@
1
+ require 'controlrepo/class'
2
+ require 'controlrepo/node'
3
+ require 'controlrepo/group'
4
+ require 'controlrepo/test'
5
+
6
+ class Controlrepo
7
+ class TestConfig
8
+ require 'yaml'
9
+
10
+ attr_accessor :classes
11
+ attr_accessor :nodes
12
+ attr_accessor :groups
13
+ attr_accessor :tests
14
+ attr_accessor :environment
15
+
16
+ def initialize(file, environment = 'production')
17
+ begin
18
+ config = YAML.load(File.read(file))
19
+ rescue YAML::ParserError
20
+ raise "Could not parse the YAML file, check that it is valid YAML and that the encoding is correct"
21
+ end
22
+
23
+ @environment = environment
24
+ @classes = []
25
+ @nodes = []
26
+ @groups = []
27
+ @tests = []
28
+
29
+ config['classes'].each { |clarse| @classes << Controlrepo::Class.new(clarse) }
30
+ config['nodes'].each { |node| @nodes << Controlrepo::Node.new(node) }
31
+ config['groups'].each { |name, members| @groups << Controlrepo::Group.new(name, members) }
32
+
33
+ # Add the 'all_classes' and 'all_nodes' default groups
34
+ @groups << Controlrepo::Group.new('all_nodes',@nodes)
35
+ @groups << Controlrepo::Group.new('all_classes',@classes)
36
+
37
+ config['test_matrix'].each do |machines, roles|
38
+ # TODO: Work out some way to set per-test options like idempotency
39
+ @tests << Controlrepo::Test.new(machines,roles)
40
+ end
41
+ end
42
+
43
+ def r10k_deploy_local(repo = Controlrepo.new)
44
+ require 'controlrepo'
45
+ tempdir = Dir.mktmpdir('r10k')
46
+ repo.tempdir = tempdir
47
+
48
+ # Read in the config and change all the directories, then create them
49
+ r10k_config = repo.r10k_config
50
+ r10k_config[:cachedir] = "#{tempdir}#{r10k_config[:cachedir]}"
51
+ FileUtils::mkdir_p(r10k_config[:cachedir])
52
+ r10k_config[:sources].map do |name,source_settings|
53
+ source_settings["basedir"] = "#{tempdir}#{source_settings["basedir"]}"
54
+ FileUtils::mkdir_p(source_settings["basedir"])
55
+ # Yes, I realise this is going to set it many times
56
+ repo.temp_environmentpath = source_settings["basedir"]
57
+ end
58
+ File.write("#{tempdir}/r10k.yaml",r10k_config.to_yaml)
59
+
60
+ # Pull the trigger!
61
+ Dir.chdir(tempdir) do
62
+ `r10k deploy environment #{@environment} -p --color --config #{tempdir}/r10k.yaml --verbose`
63
+ end
64
+
65
+ # Return tempdir for use
66
+ tempdir
67
+ end
68
+
69
+ def write_spec_test(location, test)
70
+ # Use an ERB template to write a spec test
71
+ template_dir = File.expand_path('../../templates',File.dirname(__FILE__))
72
+ spec_template = File.read(File.expand_path('./test_spec.rb.erb',template_dir))
73
+ randomness = (0...6).map { (65 + rand(26)).chr }.join
74
+ File.write("#{location}/#{randomness}_#{test.to_s}_spec.rb",ERB.new(spec_template, nil, '-').result(binding))
75
+ end
76
+
77
+ def write_acceptance_test(location, test)
78
+ template_dir = File.expand_path('../../templates',File.dirname(__FILE__))
79
+ acc_test_template = File.read(File.expand_path('./acceptance_test_spec.rb.erb',template_dir))
80
+ raise 'We only support writing acceptance tests for one node at the moment' unless test.nodes.count == 1
81
+ randomness = (0...6).map { (65 + rand(26)).chr }.join
82
+ File.write("#{location}/#{randomness}_#{test.to_s}_spec.rb",ERB.new(acc_test_template, nil, '-').result(binding))
83
+ end
84
+
85
+ def write_spec_helper_acceptance(location, repo)
86
+ template_dir = File.expand_path('../../templates',File.dirname(__FILE__))
87
+ spec_heler_acc_template = File.read(File.expand_path('./spec_helper_acceptance.rb.erb',template_dir))
88
+ File.write("#{location}/spec_helper_acceptance.rb",ERB.new(spec_heler_acc_template, nil, '-').result(binding))
89
+ end
90
+
91
+ def write_rakefile(location, pattern)
92
+ template_dir = File.expand_path('../../templates',File.dirname(__FILE__))
93
+ rakefile_template = File.read(File.expand_path('./Rakefile.erb',template_dir))
94
+ File.write("#{location}/Rakefile",ERB.new(rakefile_template, nil, '-').result(binding))
95
+ end
96
+
97
+ def write_gemfile(location)
98
+ template_dir = File.expand_path('../../templates',File.dirname(__FILE__))
99
+ gemfile_template = File.read(File.expand_path('./Gemfile.erb',template_dir))
100
+ File.write("#{location}/Gemfile",ERB.new(gemfile_template, nil, '-').result(binding))
101
+ end
102
+
103
+ def write_spec_helper(location, repo)
104
+ environmentpath = repo.temp_environmentpath
105
+ modulepath = repo.config['modulepath']
106
+ modulepath.delete("$basemodulepath")
107
+ modulepath.map! do |path|
108
+ "#{environmentpath}/#{@environment}/#{path}"
109
+ end
110
+ modulepath = modulepath.join(":")
111
+ repo.temp_modulepath = modulepath
112
+
113
+ # Use an ERB template to write a spec test
114
+ template_dir = File.expand_path('../../templates',File.dirname(__FILE__))
115
+ spec_helper_template = File.read(File.expand_path('./spec_helper.rb.erb',template_dir))
116
+ File.write("#{location}/spec_helper.rb",ERB.new(spec_helper_template, nil, '-').result(binding))
117
+ end
118
+
119
+ def create_fixtures_symlinks(repo)
120
+ FileUtils.mkdir_p("#{repo.tempdir}/spec/fixtures/modules")
121
+ repo.temp_modulepath.split(':').each do |path|
122
+ Dir["#{path}/*"].each do |mod|
123
+ modulename = File.basename(mod)
124
+ FileUtils.ln_s(mod, "#{repo.tempdir}/spec/fixtures/modules/#{modulename}")
125
+ end
126
+ end
127
+ end
128
+
129
+ # TODO: Work out the best way to format the output
130
+ # TODO: Look into bundling bundler into the temp dir
131
+ # TODO: Write task for beaker tests *brace yourself* Dont forget about the beaker file you have
132
+ # TODO: Compare the outlout of the beaker helper that I wrote
133
+ # with the output from the templated tests, us ethe better one
134
+ # bearing in minf that beaker has logger options that could help
135
+ end
136
+ end
@@ -0,0 +1,24 @@
1
+ ---
2
+ fixtures:
3
+ <% if symlinks.any? then -%>
4
+ symlinks:
5
+ <% symlinks.each do |link| -%>
6
+ <%= link['name'] %>: <%= link['dir'] %>
7
+ <% end -%>
8
+ <% end -%>
9
+ <% if repositories.any? then -%>
10
+ repositories:
11
+ <% repositories.each do |repo| -%>
12
+ <%= repo['name'] %>:
13
+ repo: <%= repo['repo'] %>
14
+ ref: <%= repo['ref'] %>
15
+ <% end -%>
16
+ <% end -%>
17
+ <% if forge_modules.any? then -%>
18
+ forge_modules:
19
+ <% forge_modules.each do |mod| -%>
20
+ <%= mod['name'] %>:
21
+ repo: <%= mod['repo'] %>
22
+ ref: <%= mod['ref'] %>
23
+ <% end -%>
24
+ <% end -%>
@@ -0,0 +1,5 @@
1
+ gem 'puppet'
2
+ gem 'rspec'
3
+ gem 'rspec-puppet'
4
+ gem 'rake'
5
+ gem 'puppetlabs_spec_helper'
@@ -0,0 +1 @@
1
+ require 'puppetlabs_spec_helper/rake_tasks'
@@ -0,0 +1,18 @@
1
+ require 'spec_helper_acceptance'
2
+
3
+ <% test.nodes.each do |node| -%>
4
+ describe '<%= node.name %>' do
5
+ <% test.classes.each do |cls| %>
6
+ context 'when classified with <%= cls.name %>' do
7
+ role = '<%= cls.name %>'
8
+ apply_manifest(role, :catch_failures => true)
9
+ <% if test.options[:runs_for_idempotency] -%>
10
+ <% (test.options[:runs_for_idempotency] - 1).times do -%>
11
+ apply_manifest(role, :catch_failures => true)
12
+ <% end -%>
13
+ <% end -%>
14
+ apply_manifest(role, :catch_changes => true)
15
+ end
16
+ <% end %>
17
+ end
18
+ <% end %>
@@ -0,0 +1,9 @@
1
+ HOSTS:
2
+ <%= fact_set['fqdn'] %>:
3
+ roles:
4
+ - agent
5
+ type: aio
6
+ platform: <%= platform %>
7
+ box: <%= boxname %>
8
+ box_url: <%= url %>
9
+ hypervisor: vagrant_virtualbox
@@ -0,0 +1,7 @@
1
+ require 'puppetlabs_spec_helper/module_spec_helper'
2
+
3
+ RSpec.configure do |c|
4
+ c.parser = 'future'
5
+ c.module_path = '<%= modulepath %>'
6
+ c.hiera_config = '<%= environmentpath %>/<%= @environment %>/hiera.yaml'
7
+ end
@@ -0,0 +1,24 @@
1
+ require 'beaker-rspec'
2
+
3
+ scp_to hosts, '<%= repo.tempdir %>/etc', '/etc' # Check that this wil work recursively
4
+
5
+ RSpec.configure do |c|
6
+ # Project root
7
+ proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
8
+
9
+ # Readable test descriptions
10
+ c.formatter = :documentation
11
+
12
+ # Configure all nodes in nodeset
13
+ #c.before :suite do
14
+ # # Install module
15
+ # puppet_module_install(:source => proj_root, :module_name => 'virtualbox')
16
+ # hosts.each do |host|
17
+ # on host, puppet('module','install','puppetlabs-stdlib'), { :acceptable_exit_codes => [0,1] }
18
+ # on host, puppet('module','install','puppetlabs-apt'), { :acceptable_exit_codes => [0,1] }
19
+ # on host, puppet('module','install','stahnma-epel'), { :acceptable_exit_codes => [0,1] }
20
+ # on host, puppet('module','install','camptocamp-archive'), { :acceptable_exit_codes => [0,1] }
21
+ # on host, puppet('module','install','darin-zypprepo'), { :acceptable_exit_codes => [0,1] }
22
+ # end
23
+ #end
24
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ <% test.classes.each do |cls| -%>
4
+ describe "<%= cls.name %>" do
5
+ <% test.nodes.each do |node| -%>
6
+ context "using fact set <%= node.name %>" do
7
+ let(:facts) { <%= node.fact_set %> }
8
+ it { should compile }
9
+ end
10
+ <% end -%>
11
+ end
12
+
13
+ <% end -%>
metadata ADDED
@@ -0,0 +1,162 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: controlrepo
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Dylan Ratcliffe
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: beaker-rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-puppet
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: puppet
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: ''
112
+ email:
113
+ - dylan.ratcliffe@puppetlabs.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - README.md
120
+ - controlrepo.gemspec
121
+ - lib/controlrepo.rb
122
+ - lib/controlrepo/beaker.rb
123
+ - lib/controlrepo/class.rb
124
+ - lib/controlrepo/group.rb
125
+ - lib/controlrepo/node.rb
126
+ - lib/controlrepo/rake_tasks.rb
127
+ - lib/controlrepo/test.rb
128
+ - lib/controlrepo/testconfig.rb
129
+ - templates/.fixtures.yml.erb
130
+ - templates/Gemfile.erb
131
+ - templates/Rakefile.erb
132
+ - templates/acceptance_test_spec.rb.erb
133
+ - templates/nodeset.yaml.erb
134
+ - templates/spec_helper.rb.erb
135
+ - templates/spec_helper_acceptance.rb.erb
136
+ - templates/test_spec.rb.erb
137
+ homepage: ''
138
+ licenses:
139
+ - Apache-2.0
140
+ metadata: {}
141
+ post_install_message:
142
+ rdoc_options: []
143
+ require_paths:
144
+ - lib
145
+ required_ruby_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ requirements: []
156
+ rubyforge_project:
157
+ rubygems_version: 2.4.7
158
+ signing_key:
159
+ specification_version: 4
160
+ summary: ''
161
+ test_files: []
162
+ has_rdoc: