pdk-akerl 1.8.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +826 -0
  3. data/LICENSE +201 -0
  4. data/README.md +133 -0
  5. data/exe/pdk +10 -0
  6. data/lib/pdk.rb +10 -0
  7. data/lib/pdk/answer_file.rb +121 -0
  8. data/lib/pdk/cli.rb +113 -0
  9. data/lib/pdk/cli/build.rb +76 -0
  10. data/lib/pdk/cli/bundle.rb +42 -0
  11. data/lib/pdk/cli/convert.rb +41 -0
  12. data/lib/pdk/cli/errors.rb +23 -0
  13. data/lib/pdk/cli/exec.rb +246 -0
  14. data/lib/pdk/cli/exec_group.rb +67 -0
  15. data/lib/pdk/cli/module.rb +14 -0
  16. data/lib/pdk/cli/module/build.rb +14 -0
  17. data/lib/pdk/cli/module/generate.rb +45 -0
  18. data/lib/pdk/cli/new.rb +17 -0
  19. data/lib/pdk/cli/new/class.rb +32 -0
  20. data/lib/pdk/cli/new/defined_type.rb +30 -0
  21. data/lib/pdk/cli/new/module.rb +41 -0
  22. data/lib/pdk/cli/new/provider.rb +27 -0
  23. data/lib/pdk/cli/new/task.rb +31 -0
  24. data/lib/pdk/cli/test.rb +12 -0
  25. data/lib/pdk/cli/test/unit.rb +88 -0
  26. data/lib/pdk/cli/update.rb +32 -0
  27. data/lib/pdk/cli/util.rb +193 -0
  28. data/lib/pdk/cli/util/command_redirector.rb +26 -0
  29. data/lib/pdk/cli/util/interview.rb +63 -0
  30. data/lib/pdk/cli/util/option_normalizer.rb +53 -0
  31. data/lib/pdk/cli/util/option_validator.rb +56 -0
  32. data/lib/pdk/cli/validate.rb +124 -0
  33. data/lib/pdk/generate.rb +11 -0
  34. data/lib/pdk/generate/defined_type.rb +49 -0
  35. data/lib/pdk/generate/module.rb +318 -0
  36. data/lib/pdk/generate/provider.rb +82 -0
  37. data/lib/pdk/generate/puppet_class.rb +48 -0
  38. data/lib/pdk/generate/puppet_object.rb +288 -0
  39. data/lib/pdk/generate/task.rb +86 -0
  40. data/lib/pdk/i18n.rb +4 -0
  41. data/lib/pdk/logger.rb +28 -0
  42. data/lib/pdk/module.rb +21 -0
  43. data/lib/pdk/module/build.rb +214 -0
  44. data/lib/pdk/module/convert.rb +209 -0
  45. data/lib/pdk/module/metadata.rb +193 -0
  46. data/lib/pdk/module/templatedir.rb +313 -0
  47. data/lib/pdk/module/update.rb +111 -0
  48. data/lib/pdk/module/update_manager.rb +210 -0
  49. data/lib/pdk/report.rb +112 -0
  50. data/lib/pdk/report/event.rb +357 -0
  51. data/lib/pdk/template_file.rb +89 -0
  52. data/lib/pdk/tests/unit.rb +213 -0
  53. data/lib/pdk/util.rb +271 -0
  54. data/lib/pdk/util/bundler.rb +253 -0
  55. data/lib/pdk/util/filesystem.rb +12 -0
  56. data/lib/pdk/util/git.rb +74 -0
  57. data/lib/pdk/util/puppet_version.rb +242 -0
  58. data/lib/pdk/util/ruby_version.rb +147 -0
  59. data/lib/pdk/util/vendored_file.rb +88 -0
  60. data/lib/pdk/util/version.rb +42 -0
  61. data/lib/pdk/util/windows.rb +13 -0
  62. data/lib/pdk/util/windows/api_types.rb +57 -0
  63. data/lib/pdk/util/windows/file.rb +36 -0
  64. data/lib/pdk/util/windows/string.rb +16 -0
  65. data/lib/pdk/validate.rb +14 -0
  66. data/lib/pdk/validate/base_validator.rb +209 -0
  67. data/lib/pdk/validate/metadata/metadata_json_lint.rb +86 -0
  68. data/lib/pdk/validate/metadata/metadata_syntax.rb +109 -0
  69. data/lib/pdk/validate/metadata_validator.rb +30 -0
  70. data/lib/pdk/validate/puppet/puppet_lint.rb +67 -0
  71. data/lib/pdk/validate/puppet/puppet_syntax.rb +112 -0
  72. data/lib/pdk/validate/puppet_validator.rb +30 -0
  73. data/lib/pdk/validate/ruby/rubocop.rb +77 -0
  74. data/lib/pdk/validate/ruby_validator.rb +29 -0
  75. data/lib/pdk/validate/tasks/metadata_lint.rb +126 -0
  76. data/lib/pdk/validate/tasks/name.rb +88 -0
  77. data/lib/pdk/validate/tasks_validator.rb +33 -0
  78. data/lib/pdk/version.rb +4 -0
  79. data/locales/config.yaml +21 -0
  80. data/locales/pdk.pot +1283 -0
  81. metadata +304 -0
