pdk 1.11.1 → 1.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.
@@ -0,0 +1,110 @@
1
+ require 'pdk/cli/exec/command'
2
+
3
+ module PDK
4
+ module CLI
5
+ module Exec
6
+ class InteractiveCommand < Command
7
+ def initialize(*argv)
8
+ @argv = argv
9
+
10
+ # Default to running things in the system context.
11
+ @context = :system
12
+
13
+ # Extra environment vars to add to base set.
14
+ @environment = {}
15
+ end
16
+
17
+ def register_spinner(_spinner, _opts = {})
18
+ raise _('This method is not implemented for PDK::CLI::Exec::InteractiveCommand')
19
+ end
20
+
21
+ def add_spinner(_message, _opts = {})
22
+ raise _('This method is not implemented for PDK::CLI::Exec::InteractiveCommand')
23
+ end
24
+
25
+ def timeout
26
+ raise _('This method is not implemented for PDK::CLI::Exec::InteractiveCommand')
27
+ end
28
+
29
+ def timeout=(_val)
30
+ raise _('This method is not implemented for PDK::CLI::Exec::InteractiveCommand')
31
+ end
32
+
33
+ def exec_group=(_val)
34
+ raise _('This method is not implemented for PDK::CLI::Exec::InteractiveCommand')
35
+ end
36
+
37
+ def execute!
38
+ @resolved_env = resolved_env_for_command
39
+
40
+ if [:module, :pwd].include?(context)
41
+ mod_root = PDK::Util.module_root
42
+
43
+ unless mod_root
44
+ raise PDK::CLI::FatalError, _('Current working directory is not part of a module. (No metadata.json was found.)')
45
+ end
46
+
47
+ unless context == :pwd || Dir.pwd == mod_root
48
+ orig_workdir = Dir.pwd
49
+ Dir.chdir(mod_root)
50
+ end
51
+
52
+ result = run_process_in_clean_env!
53
+ else
54
+ result = run_process!
55
+ end
56
+
57
+ {
58
+ interactive: true,
59
+ stdout: nil,
60
+ stderr: nil,
61
+ exit_code: result[:exit_code],
62
+ duration: result[:duration],
63
+ }
64
+ ensure
65
+ Dir.chdir(orig_workdir) if orig_workdir
66
+ end
67
+
68
+ protected
69
+
70
+ # TODO: debug logging
71
+ def run_process!
72
+ command_string = argv.join(' ')
73
+ PDK.logger.debug(_("Executing '%{command}' interactively") % { command: command_string })
74
+
75
+ if context == :module
76
+ PDK.logger.debug(_('Command environment:'))
77
+ @resolved_env.each do |var, val|
78
+ PDK.logger.debug(" #{var}: #{val}")
79
+ end
80
+ end
81
+
82
+ start_time = Time.now
83
+
84
+ system(@resolved_env, *argv)
85
+
86
+ exit_code = child_status.exitstatus
87
+ duration = Time.now - start_time
88
+
89
+ PDK.logger.debug(_("Execution of '%{command}' complete (duration: \
90
+ %{duration_in_seconds}s; exit code: %{exit_code})") %
91
+ {
92
+ command: command_string,
93
+ exit_code: exit_code,
94
+ duration_in_seconds: duration,
95
+ })
96
+
97
+ { exit_code: exit_code, duration: duration }
98
+ end
99
+
100
+ def child_status
101
+ $CHILD_STATUS
102
+ end
103
+
104
+ def stop_spinner
105
+ raise _('This method is not implemented for PDK::CLI::Exec::InteractiveCommand')
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -27,7 +27,7 @@ module PDK
27
27
  end
28
28
 
29
29
  def register(&block)
30
- raise PDK::CLI::FatalError, 'No block registered' unless block_given?
30
+ raise PDK::CLI::FatalError, _('No block registered') unless block_given?
31
31
 
32
32
  @threads_or_procs << if parallel?
33
33
  Thread.new do
@@ -15,3 +15,4 @@ require 'pdk/cli/new/defined_type'
15
15
  require 'pdk/cli/new/module'
16
16
  require 'pdk/cli/new/provider'
17
17
  require 'pdk/cli/new/task'
18
+ require 'pdk/cli/new/transport'
@@ -0,0 +1,25 @@
1
+ module PDK::CLI
2
+ @new_transport_cmd = @new_cmd.define_command do
3
+ name 'transport'
4
+ usage _('transport [options] <name>')
5
+ summary _('[experimental] Create a new ruby transport named <name> using given options')
6
+
7
+ run do |opts, args, _cmd|
8
+ PDK::CLI::Util.ensure_in_module!
9
+
10
+ transport_name = args[0]
11
+ module_dir = Dir.pwd
12
+
13
+ if transport_name.nil? || transport_name.empty?
14
+ puts command.help
15
+ exit 1
16
+ end
17
+
18
+ unless Util::OptionValidator.valid_transport_name?(transport_name)
19
+ raise PDK::CLI::ExitWithError, _("'%{name}' is not a valid transport name") % { name: transport_name }
20
+ end
21
+
22
+ PDK::Generate::Transport.new(module_dir, transport_name, opts).run
23
+ end
24
+ end
25
+ end
@@ -164,7 +164,7 @@ module PDK
164
164
  [puppet_ver_specs, pe_ver_specs].each do |offending|
