onceover 3.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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/Gemfile +6 -0
  4. data/Gemfile.lock +17 -0
  5. data/README.md +504 -0
  6. data/Rakefile +2 -0
  7. data/bin/onceover +17 -0
  8. data/controlrepo.gemspec +38 -0
  9. data/factsets/CentOS-5.11-32.json +263 -0
  10. data/factsets/CentOS-5.11-64.json +263 -0
  11. data/factsets/CentOS-6.6-32.json +305 -0
  12. data/factsets/CentOS-6.6-64.json +342 -0
  13. data/factsets/CentOS-7.0-64.json +352 -0
  14. data/factsets/Debian-6.0.10-32.json +322 -0
  15. data/factsets/Debian-6.0.10-64.json +322 -0
  16. data/factsets/Debian-7.8-32.json +338 -0
  17. data/factsets/Debian-7.8-64.json +338 -0
  18. data/factsets/Ubuntu-12.04-32.json +328 -0
  19. data/factsets/Ubuntu-12.04-64.json +328 -0
  20. data/factsets/Ubuntu-14.04-32.json +337 -0
  21. data/factsets/Ubuntu-14.04-64.json +373 -0
  22. data/factsets/Windows_Server-2008r2-64.json +183 -0
  23. data/factsets/Windows_Server-2012r2-64.json +164 -0
  24. data/lib/onceover/beaker.rb +225 -0
  25. data/lib/onceover/beaker/spec_helper.rb +70 -0
  26. data/lib/onceover/class.rb +29 -0
  27. data/lib/onceover/cli.rb +46 -0
  28. data/lib/onceover/cli/init.rb +31 -0
  29. data/lib/onceover/cli/run.rb +72 -0
  30. data/lib/onceover/cli/show.rb +74 -0
  31. data/lib/onceover/cli/update.rb +48 -0
  32. data/lib/onceover/controlrepo.rb +527 -0
  33. data/lib/onceover/group.rb +85 -0
  34. data/lib/onceover/logger.rb +31 -0
  35. data/lib/onceover/node.rb +44 -0
  36. data/lib/onceover/rake_tasks.rb +113 -0
  37. data/lib/onceover/runner.rb +90 -0
  38. data/lib/onceover/test.rb +157 -0
  39. data/lib/onceover/testconfig.rb +233 -0
  40. data/templates/.fixtures.yml.erb +24 -0
  41. data/templates/Rakefile.erb +6 -0
  42. data/templates/acceptance_test_spec.rb.erb +66 -0
  43. data/templates/controlrepo.yaml.erb +38 -0
  44. data/templates/factsets_README.md.erb +7 -0
  45. data/templates/nodeset.yaml.erb +12 -0
  46. data/templates/pre_conditions_README.md.erb +24 -0
  47. data/templates/spec_helper.rb.erb +16 -0
  48. data/templates/spec_helper_acceptance.rb.erb +1 -0
  49. data/templates/test_spec.rb.erb +34 -0
  50. metadata +345 -0