@@ -0,0 +1,48 @@
1
+ require 'pdk/generate/puppet_object'
2
+
3
+ module PDK
4
+ module Generate
5
+ class PuppetClass < PuppetObject
6
+ OBJECT_TYPE = :class
7
+
8
+ # Prepares the data needed to render the new Puppet class template.
9
+ #
10
+ # @return [Hash{Symbol => Object}] a hash of information that will be
11
+ # provided to the class and class spec templates during rendering.
12
+ def template_data
13
+ data = { name: object_name }
14
+
15
+ data
16
+ end
17
+
18
+ # Calculates the path to the .pp file that the new class will be written
19
+ # to.
20
+ #
21
+ # @return [String] the path where the new class will be written.
22
+ def target_object_path
23
+ @target_pp_path ||= begin
24
+ class_name_parts = object_name.split('::')[1..-1]
25
+ class_name_parts << 'init' if class_name_parts.empty?
26
+
27
+ "#{File.join(module_dir, 'manifests', *class_name_parts)}.pp"
28
+ end
29
+ end
30
+
31
+ # Calculates the path to the file where the tests for the new class will
32
+ # be written.
33
+ #
34
+ # @return [String] the path where the tests for the new class will be
35
+ # written.
36
+ def target_spec_path
37
+ @target_spec_path ||= begin
38
+ class_name_parts = object_name.split('::')
39
+
40
+ # drop the module name if the object name contains multiple parts
41
+ class_name_parts.delete_at(0) if class_name_parts.length > 1
42
+
43
+ "#{File.join(module_dir, 'spec', 'classes', *class_name_parts)}_spec.rb"
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,288 @@
1
+ require 'fileutils'
2
+
3
+ require 'pdk'
4
+ require 'pdk/logger'
5
+ require 'pdk/module/metadata'
6
+ require 'pdk/module/templatedir'
7
+ require 'pdk/template_file'
8
+ require 'pdk/util/filesystem'
9
+
10
+ module PDK
11
+ module Generate
12
+ class PuppetObject
13
+ attr_reader :module_dir
14
+ attr_reader :object_name
15
+ attr_reader :options
16
+
17
+ # Initialises the PDK::Generate::PuppetObject object.
18
+ #
19
+ # In general, this object should never be instantiated directly. Instead,
20
+ # one of the subclasses should be used e.g. PDK::Generate::Klass.
21
+ #
22
+ # New subclasses generally only need to inherit this class, set the
23
+ # OBJECT_TYPE constant and implement the {#template_data},
24
+ # {#target_object_path} and {#target_spec_path} methods.
25
+ #
26
+ # @param module_dir [String] The path to the module directory that the
27
+ # will contain the object.
28
+ # @param object_name [String] The name of the object.
29
+ # @param options [Hash{Symbol => Object}]
30
+ #
31
+ # @api public
32
+ def initialize(module_dir, object_name, options = {})
33
+ @module_dir = module_dir
34
+ @options = options
35
+ @object_name = object_name
36
+
37
+ if [:class, :defined_type].include?(object_type) # rubocop:disable Style/GuardClause
38
+ object_name_parts = object_name.split('::')
39
+
40
+ @object_name = if object_name_parts.first == module_name
41
+ object_name
42
+ else
43
+ [module_name, object_name].join('::')
44
+ end
45
+ end
46
+ end
47
+
48
+ # @abstract Subclass and implement {#template_data} to provide data to
49
+ # the templates during rendering. Implementations of this method should
50
+ # return a Hash!{Symbol => Object}.
51
+ def template_data
52
+ raise NotImplementedError
53
+ end
54
+
55
+ # @abstract Subclass and implement {#target_object_path}. Implementations
56
+ # of this method should return a String containing the destination path
57
+ # of the object being generated.
58
+ def target_object_path
59
+ raise NotImplementedError
60
+ end
61
+
62
+ # @abstract Subclass and implement {#target_type_path}. Implementations
63
+ # of this method should return a String containing the destination path
64
+ # of the additional object file being generated.
65
+ # @return [String] returns nil if there is no additional object file
66
+ def target_type_path
67
+ nil
68
+ end
69
+
70
+ # @abstract Subclass and implement {#target_spec_path}. Implementations
71
+ # of this method should return a String containing the destination path
72
+ # of the tests for the object being generated.
73
+ def target_spec_path
74
+ raise NotImplementedError
75
+ end
76
+
77
+ # @abstract Subclass and implement {#target_type_spec_path}. Implementations
78
+ # of this method should return a String containing the destination path
79
+ # of the tests for the object being generated.
80
+ def target_type_spec_path
81
+ nil
82
+ end
83
+
84
+ # Retrieves the type of the object being generated, e.g. :class,
85
+ # :defined_type, etc. This is specified in the subclass' OBJECT_TYPE
86
+ # constant.
87
+ #
88
+ # @return [Symbol] the type of the object being generated.
89
+ #
90
+ # @api private
91
+ def object_type
92
+ self.class::OBJECT_TYPE
93
+ end
94
+
95
+ # Check preconditions of this template group. By default this only makes sure that the target files do not
96
+ # already exist. Override this (and call super) to add your own preconditions.
97
+ #
98
+ # @raise [PDK::CLI::ExitWithError] if the target files already exist.
99
+ #
100
+ # @api public
101
+ def check_preconditions
102
+ [target_object_path, target_type_path, target_spec_path, target_type_spec_path].compact.each do |target_file|
103
+ next unless File.exist?(target_file)
104
+
105
+ raise PDK::CLI::ExitWithError, _("Unable to generate %{object_type}; '%{file}' already exists.") % {
106
+ file: target_file,
107
+ object_type: object_type,
108
+ }
109
+ end
110
+ end
111
+
112
+ # Check that the templates can be rendered. Find an appropriate template
113
+ # and create the target files from the template. This is the main entry
114
+ # point for the class.
115
+ #
116
+ # @raise [PDK::CLI::ExitWithError] if the target files already exist.
117
+ # @raise [PDK::CLI::FatalError] (see #render_file)
118
+ #
119
+ # @api public
120
+ def run
121
+ check_preconditions
122
+
123
+ with_templates do |template_path, config_hash|
124
+ data = template_data.merge(configs: config_hash)
125
+
126
+ render_file(target_object_path, template_path[:object], data)
127
+ render_file(target_type_path, template_path[:type], data) if template_path[:type]
128
+ render_file(target_spec_path, template_path[:spec], data) if template_path[:spec]
129
+ render_file(target_type_spec_path, template_path[:type_spec], data) if template_path[:type_spec]
130
+ end
131
+ end
132
+
133
+ # Render a file using the provided template and write it to disk.
134
+ #
135
+ # @param dest_path [String] The path that the rendered file should be
136
+ # written to. Any necessary directories will be automatically created.
137
+ # @param template_path [String] The path on disk to the file containing
138
+ # the template.
139
+ # @param data [Hash{Object => Object}] The data to be provided to the
140
+ # template when rendering.
141
+ #
142
+ # @raise [PDK::CLI::FatalError] if the parent directories to `dest_path`
143
+ # do not exist and could not be created.
144
+ # @raise [PDK::CLI::FatalError] if the rendered file could not be written
145
+ # to `dest_path`.
146
+ #
147
+ # @return [void]
148
+ #
149
+ # @api private
150
+ def render_file(dest_path, template_path, data)
151
+ write_file(dest_path) do
152
+ PDK::TemplateFile.new(template_path, data).render
153
+ end
154
+ end
155
+
156
+ # Write the result of the block to disk.
157
+ #
158
+ # @param dest_path [String] The path that the rendered file should be
159
+ # written to. Any necessary directories will be automatically created.
160
+ # @param &block [String] The content to be written
161
+ #
162
+ # @raise [PDK::CLI::FatalError] if the parent directories to `dest_path`
163
+ # do not exist and could not be created.
164
+ # @raise [PDK::CLI::FatalError] if the rendered file could not be written
165
+ # to `dest_path`.
166
+ #
167
+ # @return [void]
168
+ #
169
+ # @api private
170
+ def write_file(dest_path)
171
+ PDK.logger.info(_("Creating '%{file}' from template.") % { file: dest_path })
172
+
173
+ file_content = yield
174
+
175
+ begin
176
+ FileUtils.mkdir_p(File.dirname(dest_path))
177
+ rescue SystemCallError => e
178
+ raise PDK::CLI::FatalError, _("Unable to create directory '%{path}': %{message}") % {
179
+ path: File.dirname(dest_path),
180
+ message: e.message,
181
+ }
182
+ end
183
+
184
+ PDK::Util::Filesystem.write_file(dest_path, file_content)
185
+ rescue SystemCallError => e
186
+ raise PDK::CLI::FatalError, _("Unable to write to file '%{path}': %{message}") % {
187
+ path: dest_path,
188
+ message: e.message,
189
+ }
190
+ end
191
+
192
+ # Search the possible template directories in order of preference to find
193
+ # a template that can be used to render a new object of the specified
194
+ # type.
195
+ #
196
+ # @yieldparam template_paths [Hash{Symbol => String}] :object contains
197
+ # the path on disk to the template file for the object, :spec contains
198
+ # the path on disk to the template file for the tests for the object
199
+ # (if it exists).
200
+ # @yieldparam config_hash [Hash{Object => Object}] the contents of the
201
+ # :global key in the config_defaults.yml file.
202
+ #
203
+ # @raise [PDK::CLI::FatalError] if no suitable template could be found.
204
+ #
205
+ # @api private
206
+ def with_templates
207
+ templates.each do |template|
208
+ if template[:url].nil?
209
+ PDK.logger.debug(_('No %{dir_type} template specified; trying next template directory.') % { dir_type: template[:type] })
210
+ next
211
+ end
212
+
213
+ PDK::Module::TemplateDir.new(template[:url]) do |template_dir|
214
+ template_paths = template_dir.object_template_for(object_type)
215
+
216
+ if template_paths
217
+ config_hash = template_dir.object_config
218
+ yield template_paths, config_hash
219
+ # TODO: refactor to a search-and-execute form instead
220
+ return # work is done # rubocop:disable Lint/NonLocalExitFromIterator
221
+ elsif template[:allow_fallback]
222
+ PDK.logger.debug(_('Unable to find a %{type} template in %{url}; trying next template directory.') % { type: object_type, url: template[:url] })
223
+ else
224
+ raise PDK::CLI::FatalError, _('Unable to find the %{type} template in %{url}.') % { type: object_type, url: template[:url] }
225
+ end
226
+ end
227
+ end
228
+ rescue ArgumentError => e
229
+ raise PDK::CLI::ExitWithError, e
230
+ end
231
+
232
+ # Provides the possible template directory locations in the order in
233
+ # which they should be searched for a valid template.
234
+ #
235
+ # If a template-url has been specified on in the options hash (e.g. from
236
+ # a CLI parameter), then this template directory will be checked first
237
+ # and we do not fall back to the next possible template directory.
238
+ #
239
+ # If we have not been provided a specific template directory to use, we
240
+ # try the template specified in the module metadata (as set during
241
+ # PDK::Generate::Module) and fall back to the default template if
242
+ # necessary.
243
+ #
244
+ # @return [Array<Hash{Symbol => Object}>] an array of hashes. Each hash
245
+ # contains 3 keys: :type contains a String that describes the template
246
+ # directory, :url contains a String with the URL to the template
247
+ # directory, and :allow_fallback contains a Boolean that specifies if
248
+ # the lookup process should proceed to the next template directory if
249
+ # the template file is not in this template directory.
250
+ #
251
+ # @api private
252
+ def templates
253
+ @templates ||= [
254
+ { type: 'CLI', url: @options[:'template-url'], allow_fallback: false },
255
+ { type: 'metadata', url: module_metadata.data['template-url'], allow_fallback: true },
256
+ { type: 'default', url: PDK::Util.default_template_url, allow_fallback: false },
257
+ ]
258
+ end
259
+
260
+ # Retrieves the name of the module (without the forge username) from the
261
+ # module metadata.
262
+ #
263
+ # @raise (see #module_metadata)
264
+ # @return [String] The name of the module.
265
+ #
266
+ # @api private
267
+ def module_name
268
+ @module_name ||= module_metadata.data['name'].rpartition('-').last
269
+ end
270
+
271
+ # Parses the metadata.json file for the module.
272
+ #
273
+ # @raise [PDK::CLI::FatalError] if the metadata.json file does not exist,
274
+ # can not be read, or contains invalid metadata.
275
+ #
276
+ # @return [PDK::Module::Metadata] the parsed module metadata.
277
+ #
278
+ # @api private
279
+ def module_metadata
280
+ @module_metadata ||= begin
281
+ PDK::Module::Metadata.from_file(File.join(module_dir, 'metadata.json'))
282
+ rescue ArgumentError => e
283
+ raise PDK::CLI::FatalError, _("'%{dir}' does not contain valid Puppet module metadata: %{msg}") % { dir: module_dir, msg: e.message }
284
+ end
285
+ end
286
+ end
287
+ end
288
+ end
@@ -0,0 +1,86 @@
1
+ require 'pdk/generate/puppet_object'
2
+
3
+ module PDK
4
+ module Generate
5
+ class Task < PuppetObject
6
+ OBJECT_TYPE = :task
7
+
8
+ # Prepares the data needed to render the new task template.
9
+ #
10
+ # @return [Hash{Symbol => Object}] a hash of information that will be
11
+ # provided to the task template during rendering. Additionally, this hash
12
+ # (with the :name key removed) makes up the task metadata.
13
+ def template_data
14
+ {
15
+ name: object_name,
16
+ puppet_task_version: 1,
17
+ supports_noop: false,
18
+ description: options.fetch(:description, 'A short description of this task'),
19
+ parameters: {},
20
+ }
21
+ end
22
+
23
+ # Calculates the path to the file where the new task will be written.
24
+ #
25
+ # @return [String] the path to the task file.
26
+ def target_object_path
27
+ @target_object_path ||= File.join(module_dir, 'tasks', "#{task_name}.sh")
28
+ end
29
+
30
+ # Calculates the path to the file where the tests for the new task will
31
+ # be written.
32
+ #
33
+ # @return [nil] as there is currently no test framework for Tasks.
34
+ def target_spec_path
35
+ nil
36
+ end
37
+
38
+ def run
39
+ check_if_task_already_exists
40
+
41
+ super
42
+
43
+ write_task_metadata
44
+ end
45
+
46
+ # Checks that the task has not already been defined with a different
47
+ # extension.
48
+ #
49
+ # @raise [PDK::CLI::ExitWithError] if files with the same name as the
50
+ # task exist in the <module>/tasks/ directory
51
+ #
52
+ # @api private
53
+ def check_if_task_already_exists
54
+ error = _("A task named '%{name}' already exists in this module; defined in %{file}")
55
+ allowed_extensions = %w[.md .conf]
56
+
57
+ Dir.glob(File.join(module_dir, 'tasks', "#{task_name}.*")).each do |file|
58
+ next if allowed_extensions.include?(File.extname(file))
59
+
60
+ raise PDK::CLI::ExitWithError, error % { name: task_name, file: file }
61
+ end
62
+ end
63
+
64
+ # Writes the <module>/tasks/<task_name>.json metadata file for the task.
65
+ #
66
+ # @api private
67
+ def write_task_metadata
68
+ write_file(File.join(module_dir, 'tasks', "#{task_name}.json")) do
69
+ task_metadata = template_data.dup
70
+ task_metadata.delete(:name)
71
+ JSON.pretty_generate(task_metadata)
72
+ end
73
+ end
74
+
75
+ # Calculates the file name of the task files ('init' if the task has the
76
+ # same name as the module, otherwise use the specified task name).
77
+ #
78
+ # @return [String] the base name of the file(s) for the task.
79
+ #
80
+ # @api private
81
+ def task_name
82
+ (object_name == module_name) ? 'init' : object_name
83
+ end
84
+ end
85
+ end
86
+ end
data/lib/pdk/i18n.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'gettext-setup'
2
+
3
+ GettextSetup.initialize(File.absolute_path('../../locales', File.dirname(__FILE__)))
4
+ GettextSetup.negotiate_locale!(GettextSetup.candidate_locales)
data/lib/pdk/logger.rb ADDED
@@ -0,0 +1,28 @@
1
+ require 'logger'
2
+
3
+ module PDK
4
+ def self.logger
5
+ @logger ||= PDK::Logger.new
6
+ end
7
+
8
+ class Logger < ::Logger
9
+ def initialize
10
+ super(STDERR)
11
+
12
+ # TODO: Decide on output format.
13
+ self.formatter = proc do |severity, _datetime, _progname, msg|
14
+ "pdk (#{severity}): #{msg}\n"
15
+ end
16
+
17
+ self.level = ::Logger::INFO
18
+ end
19
+
20
+ def enable_debug_output
21
+ self.level = ::Logger::DEBUG
22
+ end
23
+
24
+ def debug?
25
+ level == ::Logger::DEBUG
26
+ end
27
+ end
28
+ end