165
165
  next if offending.empty?
166
166
 
167
- raise PDK::CLI::ExitWithError, _('You cannot specify a %{first} and %{second} at the same time') % {
167
+ raise PDK::CLI::ExitWithError, _('You cannot specify a %{first} and %{second} at the same time.') % {
168
168
  first: pup_dev_spec,
169
169
  second: offending.first,
170
170
  }
@@ -28,6 +28,10 @@ module PDK
28
28
  # Let's assume that only strings similar to module names can actually be resolved by the puppet language.
29
29
  singleton_class.send(:alias_method, :valid_provider_name?, :valid_module_name?)
30
30
 
31
+ # The name has to be a ruby symbol.
32
+ # While overly strict, let's apply the provider and module name rules for consistency.
33
+ singleton_class.send(:alias_method, :valid_transport_name?, :valid_provider_name?)
34
+
31
35
  # Validate a Puppet namespace against the regular expression in the
32
36
  # documentation: https://docs.puppet.com/puppet/4.10/lang_reserved.html#classes-and-defined-resource-types
33
37
  def self.valid_namespace?(string)
@@ -34,7 +34,14 @@ module PDK
34
34
  end
35
35
 
36
36
  def self.bolt_analytics_config
37
- PDK::Config::YAML.new(file: File.expand_path('~/.puppetlabs/bolt/analytics.yaml'))
37
+ file = File.expand_path('~/.puppetlabs/bolt/analytics.yaml')
38
+ PDK::Config::YAML.new(file: file)
39
+ rescue PDK::Config::LoadError => e
40
+ PDK.logger.debug _('Unable to load %{file}: %{message}') % {
41
+ file: file,
42
+ message: e.message,
43
+ }
44
+ PDK::Config::YAML.new
38
45
  end
39
46
 
40
47
  def self.analytics_config_path
@@ -13,7 +13,7 @@ module PDK
13
13
  def self.boolean
14
14
  {
15
15
  proc: ->(value) { [true, false].include?(value) },
16
- message: _('must be a boolean true or false'),
16
+ message: _('must be a boolean: true or false'),
17
17
  }
18
18
  end
19
19
 
@@ -39,7 +39,7 @@ module PDK
39
39
  #
40
40
  # @return [nil]
41
41
  def validate(validator)
42
- raise ArgumentError, _('validator must be a Hash') unless validator.is_a?(Hash)
42
+ raise ArgumentError, _('`validator` must be a Hash') unless validator.is_a?(Hash)
43
43
  raise ArgumentError, _('the :proc key must contain a Proc') unless validator.key?(:proc) && validator[:proc].is_a?(Proc)
44
44
  raise ArgumentError, _('the :message key must contain a String') unless validator.key?(:message) && validator[:message].is_a?(String)
45
45
 
@@ -3,6 +3,7 @@ require 'pdk/generate/module'
3
3
  require 'pdk/generate/provider'
4
4
  require 'pdk/generate/puppet_class'
5
5
  require 'pdk/generate/task'
6
+ require 'pdk/generate/transport'
6
7
  require 'pdk/module/metadata'
7
8
  require 'pdk/module/templatedir'
8
9
 
@@ -258,17 +258,27 @@ module PDK
258
258
 
259
259
  interview.add_questions(questions)
260
260
 
261
- action = File.file?('metadata.json') ? _('update') : _('create')
261
+ if File.file?('metadata.json')
262
+ puts _(
263
+ "\nWe need to update the metadata.json file for this module, so we\'re going to ask you %{count} " \
264
+ "questions.\n",
265
+ ) % {
266
+ count: interview.num_questions,
267
+ }
268
+ else
269
+ puts _(
270
+ "\nWe need to create the metadata.json file for this module, so we\'re going to ask you %{count} " \
271
+ "questions.\n",
272
+ ) % {
273
+ count: interview.num_questions,
274
+ }
275
+ end
276
+
262
277
  puts _(
263
- "\nWe need to %{action} the metadata.json file for this module, so we\'re going to ask you %{count} " \
264
- "questions.\n" \
265
278
  'If the question is not applicable to this module, accept the default option ' \
266
279
  'shown after each question. You can modify any answers at any time by manually updating ' \
267
280
  "the metadata.json file.\n\n",
268
- ) % {
269
- count: interview.num_questions,
270
- action: action,
271
- }
281
+ )
272
282
 