@@ -0,0 +1,85 @@
1
+ require 'onceover/class'
2
+ require 'onceover/node'
3
+
4
+ class Onceover
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
+
15
+ def initialize(name = nil, members = [])
16
+ @name = name
17
+ @members = []
18
+
19
+ if Onceover::Group.valid_members?(members)
20
+ # If it's already a valid list just chuck it in there
21
+ @members = members
22
+ elsif members.is_a?(Hash)
23
+ # if it's a hash then do subtractive stiff
24
+ @members = Onceover::Group.subtractive_to_list(members)
25
+ else
26
+ # Turn it into a full list
27
+ member_objects = []
28
+
29
+ # This should also handle lists that include groups
30
+ members.each { |member| member_objects << Onceover::TestConfig.find_list(member) }
31
+ member_objects.flatten!
32
+
33
+ # Check that they are all the same type
34
+ unless Onceover::Group.valid_members?(member_objects)
35
+ raise 'Groups must contain either all nodes or all classes. Either there was a mix, or something was spelled wrong'
36
+ end
37
+
38
+ # Smash it into the instance variable
39
+ @members = member_objects
40
+ end
41
+
42
+ # Finally add it to the list of all grops
43
+ @@all << self
44
+ end
45
+
46
+ def self.find(group_name)
47
+ @@all.each do |group|
48
+ if group.name == group_name
49
+ return group
50
+ end
51
+ end
52
+ nil
53
+ end
54
+
55
+ def self.all
56
+ @@all
57
+ end
58
+
59
+ def self.valid_members?(members)
60
+ # Check that they are all the same type
61
+ # Also catch any errors to assume it's invalid
62
+ begin
63
+ if members.all? { |item| item.is_a?(Onceover::Class) }
64
+ return true
65
+ elsif members.all? { |item| item.is_a?(Onceover::Node) }
66
+ return true
67
+ else
68
+ return false
69
+ end
70
+ rescue
71
+ return false
72
+ end
73
+ end
74
+
75
+ def self.subtractive_to_list(subtractive_hash)
76
+ # Take a hash that looks like this:
77
+ # { 'include' => 'somegroup'
78
+ # 'exclude' => 'other'}
79
+ # and return a list of classes/nodes
80
+ include_list = Onceover::TestConfig.find_list(subtractive_hash['include'])
81
+ exclude_list = Onceover::TestConfig.find_list(subtractive_hash['exclude'])
82
+ include_list - exclude_list
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,31 @@
1
+ require 'logging'
2
+
3
+ module Onceover::Logger
4
+ def logger
5
+ unless $logger
6
+ # here we setup a color scheme called 'bright'
7
+ Logging.color_scheme( 'bright',
8
+ :levels => {
9
+ :debug => :cyan,
10
+ :info => :green,
11
+ :warn => :yellow,
12
+ :error => :red,
13
+ :fatal => [:white, :on_red]
14
+ }
15
+ )
16
+
17
+ Logging.appenders.stdout(
18
+ 'stdout',
19
+ :layout => Logging.layouts.pattern(
20
+ :pattern => '%l\t -> %m\n',
21
+ :color_scheme => 'bright'
22
+ )
23
+ )
24
+
25
+ $logger = Logging.logger['Colors']
26
+ $logger.add_appenders 'stdout'
27
+ $logger.level = :warn
28
+ end
29
+ $logger
30
+ end
31
+ end
@@ -0,0 +1,44 @@
1
+ require 'onceover/controlrepo'
2
+
3
+ class Onceover
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 = Onceover::Controlrepo.facts[Onceover::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?(Onceover::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
+ logger.warn "Node #{node_name} not found"
37
+ nil
38
+ end
39
+
40
+ def self.all
41
+ @@all
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,113 @@
1
+ require 'onceover/controlrepo'
2
+ require 'pathname'
3
+
4
+ @repo = nil
5
+ @config = nil
6
+
7
+ task :generate_fixtures do
8
+ repo = Onceover::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 = Onceover::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 :controlrepo_details do
28
+ require 'onceover/controlrepo'
29
+ puts Onceover::Controlrepo.new.to_s
30
+ end
31
+
32
+ task :generate_controlrepo_yaml do
33
+ require 'onceover/controlrepo'
34
+ repo = Onceover::Controlrepo.new
35
+ template_dir = File.expand_path('../../templates',File.dirname(__FILE__))
36
+ controlrepo_yaml_template = File.read(File.expand_path('./controlrepo.yaml.erb',template_dir))
37
+ puts ERB.new(controlrepo_yaml_template, nil, '-').result(binding)
38
+ end
39
+
40
+
41
+ task :generate_nodesets do
42
+ require 'onceover/beaker'
43
+ require 'net/http'
44
+ require 'json'
45
+
46
+ repo = Onceover::Controlrepo.new
47
+
48
+ puts "HOSTS:"
49
+
50
+ repo.facts.each do |fact_set|
51
+ node_name = File.basename(repo.facts_files[repo.facts.index(fact_set)],'.json')
52
+ boxname = Onceover::Beaker.facts_to_vagrant_box(fact_set)
53
+ platform = Onceover::Beaker.facts_to_platform(fact_set)
54
+ response = Net::HTTP.get(URI.parse("https://atlas.hashicorp.com/api/v1/box/#{boxname}"))
55
+ url = 'URL goes here'
56
+
57
+ if response =~ /Not Found/i
58
+ comment_out = true
59
+ else
60
+ comment_out = false
61
+ box_info = JSON.parse(response)
62
+ box_info['current_version']['providers'].each do |provider|
63
+ if provider['name'] == 'virtualbox'
64
+ url = provider['original_url']
65
+ end
66
+ end
67
+ end
68
+
69
+ # Use an ERB template
70
+ template_dir = File.expand_path('../../templates',File.dirname(__FILE__))
71
+ fixtures_template = File.read(File.expand_path('./nodeset.yaml.erb',template_dir))
72
+ puts ERB.new(fixtures_template, nil, '-').result(binding)
73
+ end
74
+
75
+ end
76
+
77
+ task :controlrepo_autotest_prep do
78
+ require 'onceover/testconfig'
79
+ require 'onceover/runner'
80
+ @repo = Onceover::Controlrepo.new
81
+ # TODO: This should be getting the location of controlrepo.yaml from @repo
82
+ @config = Onceover::TestConfig.new("#{@repo.spec_dir}/controlrepo.yaml")
83
+
84
+ @runner = Onceover::Runner.new(@repo, @config)
85
+ @runner.prepare!
86
+ end
87
+
88
+ task :controlrepo_autotest_spec do
89
+ @runner.run_spec!
90
+ end
91
+
92
+ task :controlrepo_autotest_acceptance do
93
+ @runner.run_acceptance!
94
+ end
95
+
96
+ task :controlrepo_spec => [
97
+ :controlrepo_autotest_prep,
98
+ :controlrepo_autotest_spec
99
+ ]
100
+
101
+ task :controlrepo_acceptance => [
102
+ :controlrepo_autotest_prep,
103
+ :controlrepo_autotest_acceptance
104
+ ]
105
+
106
+ task :controlrepo_temp_create do
107
+ require 'onceover/testconfig'
108
+ repo = Onceover::Controlrepo.new
109
+ config = Onceover::TestConfig.new("#{repo.spec_dir}/controlrepo.yaml")
110
+ FileUtils.rm_rf(repo.tempdir)
111
+ # Deploy r10k to a temp dir
112
+ config.r10k_deploy_local(repo)
113
+ end
@@ -0,0 +1,90 @@
1
+ class Onceover
2
+ class Runner
3
+ attr_reader :repo
4
+ attr_reader :config
5
+
6
+ def initialize(repo, config, mode = [:spec, :acceptance])
7
+ @repo = repo
8
+ @config = config
9
+ @mode = [mode].flatten
10
+ end
11
+
12
+ def prepare!
13
+ # Deploy the puppetfile
14
+ @config.r10k_deploy_local(@repo)
15
+
16
+ # Remove the entire spec directory to make sure we have
17
+ # all the latest tests
18
+ FileUtils.rm_rf("#{@repo.tempdir}/spec")
19
+
20
+ # Create the other directories we need
21
+ FileUtils.mkdir_p("#{@repo.tempdir}/spec/classes")
22
+ FileUtils.mkdir_p("#{@repo.tempdir}/spec/acceptance/nodesets")
23
+
24
+ # Copy our entire spec directory over
25
+ FileUtils.cp_r("#{@repo.spec_dir}","#{@repo.tempdir}")
26
+
27
+ # Create the Rakefile so that we can take advantage of the existing tasks
28
+ @config.write_rakefile(@repo.tempdir, "spec/classes/**/*_spec.rb")
29
+
30
+ # Create spec_helper.rb
31
+ @config.write_spec_helper("#{@repo.tempdir}/spec",@repo)
32
+
33
+ # Create spec_helper_accpetance.rb
34
+ @config.write_spec_helper_acceptance("#{@repo.tempdir}/spec",@repo)
35
+
36
+ # TODO: Remove all tests that do not match set tags
37
+
38
+ if @mode.include?(:spec)
39
+ # Verify all of the spec tests
40
+ @config.spec_tests.each { |test| @config.verify_spec_test(@repo,test) }
41
+
42
+ # Deduplicate and write the tests (Spec and Acceptance)
43
+ @config.run_filters(Onceover::Test.deduplicate(@config.spec_tests)).each do |test|
44
+ @config.write_spec_test("#{@repo.tempdir}/spec/classes",test)
45
+ end
46
+ end
47
+
48
+ if @mode.include?(:acceptance)
49
+ # Verify all of the acceptance tests
50
+ @config.acceptance_tests.each { |test| @config.verify_acceptance_test(@repo,test) }
51
+
52
+ # Write them out
53
+ @config.write_acceptance_tests("#{@repo.tempdir}/spec/acceptance",@config.run_filters(Onceover::Test.deduplicate(@config.acceptance_tests)))
54
+ end
55
+
56
+ # Parse the current hiera config, modify, and write it to the temp dir
57
+ unless @repo.hiera_config ==nil
58
+ hiera_config = @repo.hiera_config
59
+ hiera_config.each do |setting,value|
60
+ if value.is_a?(Hash)
61
+ if value.has_key?(:datadir)
62
+ hiera_config[setting][:datadir] = "#{@repo.tempdir}/#{@repo.environmentpath}/production/#{value[:datadir]}"
63
+ end
64
+ end
65
+ end
66
+ File.write("#{@repo.tempdir}/#{@repo.environmentpath}/production/hiera.yaml",hiera_config.to_yaml)
67
+ end
68
+
69
+ @config.create_fixtures_symlinks(@repo)
70
+ end
71
+
72
+ def run_spec!
73
+ Dir.chdir(@repo.tempdir) do
74
+ #`bundle install --binstubs`
75
+ #`bin/rake spec_standalone`
76
+ logger.debug "Running bundle exec rake spec_standalone from #{@repo.tempdir}"
77
+ exec("bundle exec rake spec_standalone")
78
+ end
79
+ end
80
+
81
+ def run_acceptance!
82
+ Dir.chdir(@repo.tempdir) do
83
+ #`bundle install --binstubs`
84
+ #`bin/rake spec_standalone`
85
+ logger.debug "Running bundle exec rake acceptance from #{@repo.tempdir}"
86
+ exec("bundle exec rake acceptance")
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,157 @@
1
+ class Onceover
2
+ class Test
3
+ @@all =[]
4
+
5
+ attr_accessor :nodes
6
+ attr_accessor :classes
7
+ attr_accessor :test_config
8
+ attr_reader :default_test_config
9
+ attr_reader :tags
10
+
11
+ # This can accept a bunch of stuff. It can accept nodes, classes or groups anywhere
12
+ # it will then detect them and expand them out into their respective objects so that
13
+ # we just end up with a list of nodes and classes
14
+ #def initialize(on_this,test_config['classes'],options = {})
15
+ def initialize(on_this,test_this,test_config)
16
+
17
+ @default_test_config = {
18
+ 'check_idempotency' => true,
19
+ 'runs_before_idempotency' => 1
20
+ }
21
+
22
+ # Add defaults if they do not exist
23
+ test_config = @default_test_config.merge(test_config)
24
+
25
+ @nodes = []
26
+ @classes = []
27
+ @test_config = test_config
28
+ @test_config.delete('classes') # remove classes from the config
29
+ @tags = @test_config['tags']
30
+
31
+
32
+ # Make sure that tags are an array
33
+ @test_config['tags'] = [@test_config['tags']].flatten if @test_config['tags']
34
+
35
+ # Get the nodes we are working on
36
+ if Onceover::Group.find(on_this)
37
+ @nodes << Onceover::Group.find(on_this).members
38
+ elsif Onceover::Node.find(on_this)
39
+ @nodes << Onceover::Node.find(on_this)
40
+ else
41
+ raise "#{on_this} was not found in the list of nodes or groups!"
42
+ end
43
+
44
+ @nodes.flatten!
45
+
46
+ # Check that our nodes list contains only nodes
47
+ raise "#{@nodes} contained a non-node object." unless @nodes.all? { |item| item.is_a?(Onceover::Node) }
48
+
49
+ if test_this.is_a?(String)
50
+ # If we have just given a string then grab all the classes it corresponds to
51
+ if Onceover::Group.find(test_this)
52
+ @classes << Onceover::Group.find(test_this).members
53
+ elsif Onceover::Class.find(test_this)
54
+ @classes << Onceover::Class.find(test_this)
55
+ else
56
+ raise "#{test_this} was not found in the list of classes or groups!"
57
+ end
58
+ @classes.flatten!
59
+ elsif test_this.is_a?(Hash)
60
+ # If it is a hash we need to get creative
61
+
62
+ # Get all of the included classes and add them
63
+ if Onceover::Group.find(test_this['include'])
64
+ @classes << Onceover::Group.find(test_this['include']).members
65
+ elsif Onceover::Class.find(test_this['include'])
66
+ @classes << Onceover::Class.find(test_this['include'])
67
+ else
68
+ raise "#{test_this['include']} was not found in the list of classes or groups!"
69
+ end
70
+ @classes.flatten!
71
+
72
+ # Then remove any excluded ones
73
+ if Onceover::Group.find(test_this['exclude'])
74
+ Onceover::Group.find(test_this['exclude']).members.each do |clarse|
75
+ @classes.delete(clarse)
76
+ end
77
+ elsif Onceover::Class.find(test_this['exclude'])
78
+ @classes.delete(Onceover::Class.find(test_this['exclude']))
79
+ else
80
+ raise "#{test_this['exclude']} was not found in the list of classes or groups!"
81
+ end
82
+ elsif test_this.is_a?(Onceover::Class)
83
+ @classes << test_this
84
+ end
85
+ end
86
+
87
+ def eql?(other)
88
+ (@nodes.sort.eql?(other.nodes.sort)) and (@classes.sort.eql?(other.classes.sort))
89
+ end
90
+
91
+ def to_s
92
+ class_msg = ""
93
+ node_msg = ""
94
+ if classes.count > 1
95
+ class_msg = "#{classes.count}_classes"
96
+ else
97
+ class_msg = classes[0].name
98
+ end
99
+
100
+ if nodes.count > 1
101
+ node_msg = "#{nodes.count}_nodes"
102
+ else
103
+ node_msg = nodes[0].name
104
+ end
105
+
106
+ "#{class_msg}_on_#{node_msg}"
107
+ end
108
+
109
+ def self.deduplicate(tests)
110
+ require 'deep_merge'
111
+ # This should take an array of tests and remove any duplicates from them
112
+
113
+ # this will be an array of arrays, or maybe hashes
114
+ combinations = []
115
+ new_tests = []
116
+ tests.each do |test|
117
+ test.nodes.each do |node|
118
+ test.classes.each do |cls|
119
+ combo = {node => cls}
120
+ if combinations.member?(combo)
121
+
122
+ # Find the right test object:
123
+ relevant_test = new_tests[new_tests.index do |a|
124
+ a.nodes[0] == node and a.classes[0] == cls
125
+ end]
126
+
127
+ # Delete all default values in the current options hash
128
+ test.test_config.delete_if do |key,value|
129
+ test.default_test_config[key] == value
130
+ end
131
+
132
+ # Merge the non-default options right on in there
133
+ relevant_test.test_config.deep_merge!(test.test_config)
134
+ else
135
+ combinations << combo
136
+ new_tests << Onceover::Test.new(node,cls,test.test_config)
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ # The array that this returns should be ephemeral, it does not
143
+ # represent anything defined in a controlrepo and should just
144
+ # be passed into the thing doing the testing and then killed,
145
+ # we don't want too many copies of the same shit going around
146
+ #
147
+ # Actually based on the way things are written I don't think this
148
+ # will duplicated node or class objects, just test objects,
149
+ # everything else is passed by reference
150
+ new_tests
151
+ end
152
+
153
+ def self.all
154
+ @@all
155
+ end
156
+ end
157
+ end