pdk 1.11.1 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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