puppet-retrospec 0.11.0 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +17 -0
- data/CHANGELOG.md +11 -0
- data/DEVELOPMENT.md +3 -0
- data/Gemfile +10 -9
- data/Gemfile.lock +2 -0
- data/README.md +211 -273
- data/Rakefile +8 -8
- data/VERSION +1 -1
- data/lib/retrospec-puppet.rb +3 -3
- data/lib/retrospec/plugins/v1/plugin/conditional.rb +5 -6
- data/lib/retrospec/plugins/v1/plugin/exceptions.rb +17 -0
- data/lib/retrospec/plugins/v1/plugin/generators.rb +7 -0
- data/lib/retrospec/plugins/v1/plugin/generators/fact_generator.rb +18 -10
- data/lib/retrospec/plugins/v1/plugin/generators/function_generator.rb +187 -0
- data/lib/retrospec/plugins/v1/plugin/generators/module_generator.rb +25 -26
- data/lib/retrospec/plugins/v1/plugin/generators/{facter.rb → parsers/facter.rb} +28 -35
- data/lib/retrospec/plugins/v1/plugin/generators/parsers/function.rb +91 -0
- data/lib/retrospec/plugins/v1/plugin/generators/parsers/type.rb +79 -0
- data/lib/retrospec/plugins/v1/plugin/generators/provider_generator.rb +107 -0
- data/lib/retrospec/plugins/v1/plugin/generators/schema_generator.rb +221 -0
- data/lib/retrospec/plugins/v1/plugin/generators/type_generator.rb +118 -0
- data/lib/retrospec/plugins/v1/plugin/helpers.rb +3 -7
- data/lib/retrospec/plugins/v1/plugin/puppet.rb +141 -60
- data/lib/retrospec/plugins/v1/plugin/puppet_module.rb +29 -26
- data/lib/retrospec/plugins/v1/plugin/resource.rb +6 -7
- data/lib/retrospec/plugins/v1/plugin/spec_object.rb +5 -8
- data/lib/retrospec/plugins/v1/plugin/template_helpers.rb +9 -10
- data/lib/retrospec/plugins/v1/plugin/templates/clone-hook +15 -8
- data/lib/retrospec/plugins/v1/plugin/type_code.rb +4 -4
- data/lib/retrospec/plugins/v1/plugin/variable_store.rb +26 -30
- data/lib/retrospec/plugins/v1/plugin/version.rb +1 -1
- data/puppet-retrospec.gemspec +43 -4
- data/spec/fixtures/facts/oracle_controls.rb +38 -0
- data/spec/fixtures/fixture_modules/required_parameters/manifests/init.pp +8 -0
- data/spec/fixtures/fixture_modules/sample_module/lib/facter/fix_installed.rb +11 -0
- data/spec/fixtures/fixture_modules/sample_module/lib/puppet/functions/awesome_parser.rb +13 -0
- data/spec/fixtures/fixture_modules/sample_module/lib/puppet/functions/reduce.rb +31 -0
- data/spec/fixtures/fixture_modules/sample_module/lib/puppet/parser/functions/bad_sha1.rb +6 -0
- data/spec/fixtures/fixture_modules/sample_module/lib/puppet/parser/functions/defined.rb +94 -0
- data/spec/fixtures/fixture_modules/sample_module/lib/puppet/parser/functions/sha1.rb +6 -0
- data/spec/fixtures/fixture_modules/sample_module/spec/unit/facter/fix_installed_spec.rb +21 -0
- data/spec/fixtures/modules/tomcat/files/.gitkeep +0 -0
- data/spec/fixtures/modules/tomcat/templates/.gitkeep +0 -0
- data/spec/fixtures/modules/tomcat/tests/.gitkeep +0 -0
- data/spec/fixtures/providers/bmc/ipmitool.rb +188 -0
- data/spec/fixtures/providers/bmcuser/ipmitool.rb +140 -0
- data/spec/fixtures/types/bmc.rb +102 -0
- data/spec/fixtures/types/bmcuser.rb +46 -0
- data/spec/fixtures/types/db_opatch.rb +93 -0
- data/spec/integration/retrospec_spec.rb +1 -3
- data/spec/spec_helper.rb +33 -6
- data/spec/unit/conditional_spec.rb +12 -15
- data/spec/unit/generators/fact_generater_spec.rb +49 -17
- data/spec/unit/generators/function_generator_spec.rb +301 -0
- data/spec/unit/generators/function_spec.rb +67 -0
- data/spec/unit/generators/parsers/fact_spec.rb +62 -0
- data/spec/unit/generators/parsers/provider_spec.rb +44 -0
- data/spec/unit/generators/parsers/type_spec.rb +93 -0
- data/spec/unit/generators/provider_generator_spec.rb +120 -0
- data/spec/unit/generators/schema_generator_spec.rb +122 -0
- data/spec/unit/generators/type_generator_spec.rb +173 -0
- data/spec/unit/module_spec.rb +7 -10
- data/spec/unit/plugin_spec.rb +213 -15
- data/spec/unit/puppet-retrospec_spec.rb +81 -100
- data/spec/unit/resource_spec.rb +16 -17
- data/spec/unit/spec_object_spec.rb +46 -0
- data/spec/unit/type_code_spec.rb +9 -11
- data/spec/unit/variable_store_spec.rb +41 -43
- metadata +54 -4
- data/spec/unit/generators/fact_spec.rb +0 -58
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'ostruct'
|
2
|
-
|
3
2
|
# this is required to use when processing erb templates
|
4
3
|
class OpenStruct
|
5
4
|
def get_binding
|
@@ -16,20 +15,14 @@ module Retrospec
|
|
16
15
|
class Execution
|
17
16
|
def self.execute(command, options={})
|
18
17
|
value = {:klass => self.to_s.gsub('Retrospec::Puppet::Generators::', ''),
|
19
|
-
|
18
|
+
:method => :execute, :value => command}
|
20
19
|
Retrospec::Puppet::Generators::Facter.exec_calls[command] = value
|
21
20
|
end
|
22
21
|
end
|
23
22
|
end
|
24
23
|
|
25
|
-
@model = OpenStruct.new(:facts => {})
|
26
|
-
@used_facts = {}
|
27
|
-
@confines = []
|
28
|
-
@methods_defined = []
|
29
|
-
|
30
24
|
def initialize(name, options, &block)
|
31
25
|
@fact_name = name
|
32
|
-
#block.call
|
33
26
|
end
|
34
27
|
|
35
28
|
def self.exec_calls
|
@@ -40,6 +33,10 @@ module Retrospec
|
|
40
33
|
@used_facts ||= {}
|
41
34
|
end
|
42
35
|
|
36
|
+
def self.methods_defined
|
37
|
+
@methods_defined ||= []
|
38
|
+
end
|
39
|
+
|
43
40
|
def self.value(name)
|
44
41
|
used_facts[name] = {:name => name}
|
45
42
|
end
|
@@ -51,12 +48,16 @@ module Retrospec
|
|
51
48
|
end
|
52
49
|
|
53
50
|
def self.method_missing(method_sym, *arguments, &block)
|
54
|
-
|
51
|
+
unless methods_defined.include?(method_sym)
|
52
|
+
methods_defined << method_sym
|
53
|
+
end
|
54
|
+
method_sym
|
55
55
|
end
|
56
56
|
|
57
57
|
def self.setcode(&block)
|
58
58
|
begin
|
59
59
|
block.call
|
60
|
+
rescue Exception => e
|
60
61
|
rescue NameError => e
|
61
62
|
end
|
62
63
|
end
|
@@ -68,49 +69,41 @@ module Retrospec
|
|
68
69
|
# loads the fact into the loader for evaluation
|
69
70
|
# and data collection
|
70
71
|
def self.load_fact(file)
|
71
|
-
@model = OpenStruct.new(:facts => {})
|
72
|
-
|
73
|
-
|
74
|
-
|
72
|
+
@model = OpenStruct.new(:facts => {}, :defined_methods => [], :global_used_facts => {}, :global_used_execs => {})
|
73
|
+
begin
|
74
|
+
proc = Proc.new {}
|
75
|
+
eval(File.read(file), proc.binding, file)
|
76
|
+
rescue LoadError => e
|
77
|
+
puts "Error loading dependency for file: #{file}, skipping".fatal
|
78
|
+
rescue Exception => e
|
79
|
+
puts "Error evaluating file: #{file}, skipping".fatal
|
80
|
+
end
|
81
|
+
@model
|
75
82
|
end
|
76
83
|
|
77
84
|
# every fact will have a Facter.add functionality
|
78
85
|
# this is the startign point to collect all data
|
79
86
|
def self.add(name, options={}, &block)
|
80
|
-
@model.facts[name] = OpenStruct.new(:fact_name => name)
|
81
87
|
# calls the facter.add block
|
82
88
|
# this may call separate confine statements
|
83
|
-
|
84
|
-
@
|
85
|
-
@
|
89
|
+
# for each Facter.add block that gets called we need to reset a few things
|
90
|
+
@confines = {}
|
91
|
+
@used_facts = {}
|
86
92
|
@exec_calls = {}
|
87
93
|
begin
|
88
94
|
block.call
|
95
|
+
rescue Exception => e
|
89
96
|
rescue NameError => e
|
90
97
|
end
|
98
|
+
|
99
|
+
@model.facts[name] = OpenStruct.new(:fact_name => name)
|
91
100
|
@model.facts[name].used_facts = used_facts
|
92
101
|
@model.facts[name].confines = @confines
|
93
|
-
|
94
|
-
@confines = []
|
95
|
-
@model.defined_methods = @methods_defined
|
102
|
+
@model.defined_methods = methods_defined
|
96
103
|
@model.facts[name].exec_calls = exec_calls
|
97
104
|
@model
|
98
105
|
end
|
99
|
-
|
100
|
-
def self.transform_data(data)
|
101
|
-
#ObenStruct.new(:)
|
102
|
-
# {:method_fact=>
|
103
|
-
# {:fact_name=>:method_fact,
|
104
|
-
# :used_facts=>{:is_virtual=>{:name=>:is_virtual}},
|
105
|
-
# :confines=>[{:kernel=>"Linux"}],
|
106
|
-
# :exec_calls=>["which lsb"]},
|
107
|
-
# :global_used_facts=>{},
|
108
|
-
# :global_used_execs=>[],
|
109
|
-
# :defined_methods=>[:default_kernel]}
|
110
|
-
data
|
111
|
-
end
|
112
106
|
end
|
113
|
-
|
114
107
|
end
|
115
108
|
end
|
116
|
-
end
|
109
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'retrospec/plugins/v1/plugin/puppet_module'
|
3
|
+
# this is required to use when processing erb templates
|
4
|
+
class OpenStruct
|
5
|
+
def get_binding
|
6
|
+
binding
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module Retrospec
|
11
|
+
module Puppet
|
12
|
+
module Functions
|
13
|
+
# for puppet 4 functions
|
14
|
+
def self.create_function(func_name, function_base = nil, &block)
|
15
|
+
# the bundled version of puppet with this gem is quite old and is preventing me from creating a function
|
16
|
+
# to get the actual properties of it. For now we can just skip the creation and stub enough functions
|
17
|
+
# to get at the data. However, if we just eval the file we can probably bypass all this code and use class
|
18
|
+
# methods instead
|
19
|
+
#require 'puppet/pops'
|
20
|
+
#f = ::Puppet::Functions.create_function(func_name, function_base, &block)
|
21
|
+
block.call
|
22
|
+
@model.name = func_name
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.load_function(file)
|
26
|
+
begin
|
27
|
+
::Puppet.initialize_settings
|
28
|
+
rescue
|
29
|
+
# do nothing otherwise calling init twice raises an error
|
30
|
+
end
|
31
|
+
@model = OpenStruct.new(:name => File.basename(file, '.rb'), :dispatched_methods => {},
|
32
|
+
:required_methods => [])
|
33
|
+
|
34
|
+
f = eval(File.read(file))
|
35
|
+
@model.required_methods = find_required_methods(@model.name, @model.dispatched_methods.keys)
|
36
|
+
@model
|
37
|
+
end
|
38
|
+
|
39
|
+
# figures out which methods need to be present in the function so that we can create a test for them
|
40
|
+
def self.find_required_methods(name, dispatched_methods=[])
|
41
|
+
if dispatched_methods.empty?
|
42
|
+
[name]
|
43
|
+
else
|
44
|
+
dispatched_methods
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.dispatch(meth_name, &block)
|
49
|
+
@params = [] # reset the variable
|
50
|
+
args = block.call
|
51
|
+
@model.dispatched_methods[meth_name] = {:name => meth_name, :args => args}
|
52
|
+
end
|
53
|
+
|
54
|
+
# this is a catch all method that helps us discover which dsl methods are used
|
55
|
+
def self.method_missing(meth_sym, *arguments, &block)
|
56
|
+
@params << {:name => meth_sym, :args => arguments}
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
module Parser
|
62
|
+
module Functions
|
63
|
+
# for puppet 3 functions
|
64
|
+
def self.newfunction(name, options = {}, &block)
|
65
|
+
options.merge({:name => name})
|
66
|
+
end
|
67
|
+
|
68
|
+
# for puppet 4 functions
|
69
|
+
def self.create_function(func_name, function_base = Function, &block)
|
70
|
+
{:name => func_name }
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.load_function(file)
|
74
|
+
begin
|
75
|
+
::Puppet.initialize_settings
|
76
|
+
rescue
|
77
|
+
# do nothing otherwise calling init twice raises an error
|
78
|
+
end
|
79
|
+
@model = OpenStruct.new(:name => File.basename(file, '.rb'), :arity => nil, :doc => '', :type => nil,
|
80
|
+
:class_methods => [], :instance_methods => [], :options => {})
|
81
|
+
f = eval(File.read(file))
|
82
|
+
@model.name = f[:name]
|
83
|
+
@model.arity = f[:arity]
|
84
|
+
@model.doc = f[:doc]
|
85
|
+
@model.type = f[:type]
|
86
|
+
@model
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
# this is required to use when processing erb templates
|
4
|
+
class OpenStruct
|
5
|
+
def get_binding
|
6
|
+
binding
|
7
|
+
end
|
8
|
+
end
|
9
|
+
# we could also create a new instance of the type and use instances methods to retrieve many things
|
10
|
+
# Puppet::Type.type(type_name) after loading the file
|
11
|
+
module Retrospec
|
12
|
+
module Puppet
|
13
|
+
class Type
|
14
|
+
# loads the type_file and provider_file if given
|
15
|
+
# determines if type or provider is being used and
|
16
|
+
# evals the code
|
17
|
+
# if the provider_file is not loadable we cannot build a full context
|
18
|
+
# for the template to be rendered with so we use a default context instead
|
19
|
+
def self.load_type(type_file, provider_file = nil)
|
20
|
+
if provider_file
|
21
|
+
begin
|
22
|
+
file = provider_file
|
23
|
+
@model = OpenStruct.new(:name => File.basename(file, '.rb'), :file => file,
|
24
|
+
:class_methods => [], :instance_methods => [],
|
25
|
+
:properties => [], :parameters => [])
|
26
|
+
require type_file
|
27
|
+
require provider_file
|
28
|
+
t = eval(File.read(file))
|
29
|
+
@model.parameters = t.resource_type.parameters
|
30
|
+
@model.properties = t.resource_type.validproperties
|
31
|
+
@model.name = t.name
|
32
|
+
@model.class_methods = t.methods(false)
|
33
|
+
@model.instance_methods = t.instance_methods(false)
|
34
|
+
rescue LoadError => e
|
35
|
+
puts "#{e.message}, generating empty file".fatal
|
36
|
+
rescue NameError => e
|
37
|
+
puts "#{e.message}, is this valid provider code?".fatal
|
38
|
+
rescue NoMethodError => e
|
39
|
+
puts "#{e.message}, is this valid provider code?".fatal
|
40
|
+
rescue ::Puppet::Context::UndefinedBindingError => e
|
41
|
+
puts "There was an issue with loading the provider code for #{file}, skipping".fatal
|
42
|
+
end
|
43
|
+
@model
|
44
|
+
else
|
45
|
+
begin
|
46
|
+
require type_file
|
47
|
+
file = type_file
|
48
|
+
@model = OpenStruct.new(:name => File.basename(file, '.rb'), :file => file,
|
49
|
+
:properties => [], :instance_methods => [],
|
50
|
+
:parameters => [], :methods_defined => [])
|
51
|
+
t = eval(File.read(file))
|
52
|
+
@model.name = t.name
|
53
|
+
@model.parameters = t.parameters
|
54
|
+
@model.properties = t.properties.collect(&:name)
|
55
|
+
@model.instance_methods = t.instance_methods(false)
|
56
|
+
@model
|
57
|
+
rescue LoadError => e
|
58
|
+
puts "#{e.message}, generating empty file".fatal
|
59
|
+
rescue NameError => e
|
60
|
+
puts "#{e.message}, is this valid type code?".fatal
|
61
|
+
rescue NoMethodError => e
|
62
|
+
puts "#{e.message}, is this valid type code?".fatal
|
63
|
+
end
|
64
|
+
end
|
65
|
+
@model
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.type(name, _options = {}, &_block)
|
69
|
+
::Puppet::Type.type(name)
|
70
|
+
end
|
71
|
+
|
72
|
+
# I don't know of a better way to get the name of the type
|
73
|
+
def self.newtype(name, _options = {}, &_block)
|
74
|
+
::Puppet::Type.type(name)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require_relative 'parsers/type'
|
2
|
+
require 'facets'
|
3
|
+
|
4
|
+
module Retrospec::Puppet::Generators
|
5
|
+
class ProviderGenerator < Retrospec::Plugins::V1::Plugin
|
6
|
+
attr_reader :template_dir, :context
|
7
|
+
attr_accessor :provider_type
|
8
|
+
|
9
|
+
# retrospec will initaialize this class so its up to you
|
10
|
+
# to set any additional variables you need in the context in feed the templates.
|
11
|
+
def initialize(module_path, spec_object = {})
|
12
|
+
super
|
13
|
+
# below is the Spec Object which serves as a context for template rendering
|
14
|
+
# you will need to initialize this object, so the erb templates can get the binding
|
15
|
+
# the SpecObject can be customized to your liking as its different for every plugin gem.
|
16
|
+
@context = OpenStruct.new(:provider_name => spec_object[:name], :type_name => spec_object[:type])
|
17
|
+
@provider_type = context.type_name
|
18
|
+
end
|
19
|
+
|
20
|
+
# returns the path to the templates
|
21
|
+
# first looks inside the external templates directory for specific file
|
22
|
+
# then looks inside the gem path templates directory, which is really only useful
|
23
|
+
# when developing new templates.
|
24
|
+
def template_dir
|
25
|
+
external_templates = File.expand_path(File.join(config_data[:template_dir], 'providers', 'provider_template.rb.retrospec.erb'))
|
26
|
+
if File.exist?(external_templates)
|
27
|
+
File.join(config_data[:template_dir], 'providers')
|
28
|
+
else
|
29
|
+
File.expand_path(File.join(File.dirname(File.dirname(__FILE__)), 'templates', 'providers'))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# used to display subcommand options to the cli
|
34
|
+
# the global options are passed in for your usage
|
35
|
+
# http://trollop.rubyforge.org
|
36
|
+
# all options here are available in the config passed into config object
|
37
|
+
# returns the parameters
|
38
|
+
def self.run_cli(global_opts, args=ARGV)
|
39
|
+
sub_command_opts = Trollop.options(args) do
|
40
|
+
banner <<-EOS
|
41
|
+
Generates a new provider with the given name.
|
42
|
+
|
43
|
+
EOS
|
44
|
+
opt :name, 'The name of the provider you wish to create', :type => :string, :required => true, :short => '-n'
|
45
|
+
opt :type, 'The type name of the provider', :type => :string, :required => true, :short => '-t'
|
46
|
+
end
|
47
|
+
unless sub_command_opts[:name]
|
48
|
+
Trollop.educate
|
49
|
+
exit 1
|
50
|
+
end
|
51
|
+
plugin_data = global_opts.merge(sub_command_opts)
|
52
|
+
plugin_data
|
53
|
+
end
|
54
|
+
|
55
|
+
def provider_dir
|
56
|
+
File.join(module_path, 'lib', 'puppet', 'provider')
|
57
|
+
end
|
58
|
+
|
59
|
+
def type_dir
|
60
|
+
File.join(module_path, 'lib', 'puppet', 'type')
|
61
|
+
end
|
62
|
+
|
63
|
+
# returns the type file that the provider uses
|
64
|
+
# if the type file does not exist it assumes a core puppet type
|
65
|
+
# because we could potentially dealing with multiple
|
66
|
+
def type_file(p_type = provider_type)
|
67
|
+
if TypeGenerator::CORE_TYPES.include?(p_type)
|
68
|
+
type_file = "puppet/type/#{p_type}.rb"
|
69
|
+
else
|
70
|
+
type_file = File.join(type_dir, "#{p_type}.rb")
|
71
|
+
end
|
72
|
+
type_file
|
73
|
+
end
|
74
|
+
|
75
|
+
def provider_spec_dir
|
76
|
+
File.join(module_path, 'spec', 'unit', 'puppet', 'provider')
|
77
|
+
end
|
78
|
+
|
79
|
+
def provider_name_path
|
80
|
+
File.join(provider_dir, provider_type, "#{provider_name}.rb")
|
81
|
+
end
|
82
|
+
|
83
|
+
def provider_name
|
84
|
+
context.provider_name
|
85
|
+
end
|
86
|
+
|
87
|
+
def generate_provider_files
|
88
|
+
safe_create_template_file(provider_name_path, File.join(template_dir, 'provider_template.rb.retrospec.erb'), context)
|
89
|
+
provider_name_path
|
90
|
+
end
|
91
|
+
|
92
|
+
def generate_provider_spec_files
|
93
|
+
provider_files = Dir.glob(File.join(provider_dir, '**', '*.rb')).sort
|
94
|
+
spec_files = []
|
95
|
+
provider_files.each do |provider_file|
|
96
|
+
t_name = File.basename(File.dirname(provider_file))
|
97
|
+
provider_file_data = Retrospec::Puppet::Type.load_type(type_file(t_name), provider_file)
|
98
|
+
provider_file_data.type_name = t_name # add the provider type
|
99
|
+
# because many facts can be in a single file we want to create a unique file for each fact
|
100
|
+
provider_spec_path = File.join(provider_spec_dir, t_name, "#{provider_file_data.name}_spec.rb")
|
101
|
+
spec_files << provider_spec_path
|
102
|
+
safe_create_template_file(provider_spec_path, File.join(template_dir, 'provider_spec.rb.retrospec.erb'), provider_file_data)
|
103
|
+
end
|
104
|
+
spec_files
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Retrospec
|
4
|
+
module Puppet
|
5
|
+
module Generators
|
6
|
+
class SchemaGenerator < Retrospec::Plugins::V1::Plugin
|
7
|
+
attr_reader :template_dir, :schema
|
8
|
+
attr_accessor :context
|
9
|
+
# retrospec will initilalize this class so its up to you
|
10
|
+
# to set any additional variables you need to get the job done.
|
11
|
+
def initialize(module_path, spec_object = {})
|
12
|
+
super
|
13
|
+
# below is the Spec Object which serves as a context for template rendering
|
14
|
+
# you will need to initialize this object, so the erb templates can get the binding
|
15
|
+
# the SpecObject can be customized to your liking as its different for every plugin gem.
|
16
|
+
@context = OpenStruct.new(:puppet_context => spec_object[:puppet_context])
|
17
|
+
@schema = base_schema
|
18
|
+
@parameter_count = 0
|
19
|
+
end
|
20
|
+
|
21
|
+
# returns the path to the templates
|
22
|
+
# first looks inside the external templates directory for specific file
|
23
|
+
# then looks inside the gem path templates directory, which is really only useful
|
24
|
+
# when developing new templates.
|
25
|
+
def template_dir
|
26
|
+
external_templates = File.expand_path(File.join(config_data[:template_dir], 'schemas', 'schema_file.yaml.retrospec.erb'))
|
27
|
+
if File.exist?(external_templates)
|
28
|
+
File.join(config_data[:template_dir], 'schemas')
|
29
|
+
else
|
30
|
+
File.expand_path(File.join(File.dirname(File.dirname(__FILE__)), 'templates', 'schemas'))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# used to display subcommand options to the cli
|
35
|
+
# the global options are passed in for your usage
|
36
|
+
# http://trollop.rubyforge.org
|
37
|
+
# all options here are available in the config passed into config object
|
38
|
+
# returns the parameters
|
39
|
+
def self.run_cli(global_opts, args=ARGV)
|
40
|
+
sub_command_opts = Trollop.options(args) do
|
41
|
+
banner <<-EOS
|
42
|
+
Generates a kwalify schema based off class parameters.
|
43
|
+
|
44
|
+
EOS
|
45
|
+
end
|
46
|
+
plugin_data = global_opts.merge(sub_command_opts)
|
47
|
+
plugin_data
|
48
|
+
end
|
49
|
+
|
50
|
+
# creates the schema in ruby object format
|
51
|
+
def create_map_content
|
52
|
+
all_hiera_data.each do |name, opts|
|
53
|
+
add_mapping(name => opts)
|
54
|
+
end
|
55
|
+
schema
|
56
|
+
end
|
57
|
+
|
58
|
+
def schema_name
|
59
|
+
puppet_context.module_name || File.basename(module_path)
|
60
|
+
end
|
61
|
+
|
62
|
+
# absolute path of schema file
|
63
|
+
def schema_path
|
64
|
+
File.join(module_path, "#{schema_name}_schema.yaml")
|
65
|
+
end
|
66
|
+
|
67
|
+
# generates the schema file, using a template
|
68
|
+
def generate_schema_file
|
69
|
+
map_content = create_map_content
|
70
|
+
context[:map_content] = map_content.to_yaml
|
71
|
+
context[:raw_maps] = map_content
|
72
|
+
context[:parameter_count] = @parameter_count
|
73
|
+
context[:schema_path] = schema_path
|
74
|
+
context[:schema_name] = schema_name
|
75
|
+
template_file = File.join(template_dir, 'schema_file.yaml.retrospec.erb')
|
76
|
+
safe_create_template_file(schema_path, template_file, context)
|
77
|
+
schema_path
|
78
|
+
end
|
79
|
+
|
80
|
+
# example
|
81
|
+
# "motd::motd_content" => {
|
82
|
+
# "type" => "str",
|
83
|
+
# "required" => false
|
84
|
+
# },
|
85
|
+
def add_mapping(map_value)
|
86
|
+
schema['mapping'].merge!(map_value)
|
87
|
+
end
|
88
|
+
|
89
|
+
def types
|
90
|
+
context.puppet_context.types
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
# generates a class or defination map
|
96
|
+
def generate_type_map(puppet_type)
|
97
|
+
type_name = puppet_type.type.to_s
|
98
|
+
arg_map = {}
|
99
|
+
#arg_map = {'mapping' => {}, 'required' => false, 'type' => 'map' }
|
100
|
+
puppet_type.arguments.each do |k, _v|
|
101
|
+
key = "#{puppet_type.name}::#{k}"
|
102
|
+
kwalify_type = type_map(_v.class)
|
103
|
+
@parameter_count = @parameter_count + 1
|
104
|
+
arg_map.deep_merge!(generate_map(key, _v, kwalify_type))
|
105
|
+
end
|
106
|
+
t_map = {type_name => { 'type' => 'map', 'mapping' => arg_map}}
|
107
|
+
end
|
108
|
+
|
109
|
+
# returns a hash of parameters with their classes
|
110
|
+
def parameter_schema(found_types)
|
111
|
+
parameter_data = {}
|
112
|
+
found_types.each do |t|
|
113
|
+
parameter_data = parameter_data.deep_merge(generate_type_map(t))
|
114
|
+
end
|
115
|
+
parameter_data
|
116
|
+
end
|
117
|
+
|
118
|
+
# gathers all the class parameters that could be used in hiera data mocking
|
119
|
+
# this is the only function that generates the necessary data to be used for schema
|
120
|
+
# creation.
|
121
|
+
def all_hiera_data
|
122
|
+
if @all_hiera_data.nil?
|
123
|
+
@all_hiera_data = parameter_schema(types)
|
124
|
+
end
|
125
|
+
@all_hiera_data
|
126
|
+
end
|
127
|
+
|
128
|
+
def is_required?(item)
|
129
|
+
item.nil?
|
130
|
+
end
|
131
|
+
|
132
|
+
def base_schema
|
133
|
+
@base_schema ||= {
|
134
|
+
"type" => "map",
|
135
|
+
"mapping" => {
|
136
|
+
"hostclass" => {
|
137
|
+
"type" => "map",
|
138
|
+
"mapping" => {}
|
139
|
+
},
|
140
|
+
"definition" => {
|
141
|
+
"type" => "map",
|
142
|
+
"mapping" => {}
|
143
|
+
}
|
144
|
+
}
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
148
|
+
def any_map
|
149
|
+
{"=" => {
|
150
|
+
"type" => "any",
|
151
|
+
"required" => false
|
152
|
+
}
|
153
|
+
}
|
154
|
+
end
|
155
|
+
|
156
|
+
def puppet_context
|
157
|
+
context.puppet_context
|
158
|
+
end
|
159
|
+
|
160
|
+
# conversion table from ruby types to kwalify types
|
161
|
+
def type_table
|
162
|
+
@type_table ||= {
|
163
|
+
'Array'=>"seq",
|
164
|
+
'Hash'=>"map",
|
165
|
+
'String'=>"str",
|
166
|
+
'Integer'=>"int",
|
167
|
+
'Float'=>"float",
|
168
|
+
'Numeric'=>"number",
|
169
|
+
'Date'=>"date",
|
170
|
+
'Time'=>"timestamp",
|
171
|
+
'Object'=>"any",
|
172
|
+
'FalseClass' => 'bool',
|
173
|
+
'TrueClass' => 'bool',
|
174
|
+
'Fixnum' => 'number',
|
175
|
+
'NilClass' => 'any',
|
176
|
+
'Puppet::Parser::AST::Variable' => 'any',
|
177
|
+
'Puppet::Parser::AST::Boolean' => 'bool',
|
178
|
+
'Puppet::Parser::AST::String' => 'str',
|
179
|
+
'Puppet::Parser::AST::ASTHash' => 'map',
|
180
|
+
'Puppet::Parser::AST::ASTArray' => 'seq',
|
181
|
+
}
|
182
|
+
end
|
183
|
+
|
184
|
+
# convert the given class to a kwalify class, defaults to any
|
185
|
+
def type_map(klass)
|
186
|
+
type_table[klass.to_s] || 'any'
|
187
|
+
end
|
188
|
+
|
189
|
+
# given a kwalify key undefined error
|
190
|
+
# this method will produce a map of how the key should be defined
|
191
|
+
# example
|
192
|
+
#"motd::motd_content" => {
|
193
|
+
# "type" => "str",
|
194
|
+
# "required" => false
|
195
|
+
# },
|
196
|
+
def generate_map(key, value, value_type)
|
197
|
+
case value_type
|
198
|
+
when 'seq'
|
199
|
+
{key => {
|
200
|
+
"type" => 'seq',
|
201
|
+
"sequence" => [{'type' => 'str'}],
|
202
|
+
"required" => is_required?(value)
|
203
|
+
}}
|
204
|
+
when 'map'
|
205
|
+
{key => {
|
206
|
+
"type" => 'map',
|
207
|
+
"mapping" => {'=' => {'type' => 'any', 'required' => false}},
|
208
|
+
"required" => is_required?(value)
|
209
|
+
}}
|
210
|
+
else
|
211
|
+
{key => {
|
212
|
+
"type" => value_type,
|
213
|
+
"required" => is_required?(value)
|
214
|
+
}}
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|