puppet-retrospec 0.11.0 → 0.12.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.
- 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
|
+
|