pdk 1.17.0 → 1.18.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 +5 -5
- data/CHANGELOG.md +71 -2
- data/lib/pdk/cli/convert.rb +7 -9
- data/lib/pdk/cli/new/class.rb +2 -1
- data/lib/pdk/cli/new/defined_type.rb +2 -1
- data/lib/pdk/cli/new/provider.rb +2 -1
- data/lib/pdk/cli/new/task.rb +2 -1
- data/lib/pdk/cli/new/test.rb +2 -1
- data/lib/pdk/cli/new/transport.rb +2 -1
- data/lib/pdk/cli/remove/config.rb +80 -0
- data/lib/pdk/cli/remove.rb +20 -0
- data/lib/pdk/cli/set/config.rb +119 -0
- data/lib/pdk/cli/set.rb +20 -0
- data/lib/pdk/cli/update.rb +6 -8
- data/lib/pdk/cli/util/update_manager_printer.rb +82 -0
- data/lib/pdk/cli/util.rb +1 -0
- data/lib/pdk/cli.rb +2 -0
- data/lib/pdk/config.rb +96 -13
- data/lib/pdk/context.rb +8 -5
- data/lib/pdk/generate/defined_type.rb +25 -32
- data/lib/pdk/generate/module.rb +11 -10
- data/lib/pdk/generate/provider.rb +16 -65
- data/lib/pdk/generate/puppet_class.rb +25 -31
- data/lib/pdk/generate/puppet_object.rb +83 -187
- data/lib/pdk/generate/resource_api_object.rb +55 -0
- data/lib/pdk/generate/task.rb +28 -46
- data/lib/pdk/generate/transport.rb +21 -75
- data/lib/pdk/generate.rb +1 -0
- data/lib/pdk/module/convert.rb +41 -23
- data/lib/pdk/module/release.rb +1 -1
- data/lib/pdk/module/update.rb +6 -10
- data/lib/pdk/module/update_manager.rb +7 -0
- data/lib/pdk/module.rb +0 -1
- data/lib/pdk/template/fetcher/git.rb +85 -0
- data/lib/pdk/template/fetcher/local.rb +28 -0
- data/lib/pdk/template/fetcher.rb +98 -0
- data/lib/pdk/template/renderer/v1/legacy_template_dir.rb +116 -0
- data/lib/pdk/template/renderer/v1/renderer.rb +132 -0
- data/lib/pdk/template/renderer/v1/template_file.rb +102 -0
- data/lib/pdk/template/renderer/v1.rb +25 -0
- data/lib/pdk/template/renderer.rb +96 -0
- data/lib/pdk/template/template_dir.rb +67 -0
- data/lib/pdk/template.rb +59 -0
- data/lib/pdk/tests/unit.rb +5 -0
- data/lib/pdk/util/json_finder.rb +84 -0
- data/lib/pdk/util/puppet_strings.rb +3 -3
- data/lib/pdk/util/template_uri.rb +4 -6
- data/lib/pdk/util.rb +4 -35
- data/lib/pdk/validate/control_repo/control_repo_validator_group.rb +23 -0
- data/lib/pdk/validate/control_repo/environment_conf_validator.rb +98 -0
- data/lib/pdk/validate/invokable_validator.rb +2 -3
- data/lib/pdk/validate/validator.rb +7 -0
- data/lib/pdk/validate/validator_group.rb +1 -0
- data/lib/pdk/validate.rb +17 -10
- data/lib/pdk/version.rb +1 -1
- data/lib/pdk.rb +1 -1
- data/locales/pdk.pot +356 -228
- metadata +33 -12
- data/lib/pdk/module/template_dir/base.rb +0 -268
- data/lib/pdk/module/template_dir/git.rb +0 -91
- data/lib/pdk/module/template_dir/local.rb +0 -21
- data/lib/pdk/module/template_dir.rb +0 -115
- data/lib/pdk/template_file.rb +0 -96
@@ -0,0 +1,84 @@
|
|
1
|
+
module PDK
|
2
|
+
module Util
|
3
|
+
# Processes a given string, looking for JSON objects and parsing them.
|
4
|
+
#
|
5
|
+
# @example A string with a JSON object and some junk characters
|
6
|
+
# PDK::Util::JSONFinder.new('foo{"bar":1}').objects
|
7
|
+
# => [{ 'bar' => 1 }]
|
8
|
+
#
|
9
|
+
# @example A string with mulitple JSON objects
|
10
|
+
# PDK::Util::JSONFinder.new('foo{"bar":1}baz{"gronk":2}').objects
|
11
|
+
# => [{ 'bar' => 1 }, { 'gronk' => 2 }]
|
12
|
+
class JSONFinder
|
13
|
+
# Creates a new instance of PDK::Util::JSONFinder.
|
14
|
+
#
|
15
|
+
# @param string [String] the string to find JSON objects inside of.
|
16
|
+
#
|
17
|
+
# @return [PDK::Util::JSONFinder] a new PDK::Util::JSONFinder object.
|
18
|
+
def initialize(string)
|
19
|
+
require 'strscan'
|
20
|
+
|
21
|
+
@scanner = StringScanner.new(string)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the parsed JSON objects from the string.
|
25
|
+
#
|
26
|
+
# @return [Array[Hash]] the parsed JSON objects present in the string.
|
27
|
+
def objects
|
28
|
+
return @objects unless @objects.nil?
|
29
|
+
|
30
|
+
require 'json'
|
31
|
+
|
32
|
+
until @scanner.eos?
|
33
|
+
@scanner.getch until @scanner.peek(1) == '{' || @scanner.eos?
|
34
|
+
|
35
|
+
(@objects ||= []) << begin
|
36
|
+
JSON.parse(read_object(true) || '')
|
37
|
+
rescue JSON::ParserError
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
@objects = @objects.compact
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# Recursively process the string to extract a complete JSON object.
|
48
|
+
#
|
49
|
+
# @param new_object [Boolean] Set to true if processing a new object to
|
50
|
+
# capture the opening brace. Set to false if being called recursively
|
51
|
+
# where the opening brace has already been captured.
|
52
|
+
#
|
53
|
+
# @return [String] The matched substring containing a JSON object.
|
54
|
+
def read_object(new_object = false)
|
55
|
+
matched_text = new_object ? @scanner.getch : ''
|
56
|
+
|
57
|
+
until @scanner.eos?
|
58
|
+
text = @scanner.scan_until(%r{(?:(?<!\\)"|\{|\})})
|
59
|
+
unless text
|
60
|
+
@scanner.terminate
|
61
|
+
return nil
|
62
|
+
end
|
63
|
+
matched_text += text
|
64
|
+
|
65
|
+
case @scanner.matched
|
66
|
+
when '}'
|
67
|
+
break
|
68
|
+
when '"'
|
69
|
+
text = @scanner.scan_until(%r{(?<!\\)"})
|
70
|
+
unless text
|
71
|
+
@scanner.terminate
|
72
|
+
return nil
|
73
|
+
end
|
74
|
+
matched_text += text
|
75
|
+
else
|
76
|
+
matched_text += read_object
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
matched_text
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -92,12 +92,12 @@ module PDK
|
|
92
92
|
require 'pdk/generate'
|
93
93
|
|
94
94
|
generators = PDK::Generate.generators.select do |gen|
|
95
|
-
gen.
|
95
|
+
gen.const_defined?(:PUPPET_STRINGS_TYPE) && !gen::PUPPET_STRINGS_TYPE.nil?
|
96
96
|
end
|
97
97
|
|
98
98
|
known_objects = generate_hash
|
99
99
|
|
100
|
-
generators.map { |gen| [gen, known_objects[gen
|
100
|
+
generators.map { |gen| [gen, known_objects[gen::PUPPET_STRINGS_TYPE]] }.reject do |_, obj|
|
101
101
|
obj.nil? || obj.empty?
|
102
102
|
end
|
103
103
|
end
|
@@ -117,7 +117,7 @@ module PDK
|
|
117
117
|
require 'pdk/generate'
|
118
118
|
|
119
119
|
PDK::Generate.generators.find do |gen|
|
120
|
-
gen.
|
120
|
+
gen.const_defined?(:PUPPET_STRINGS_TYPE) && gen::PUPPET_STRINGS_TYPE == type
|
121
121
|
end
|
122
122
|
end
|
123
123
|
end
|
@@ -192,7 +192,6 @@ module PDK
|
|
192
192
|
# directory, and :allow_fallback contains a Boolean that specifies if
|
193
193
|
# the lookup process should proceed to the next template directory if
|
194
194
|
# the template file is not in this template directory.
|
195
|
-
#
|
196
195
|
def self.templates(opts)
|
197
196
|
require 'pdk/answer_file'
|
198
197
|
require 'pdk/util'
|
@@ -261,21 +260,20 @@ module PDK
|
|
261
260
|
found_template[:uri]
|
262
261
|
end
|
263
262
|
|
264
|
-
def self.valid_template?(template)
|
263
|
+
def self.valid_template?(template, context = PDK.context)
|
265
264
|
require 'addressable'
|
266
265
|
|
267
266
|
return false if template.nil? || !template.is_a?(Hash)
|
268
267
|
return false if template[:uri].nil? || !template[:uri].is_a?(Addressable::URI)
|
269
268
|
|
270
269
|
return true if PDK::Util::Git.repo?(bare_uri(template[:uri]))
|
271
|
-
|
272
270
|
path = human_readable(template[:uri].path)
|
273
271
|
if PDK::Util::Filesystem.directory?(path)
|
274
272
|
# We know that it's not a git repository, but it's a valid path on disk
|
275
273
|
begin
|
276
|
-
PDK::
|
277
|
-
return
|
278
|
-
rescue
|
274
|
+
renderer = PDK::Template::Renderer.instance(path, template[:uri], context)
|
275
|
+
return !renderer.nil?
|
276
|
+
rescue StandardError
|
279
277
|
nil
|
280
278
|
end
|
281
279
|
end
|
data/lib/pdk/util.rb
CHANGED
@@ -14,6 +14,7 @@ module PDK
|
|
14
14
|
autoload :Env, 'pdk/util/env'
|
15
15
|
autoload :Filesystem, 'pdk/util/filesystem'
|
16
16
|
autoload :Git, 'pdk/util/git'
|
17
|
+
autoload :JSONFinder, 'pdk/util/json_finder'
|
17
18
|
autoload :PuppetStrings, 'pdk/util/puppet_strings'
|
18
19
|
autoload :PuppetVersion, 'pdk/util/puppet_version'
|
19
20
|
autoload :RubyVersion, 'pdk/util/ruby_version'
|
@@ -148,7 +149,7 @@ module PDK
|
|
148
149
|
|
149
150
|
def system_configdir
|
150
151
|
return @system_configdir unless @system_configdir.nil?
|
151
|
-
return @system_configdir = File.join('opt', 'puppetlabs', 'pdk', 'config') unless Gem.win_platform?
|
152
|
+
return @system_configdir = File.join(File::SEPARATOR, 'opt', 'puppetlabs', 'pdk', 'config') unless Gem.win_platform?
|
152
153
|
|
153
154
|
return @system_configdir = File.join(PDK::Util::Env['ProgramData'], 'PuppetLabs', 'PDK') unless PDK::Util::Env['ProgramData'].nil?
|
154
155
|
@system_configdir = File.join(PDK::Util::Env['AllUsersProfile'], 'PuppetLabs', 'PDK')
|
@@ -196,7 +197,7 @@ module PDK
|
|
196
197
|
# @return [Hash, nil] subset of text as Hash of first valid JSON found, or nil if no valid
|
197
198
|
# JSON found in the text
|
198
199
|
def find_first_json_in(text)
|
199
|
-
|
200
|
+
find_all_json_in(text).first
|
200
201
|
end
|
201
202
|
module_function :find_first_json_in
|
202
203
|
|
@@ -206,42 +207,10 @@ module PDK
|
|
206
207
|
# @return [Array<Hash>] subset of text as Array of all JSON object found, empty Array if none are found
|
207
208
|
# JSON found in the text
|
208
209
|
def find_all_json_in(text)
|
209
|
-
|
210
|
+
PDK::Util::JSONFinder.new(text).objects
|
210
211
|
end
|
211
212
|
module_function :find_all_json_in
|
212
213
|
|
213
|
-
# Iterate through possible JSON documents until we find one that is valid.
|
214
|
-
#
|
215
|
-
# @param [String] text the text in which to find a JSON document
|
216
|
-
# @param [Hash] opts options
|
217
|
-
# @option opts [Boolean] :break_on_first Whether or not to break after valid JSON is found, defaults to true
|
218
|
-
#
|
219
|
-
# @return [Hash, Array<Hash>, nil] subset of text as Hash of first valid JSON found, array of all valid JSON found, or nil if no valid
|
220
|
-
# JSON found in the text
|
221
|
-
#
|
222
|
-
# @private
|
223
|
-
def find_valid_json_in(text, opts = {})
|
224
|
-
break_on_first = opts.key?(:break_on_first) ? opts[:break_on_first] : true
|
225
|
-
|
226
|
-
json_result = break_on_first ? nil : []
|
227
|
-
|
228
|
-
text.scan(%r{\{(?:[^{}]|(?:\g<0>))*\}}x) do |str|
|
229
|
-
begin
|
230
|
-
if break_on_first
|
231
|
-
json_result = JSON.parse(str)
|
232
|
-
break
|
233
|
-
else
|
234
|
-
json_result.push(JSON.parse(str))
|
235
|
-
end
|
236
|
-
rescue JSON::ParserError
|
237
|
-
next
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
json_result
|
242
|
-
end
|
243
|
-
module_function :find_valid_json_in
|
244
|
-
|
245
214
|
# Returns the targets' paths relative to the working directory
|
246
215
|
#
|
247
216
|
# @return [Array<String>] The absolute or path to the target
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'pdk'
|
2
|
+
|
3
|
+
module PDK
|
4
|
+
module Validate
|
5
|
+
module ControlRepo
|
6
|
+
class ControlRepoValidatorGroup < ValidatorGroup
|
7
|
+
def name
|
8
|
+
'control-repo'
|
9
|
+
end
|
10
|
+
|
11
|
+
def valid_in_context?
|
12
|
+
context.is_a?(PDK::Context::ControlRepo)
|
13
|
+
end
|
14
|
+
|
15
|
+
def validators
|
16
|
+
[
|
17
|
+
EnvironmentConfValidator,
|
18
|
+
].freeze
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'pdk'
|
2
|
+
|
3
|
+
module PDK
|
4
|
+
module Validate
|
5
|
+
module ControlRepo
|
6
|
+
class EnvironmentConfValidator < InternalRubyValidator
|
7
|
+
ALLOWED_SETTINGS = %w[modulepath manifest config_version environment_timeout].freeze
|
8
|
+
|
9
|
+
def name
|
10
|
+
'environment-conf'
|
11
|
+
end
|
12
|
+
|
13
|
+
def valid_in_context?
|
14
|
+
context.is_a?(PDK::Context::ControlRepo)
|
15
|
+
end
|
16
|
+
|
17
|
+
def pattern
|
18
|
+
['environment.conf']
|
19
|
+
end
|
20
|
+
|
21
|
+
def spinner_text
|
22
|
+
_('Checking Puppet Environment settings (%{patterns}).') % {
|
23
|
+
patterns: pattern.join(' '),
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate_target(report, target)
|
28
|
+
unless PDK::Util::Filesystem.readable?(target)
|
29
|
+
report.add_event(
|
30
|
+
file: target,
|
31
|
+
source: name,
|
32
|
+
state: :failure,
|
33
|
+
severity: 'error',
|
34
|
+
message: _('Could not be read.'),
|
35
|
+
)
|
36
|
+
return 1
|
37
|
+
end
|
38
|
+
|
39
|
+
is_valid = true
|
40
|
+
begin
|
41
|
+
env_conf = PDK::ControlRepo.environment_conf_as_config(target)
|
42
|
+
|
43
|
+
env_conf.resolve.each do |setting_name, setting_value|
|
44
|
+
# Remove the 'environment.' setting_name prefix
|
45
|
+
setting_name = setting_name.slice(12..-1)
|
46
|
+
next if ALLOWED_SETTINGS.include?(setting_name)
|
47
|
+
# A hash indicates that the ini file has a section in it.
|
48
|
+
message = if setting_value.is_a?(Hash)
|
49
|
+
_("Invalid section '%{name}'") % { name: setting_name }
|
50
|
+
else
|
51
|
+
_("Invalid setting '%{name}'") % { name: setting_name }
|
52
|
+
end
|
53
|
+
|
54
|
+
report.add_event(
|
55
|
+
file: target,
|
56
|
+
source: name,
|
57
|
+
state: :failure,
|
58
|
+
severity: 'error',
|
59
|
+
message: message,
|
60
|
+
)
|
61
|
+
is_valid = false
|
62
|
+
end
|
63
|
+
|
64
|
+
timeout = env_conf.fetch('environment_timeout', nil)
|
65
|
+
unless timeout.nil? || timeout == '0' || timeout == 'unlimited'
|
66
|
+
report.add_event(
|
67
|
+
file: target,
|
68
|
+
source: name,
|
69
|
+
state: :failure,
|
70
|
+
severity: 'error',
|
71
|
+
message: _("environment_timeout is set to '%{timeout}' but should be 0, 'unlimited' or not set.") % { timeout: timeout },
|
72
|
+
)
|
73
|
+
is_valid = false
|
74
|
+
end
|
75
|
+
|
76
|
+
return 1 unless is_valid
|
77
|
+
report.add_event(
|
78
|
+
file: target,
|
79
|
+
source: name,
|
80
|
+
state: :passed,
|
81
|
+
severity: 'ok',
|
82
|
+
)
|
83
|
+
return 0
|
84
|
+
rescue StandardError => e
|
85
|
+
report.add_event(
|
86
|
+
file: target,
|
87
|
+
source: name,
|
88
|
+
state: :failure,
|
89
|
+
severity: 'error',
|
90
|
+
message: e.message,
|
91
|
+
)
|
92
|
+
return 1
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -19,9 +19,8 @@ module PDK
|
|
19
19
|
:once
|
20
20
|
end
|
21
21
|
|
22
|
-
# Whether this Validator can be invoked in this context. By default any
|
23
|
-
# @
|
24
|
-
# @abstract
|
22
|
+
# Whether this Validator can be invoked in this context. By default any InvokableValidator can work in any Context, except ::None
|
23
|
+
# @see PDK::Validate::Validator
|
25
24
|
def valid_in_context?
|
26
25
|
!context.is_a?(PDK::Context::None)
|
27
26
|
end
|
@@ -40,6 +40,13 @@ module PDK
|
|
40
40
|
@prepared = false
|
41
41
|
end
|
42
42
|
|
43
|
+
# Whether this Validator can be invoked in this context. By default any Validator can work in any Context
|
44
|
+
# @return [Boolean]
|
45
|
+
# @abstract
|
46
|
+
def valid_in_context?
|
47
|
+
true
|
48
|
+
end
|
49
|
+
|
43
50
|
# Returns the text used for the spinner to display to the user while invoking
|
44
51
|
#
|
45
52
|
# @return [String]
|
data/lib/pdk/validate.rb
CHANGED
@@ -8,15 +8,22 @@ module PDK
|
|
8
8
|
autoload :Validator, 'pdk/validate/validator'
|
9
9
|
autoload :ValidatorGroup, 'pdk/validate/validator_group'
|
10
10
|
|
11
|
+
module ControlRepo
|
12
|
+
autoload :ControlRepoValidatorGroup, 'pdk/validate/control_repo/control_repo_validator_group'
|
13
|
+
autoload :EnvironmentConfValidator, 'pdk/validate/control_repo/environment_conf_validator'
|
14
|
+
end
|
15
|
+
|
11
16
|
module Metadata
|
12
17
|
autoload :MetadataJSONLintValidator, 'pdk/validate/metadata/metadata_json_lint_validator'
|
13
18
|
autoload :MetadataSyntaxValidator, 'pdk/validate/metadata/metadata_syntax_validator'
|
14
19
|
autoload :MetadataValidatorGroup, 'pdk/validate/metadata/metadata_validator_group'
|
15
20
|
end
|
16
21
|
|
17
|
-
module
|
18
|
-
autoload :
|
19
|
-
autoload :
|
22
|
+
module Puppet
|
23
|
+
autoload :PuppetEPPValidator, 'pdk/validate/puppet/puppet_epp_validator'
|
24
|
+
autoload :PuppetLintValidator, 'pdk/validate/puppet/puppet_lint_validator'
|
25
|
+
autoload :PuppetSyntaxValidator, 'pdk/validate/puppet/puppet_syntax_validator'
|
26
|
+
autoload :PuppetValidatorGroup, 'pdk/validate/puppet/puppet_validator_group'
|
20
27
|
end
|
21
28
|
|
22
29
|
module Ruby
|
@@ -25,16 +32,14 @@ module PDK
|
|
25
32
|
end
|
26
33
|
|
27
34
|
module Tasks
|
28
|
-
autoload :TasksNameValidator, 'pdk/validate/tasks/tasks_name_validator'
|
29
35
|
autoload :TasksMetadataLintValidator, 'pdk/validate/tasks/tasks_metadata_lint_validator'
|
36
|
+
autoload :TasksNameValidator, 'pdk/validate/tasks/tasks_name_validator'
|
30
37
|
autoload :TasksValidatorGroup, 'pdk/validate/tasks/tasks_validator_group'
|
31
38
|
end
|
32
39
|
|
33
|
-
module
|
34
|
-
autoload :
|
35
|
-
autoload :
|
36
|
-
autoload :PuppetSyntaxValidator, 'pdk/validate/puppet/puppet_syntax_validator'
|
37
|
-
autoload :PuppetValidatorGroup, 'pdk/validate/puppet/puppet_validator_group'
|
40
|
+
module YAML
|
41
|
+
autoload :YAMLSyntaxValidator, 'pdk/validate/yaml/yaml_syntax_validator'
|
42
|
+
autoload :YAMLValidatorGroup, 'pdk/validate/yaml/yaml_validator_group'
|
38
43
|
end
|
39
44
|
|
40
45
|
def self.validators
|
@@ -47,8 +52,9 @@ module PDK
|
|
47
52
|
|
48
53
|
# @api private
|
49
54
|
def self.validator_hash
|
50
|
-
# TODO: This isn't the most performant... But with only
|
55
|
+
# TODO: This isn't the most performant... But with only 6 items, it's fine
|
51
56
|
@validator_hash ||= [
|
57
|
+
ControlRepo::ControlRepoValidatorGroup,
|
52
58
|
Metadata::MetadataValidatorGroup,
|
53
59
|
Puppet::PuppetValidatorGroup,
|
54
60
|
Ruby::RubyValidatorGroup,
|
@@ -60,6 +66,7 @@ module PDK
|
|
60
66
|
def self.invoke_validators_by_name(context, names, parallel = false, options = {})
|
61
67
|
instances = names.select { |name| validator_names.include?(name) }
|
62
68
|
.map { |name| validator_hash[name].new(context, options) }
|
69
|
+
.select { |instance| instance.valid_in_context? }
|
63
70
|
.each { |instance| instance.prepare_invoke! }
|
64
71
|
report = PDK::Report.new
|
65
72
|
|
data/lib/pdk/version.rb
CHANGED
data/lib/pdk.rb
CHANGED
@@ -11,7 +11,7 @@ module PDK
|
|
11
11
|
autoload :Logger, 'pdk/logger'
|
12
12
|
autoload :Module, 'pdk/module'
|
13
13
|
autoload :Report, 'pdk/report'
|
14
|
-
autoload :
|
14
|
+
autoload :Template, 'pdk/template'
|
15
15
|
autoload :TEMPLATE_REF, 'pdk/version'
|
16
16
|
autoload :Util, 'pdk/util'
|
17
17
|
autoload :Validate, 'pdk/validate'
|