273
283
  answers = interview.run
274
284
 
@@ -72,11 +72,6 @@ module PDK
72
72
  def target_type_spec_path
73
73
  @target_type_spec_path ||= File.join(module_dir, 'spec', 'unit', 'puppet', 'type', object_name) + '_spec.rb'
74
74
  end
75
-
76
- # transform a object name into a ruby class name
77
- def self.class_name_from_object_name(object_name)
78
- object_name.to_s.split('_').map(&:capitalize).join
79
- end
80
75
  end
81
76
  end
82
77
  end
@@ -81,6 +81,13 @@ module PDK
81
81
  nil
82
82
  end
83
83
 
84
+ # @abstract Subclass and implement {#target_device_path}. Implementations
85
+ # of this method should return a String containing the destination path
86
+ # of the device class being generated.
87
+ def target_device_path
88
+ nil
89
+ end
90
+
84
91
  # Retrieves the type of the object being generated, e.g. :class,
85
92
  # :defined_type, etc. This is specified in the subclass' OBJECT_TYPE
86
93
  # constant.
@@ -99,7 +106,7 @@ module PDK
99
106
  #
100
107
  # @api public
101
108
  def check_preconditions
102
- [target_object_path, target_type_path, target_spec_path, target_type_spec_path].compact.each do |target_file|
109
+ [target_object_path, target_type_path, target_device_path, target_spec_path, target_type_spec_path].compact.each do |target_file|
103
110
  next unless File.exist?(target_file)
104
111
 
105
112
  raise PDK::CLI::ExitWithError, _("Unable to generate %{object_type}; '%{file}' already exists.") % {
@@ -125,6 +132,7 @@ module PDK
125
132
 
126
133
  render_file(target_object_path, template_path[:object], data)
127
134
  render_file(target_type_path, template_path[:type], data) if template_path[:type]
135
+ render_file(target_device_path, template_path[:device], data) if template_path[:device]
128
136
  render_file(target_spec_path, template_path[:spec], data) if template_path[:spec]
129
137
  render_file(target_type_spec_path, template_path[:type_spec], data) if template_path[:type_spec]
130
138
  end
@@ -219,9 +227,9 @@ module PDK
219
227
  # TODO: refactor to a search-and-execute form instead
220
228
  return # work is done # rubocop:disable Lint/NonLocalExitFromIterator
221
229
  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] })
230
+ PDK.logger.debug(_('Unable to find a %{type} template in %{url}; trying next template directory.') % { type: object_type, url: template[:uri] })
223
231
  else
224
- raise PDK::CLI::FatalError, _('Unable to find the %{type} template in %{url}.') % { type: object_type, url: template[:url] }
232
+ raise PDK::CLI::FatalError, _('Unable to find the %{type} template in %{url}.') % { type: object_type, url: template[:uri] }
225
233
  end
226
234
  end
227
235
  end
@@ -279,6 +287,11 @@ module PDK
279
287
  raise PDK::CLI::FatalError, _("'%{dir}' does not contain valid Puppet module metadata: %{msg}") % { dir: module_dir, msg: e.message }
280
288
  end
281
289
  end
290
+
291
+ # transform a object name into a ruby class name
292
+ def self.class_name_from_object_name(object_name)
293
+ object_name.to_s.split('_').map(&:capitalize).join
294
+ end
282
295
  end
283
296
  end
284
297
  end
@@ -0,0 +1,87 @@
1
+ require 'pdk/generate/puppet_object'
2
+
3
+ module PDK
4
+ module Generate
5
+ class Transport < PuppetObject
6
+ OBJECT_TYPE = :transport
7
+
8
+ # Prepares the data needed to render the new defined type template.
9
+ #
10
+ # @return [Hash{Symbol => Object}] a hash of information that will be
11
+ # provided to the defined type and defined type spec templates during
12
+ # rendering.
13
+ def template_data
14
+ data = {
15
+ name: object_name,
16
+ transport_class: Transport.class_name_from_object_name(object_name),
17
+ }
18
+
19
+ data
20
+ end
21
+
22
+ def raise_precondition_error(error)
23
+ raise PDK::CLI::ExitWithError, _('%{error}: Creating a transport needs some local configuration in your module.' \
24
+ ' Please follow the docs at https://github.com/puppetlabs/puppet-resource_api#getting-started.') % { error: error }
25
+ end
26
+
27
+ def check_preconditions
28
+ super
29
+ # These preconditions can be removed once the pdk-templates are carrying the puppet-resource_api gem by default, and have switched
30
+ # the default mock_with value.
31
+ sync_path = PDK::Util.find_upwards('.sync.yml')
32
+ if sync_path.nil?
33
+ raise_precondition_error(_('.sync.yml not found'))
34
+ end
35
+ sync = YAML.load_file(sync_path)
36
+ if !sync.is_a? Hash
37
+ raise_precondition_error(_('.sync.yml contents is not a Hash'))
38
+ elsif !sync.key? 'Gemfile'
39
+ raise_precondition_error(_('Gemfile configuration not found'))
40
+ elsif !sync['Gemfile'].key? 'optional'
41
+ raise_precondition_error(_('Gemfile.optional configuration not found'))
42
+ elsif !sync['Gemfile']['optional'].key? ':development'
43
+ raise_precondition_error(_('Gemfile.optional.:development configuration not found'))
44
+ elsif sync['Gemfile']['optional'][':development'].none? { |g| g['gem'] == 'puppet-resource_api' }
45
+ raise_precondition_error(_('puppet-resource_api not found in the Gemfile config'))
46
+ elsif !sync.key? 'spec/spec_helper.rb'
47
+ raise_precondition_error(_('spec/spec_helper.rb configuration not found'))
48
+ elsif !sync['spec/spec_helper.rb'].key? 'mock_with'
49
+ raise_precondition_error(_('spec/spec_helper.rb.mock_with configuration not found'))
50
+ elsif !sync['spec/spec_helper.rb']['mock_with'] == ':rspec'
51
+ raise_precondition_error(_('spec/spec_helper.rb.mock_with not set to \':rspec\''))
52
+ end
53
+ end
54
+
55
+ # @return [String] the path where the new transport will be written.
56
+ def target_object_path
57
+ @target_object_path ||= File.join(module_dir, 'lib', 'puppet', 'transport', object_name) + '.rb'
58
+ end
59
+
60
+ # @return [String] the path where the new schema will be written.
61
+ def target_type_path
62
+ @target_type_path ||= File.join(module_dir, 'lib', 'puppet', 'transport', 'schema', object_name) + '.rb'
63
+ end
64
+
65
+ # @return [String] the path where the deviceshim for the transport will be written.
66
+ def target_device_path
67
+ @target_device_path ||= File.join(module_dir, 'lib', 'puppet', 'util', 'network_device', object_name, 'device.rb')
68
+ end
69
+
70
+ # @return [String] the path where the tests for the new transport
71
+ # will be written.
72
+ def target_spec_path
73
+ @target_spec_path ||= File.join(module_dir, 'spec', 'unit', 'puppet', 'transport', object_name) + '_spec.rb'
74
+ end
75
+
76
+ # @return [String] the path where the tests for the new schema will be written.
77
+ def target_type_spec_path
78
+ @target_type_spec_path ||= File.join(module_dir, 'spec', 'unit', 'puppet', 'transport', 'schema', object_name) + '_spec.rb'
79
+ end
80
+
81
+ # transform a object name into a ruby class name
82
+ def self.class_name_from_object_name(object_name)
83
+ object_name.to_s.split('_').map(&:capitalize).join
84
+ end
85
+ end
86
+ end
87
+ end
@@ -125,7 +125,7 @@ module PDK
125
125
  end
126
126
  rescue ArgumentError => e
127
127
  raise PDK::CLI::ExitWithError, _(
128
- '%{message} Please rename the file or exclude it from the package ' \
128
+ '%{message} Rename the file or exclude it from the package ' \
129
129
  'by adding it to the .pdkignore file in your module.',
130
130
  ) % { message: e.message }
131
131
  end
@@ -220,8 +220,30 @@ module PDK
220
220
  FileUtils.rm_f(package_file)
221
221
 
222
222
  Dir.chdir(target_dir) do
223
- Zlib::GzipWriter.open(package_file) do |package_fd|
224
- Minitar.pack(release_name, package_fd)
223
+ begin
224
+ gz = Zlib::GzipWriter.new(File.open(package_file, 'wb'))
225
+ tar = Minitar::Output.new(gz)
226
+ Find.find(release_name) do |entry|
227
+ entry_meta = {
228
+ name: entry,
229
+ }
230
+
231
+ orig_mode = File.stat(entry).mode
232
+ min_mode = Minitar.dir?(entry) ? 0o755 : 0o644
233
+
234
+ entry_meta[:mode] = orig_mode | min_mode
235
+
236
+ if entry_meta[:mode] != orig_mode
237
+ PDK.logger.debug(_('Updated permissions of packaged \'%{entry}\' to %{new_mode}') % {
238
+ entry: entry,
239
+ new_mode: (entry_meta[:mode] & 0o7777).to_s(8),
240
+ })
241
+ end
242
+
243
+ Minitar.pack_file(entry_meta, tar)
244
+ end
245
+ ensure
246
+ tar.close
225
247
  end
226
248
  end
227
249
  end