pdk 1.9.0 → 3.2.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 +744 -711
- data/README.md +23 -21
- data/lib/pdk/answer_file.rb +3 -112
- data/lib/pdk/bolt.rb +20 -0
- data/lib/pdk/cli/build.rb +51 -54
- data/lib/pdk/cli/bundle.rb +33 -29
- data/lib/pdk/cli/console.rb +148 -0
- data/lib/pdk/cli/convert.rb +46 -37
- data/lib/pdk/cli/env.rb +51 -0
- data/lib/pdk/cli/errors.rb +4 -3
- data/lib/pdk/cli/exec/command.rb +285 -0
- data/lib/pdk/cli/exec/interactive_command.rb +109 -0
- data/lib/pdk/cli/exec.rb +32 -201
- data/lib/pdk/cli/exec_group.rb +79 -43
- data/lib/pdk/cli/get/config.rb +26 -0
- data/lib/pdk/cli/get.rb +22 -0
- data/lib/pdk/cli/new/class.rb +20 -22
- data/lib/pdk/cli/new/defined_type.rb +21 -21
- data/lib/pdk/cli/new/fact.rb +27 -0
- data/lib/pdk/cli/new/function.rb +27 -0
- data/lib/pdk/cli/new/module.rb +40 -29
- data/lib/pdk/cli/new/provider.rb +18 -18
- data/lib/pdk/cli/new/task.rb +23 -22
- data/lib/pdk/cli/new/test.rb +52 -0
- data/lib/pdk/cli/new/transport.rb +27 -0
- data/lib/pdk/cli/new.rb +15 -9
- data/lib/pdk/cli/release/prep.rb +39 -0
- data/lib/pdk/cli/release/publish.rb +46 -0
- data/lib/pdk/cli/release.rb +185 -0
- data/lib/pdk/cli/remove/config.rb +83 -0
- data/lib/pdk/cli/remove.rb +22 -0
- data/lib/pdk/cli/set/config.rb +121 -0
- data/lib/pdk/cli/set.rb +22 -0
- data/lib/pdk/cli/test/unit.rb +71 -69
- data/lib/pdk/cli/test.rb +9 -8
- data/lib/pdk/cli/update.rb +38 -21
- data/lib/pdk/cli/util/command_redirector.rb +13 -3
- data/lib/pdk/cli/util/interview.rb +25 -9
- data/lib/pdk/cli/util/option_normalizer.rb +6 -6
- data/lib/pdk/cli/util/option_validator.rb +19 -9
- data/lib/pdk/cli/util/spinner.rb +13 -0
- data/lib/pdk/cli/util/update_manager_printer.rb +82 -0
- data/lib/pdk/cli/util.rb +105 -48
- data/lib/pdk/cli/validate.rb +96 -111
- data/lib/pdk/cli.rb +134 -87
- data/lib/pdk/config/errors.rb +5 -0
- data/lib/pdk/config/ini_file.rb +184 -0
- data/lib/pdk/config/ini_file_setting.rb +35 -0
- data/lib/pdk/config/json.rb +35 -0
- data/lib/pdk/config/json_schema_namespace.rb +137 -0
- data/lib/pdk/config/json_schema_setting.rb +51 -0
- data/lib/pdk/config/json_with_schema.rb +47 -0
- data/lib/pdk/config/namespace.rb +362 -0
- data/lib/pdk/config/setting.rb +134 -0
- data/lib/pdk/config/task_schema.json +116 -0
- data/lib/pdk/config/validator.rb +31 -0
- data/lib/pdk/config/yaml.rb +41 -0
- data/lib/pdk/config/yaml_with_schema.rb +51 -0
- data/lib/pdk/config.rb +304 -0
- data/lib/pdk/context/control_repo.rb +61 -0
- data/lib/pdk/context/module.rb +28 -0
- data/lib/pdk/context/none.rb +22 -0
- data/lib/pdk/context.rb +98 -0
- data/lib/pdk/control_repo.rb +89 -0
- data/lib/pdk/generate/defined_type.rb +27 -33
- data/lib/pdk/generate/fact.rb +26 -0
- data/lib/pdk/generate/function.rb +49 -0
- data/lib/pdk/generate/module.rb +160 -153
- data/lib/pdk/generate/provider.rb +16 -69
- data/lib/pdk/generate/puppet_class.rb +27 -32
- data/lib/pdk/generate/puppet_object.rb +100 -159
- data/lib/pdk/generate/task.rb +34 -51
- data/lib/pdk/generate/transport.rb +34 -0
- data/lib/pdk/generate.rb +21 -8
- data/lib/pdk/logger.rb +24 -6
- data/lib/pdk/module/build.rb +125 -37
- data/lib/pdk/module/convert.rb +146 -65
- data/lib/pdk/module/metadata.rb +72 -71
- data/lib/pdk/module/release.rb +255 -0
- data/lib/pdk/module/update.rb +48 -37
- data/lib/pdk/module/update_manager.rb +75 -39
- data/lib/pdk/module.rb +10 -2
- data/lib/pdk/monkey_patches.rb +268 -0
- data/lib/pdk/report/event.rb +36 -48
- data/lib/pdk/report.rb +35 -22
- data/lib/pdk/template/fetcher/git.rb +84 -0
- data/lib/pdk/template/fetcher/local.rb +29 -0
- data/lib/pdk/template/fetcher.rb +100 -0
- data/lib/pdk/template/renderer/v1/legacy_template_dir.rb +108 -0
- data/lib/pdk/template/renderer/v1/renderer.rb +131 -0
- data/lib/pdk/template/renderer/v1/template_file.rb +100 -0
- data/lib/pdk/template/renderer/v1.rb +25 -0
- data/lib/pdk/template/renderer.rb +97 -0
- data/lib/pdk/template/template_dir.rb +67 -0
- data/lib/pdk/template.rb +52 -0
- data/lib/pdk/tests/unit.rb +101 -51
- data/lib/pdk/util/bundler.rb +44 -42
- data/lib/pdk/util/changelog_generator.rb +138 -0
- data/lib/pdk/util/env.rb +48 -0
- data/lib/pdk/util/filesystem.rb +139 -2
- data/lib/pdk/util/git.rb +108 -8
- data/lib/pdk/util/json_finder.rb +86 -0
- data/lib/pdk/util/puppet_strings.rb +125 -0
- data/lib/pdk/util/puppet_version.rb +71 -87
- data/lib/pdk/util/ruby_version.rb +49 -25
- data/lib/pdk/util/template_uri.rb +283 -0
- data/lib/pdk/util/vendored_file.rb +34 -42
- data/lib/pdk/util/version.rb +11 -10
- data/lib/pdk/util/windows/api_types.rb +74 -44
- data/lib/pdk/util/windows/file.rb +31 -27
- data/lib/pdk/util/windows/process.rb +74 -0
- data/lib/pdk/util/windows/string.rb +19 -12
- data/lib/pdk/util/windows.rb +2 -0
- data/lib/pdk/util.rb +111 -124
- 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/external_command_validator.rb +213 -0
- data/lib/pdk/validate/internal_ruby_validator.rb +101 -0
- data/lib/pdk/validate/invokable_validator.rb +238 -0
- data/lib/pdk/validate/metadata/metadata_json_lint_validator.rb +84 -0
- data/lib/pdk/validate/metadata/metadata_syntax_validator.rb +76 -0
- data/lib/pdk/validate/metadata/metadata_validator_group.rb +20 -0
- data/lib/pdk/validate/puppet/puppet_epp_validator.rb +131 -0
- data/lib/pdk/validate/puppet/puppet_lint_validator.rb +66 -0
- data/lib/pdk/validate/puppet/puppet_plan_syntax_validator.rb +38 -0
- data/lib/pdk/validate/puppet/puppet_syntax_validator.rb +135 -0
- data/lib/pdk/validate/puppet/puppet_validator_group.rb +22 -0
- data/lib/pdk/validate/ruby/ruby_rubocop_validator.rb +79 -0
- data/lib/pdk/validate/ruby/ruby_validator_group.rb +19 -0
- data/lib/pdk/validate/tasks/tasks_metadata_lint_validator.rb +83 -0
- data/lib/pdk/validate/tasks/tasks_name_validator.rb +45 -0
- data/lib/pdk/validate/tasks/tasks_validator_group.rb +20 -0
- data/lib/pdk/validate/validator.rb +120 -0
- data/lib/pdk/validate/validator_group.rb +107 -0
- data/lib/pdk/validate/yaml/yaml_syntax_validator.rb +86 -0
- data/lib/pdk/validate/yaml/yaml_validator_group.rb +19 -0
- data/lib/pdk/validate.rb +86 -12
- data/lib/pdk/version.rb +2 -2
- data/lib/pdk.rb +60 -10
- metadata +138 -100
- data/lib/pdk/cli/module/build.rb +0 -14
- data/lib/pdk/cli/module/generate.rb +0 -45
- data/lib/pdk/cli/module.rb +0 -14
- data/lib/pdk/i18n.rb +0 -4
- data/lib/pdk/module/templatedir.rb +0 -321
- data/lib/pdk/template_file.rb +0 -95
- data/lib/pdk/validate/base_validator.rb +0 -215
- data/lib/pdk/validate/metadata/metadata_json_lint.rb +0 -86
- data/lib/pdk/validate/metadata/metadata_syntax.rb +0 -109
- data/lib/pdk/validate/metadata_validator.rb +0 -30
- data/lib/pdk/validate/puppet/puppet_lint.rb +0 -67
- data/lib/pdk/validate/puppet/puppet_syntax.rb +0 -112
- data/lib/pdk/validate/puppet_validator.rb +0 -30
- data/lib/pdk/validate/ruby/rubocop.rb +0 -77
- data/lib/pdk/validate/ruby_validator.rb +0 -29
- data/lib/pdk/validate/tasks/metadata_lint.rb +0 -126
- data/lib/pdk/validate/tasks/name.rb +0 -88
- data/lib/pdk/validate/tasks_validator.rb +0 -33
- data/lib/pdk/validate/yaml/syntax.rb +0 -109
- data/lib/pdk/validate/yaml_validator.rb +0 -31
- data/locales/config.yaml +0 -21
- data/locales/pdk.pot +0 -1291
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-06/schema#",
|
|
3
|
+
"title": "Puppet Task Metadata",
|
|
4
|
+
"description": "The metadata format for Puppet Tasks",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"description": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "A longer description(one paragraph) of how to use the task"
|
|
10
|
+
},
|
|
11
|
+
"puppet_task_version": {
|
|
12
|
+
"type": "integer",
|
|
13
|
+
"description": "The version of this spec used",
|
|
14
|
+
"default": 1
|
|
15
|
+
},
|
|
16
|
+
"supports_noop": {
|
|
17
|
+
"type": "boolean",
|
|
18
|
+
"default": false,
|
|
19
|
+
"description": "This task respects the '_noop' metaparam. If this false or absent the task runner will refuse to run this task if noop is specified."
|
|
20
|
+
},
|
|
21
|
+
"remote": {
|
|
22
|
+
"type": "boolean",
|
|
23
|
+
"default": false,
|
|
24
|
+
"description": "This task is capable of operating on a remote target using connection information in the '_target' metaparam."
|
|
25
|
+
},
|
|
26
|
+
"input_method": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"enum": ["stdin", "environment", "both", "powershell"],
|
|
29
|
+
"description": "What input method should be used to pass params to the task"
|
|
30
|
+
},
|
|
31
|
+
"parameters": {
|
|
32
|
+
"$ref": "#/definitions/paramsObject",
|
|
33
|
+
"description": "An object mapping valid parameter names to corresponding json-schemas"
|
|
34
|
+
},
|
|
35
|
+
"implementations": {
|
|
36
|
+
"type": "array",
|
|
37
|
+
"items": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"required": ["name"],
|
|
40
|
+
"properties": {
|
|
41
|
+
"name": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"description": "Name of task executable file"
|
|
44
|
+
},
|
|
45
|
+
"requirements": {
|
|
46
|
+
"type": "array",
|
|
47
|
+
"additionalItems": {
|
|
48
|
+
"type": "string"
|
|
49
|
+
},
|
|
50
|
+
"description": "Features required on target to execute task"
|
|
51
|
+
},
|
|
52
|
+
"files": {
|
|
53
|
+
"type": "array",
|
|
54
|
+
"additionalItems": {
|
|
55
|
+
"type": "string"
|
|
56
|
+
},
|
|
57
|
+
"description": "File resources required by task"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"description": "Rules for selecting implementation resources based on features available on target"
|
|
62
|
+
},
|
|
63
|
+
"files": {
|
|
64
|
+
"type": "array",
|
|
65
|
+
"items": {
|
|
66
|
+
"type": "string"
|
|
67
|
+
},
|
|
68
|
+
"description": "Path to file resources saved in valid module directory to be provided to task"
|
|
69
|
+
},
|
|
70
|
+
"private": {
|
|
71
|
+
"type": "boolean",
|
|
72
|
+
"description": "Should this task appear by default in UI lists of tasks"
|
|
73
|
+
},
|
|
74
|
+
"extensions": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"description": "Task Runner specific metadata extensions",
|
|
77
|
+
"items": {
|
|
78
|
+
"type": "object"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"definitions": {
|
|
83
|
+
"parameterName": {
|
|
84
|
+
"description": "Valid names for parameter keys",
|
|
85
|
+
"type": "string",
|
|
86
|
+
"pattern": "^[a-z][a-z0-9_]*$"
|
|
87
|
+
},
|
|
88
|
+
"paramsObject": {
|
|
89
|
+
"type": "object",
|
|
90
|
+
"description": "An object with restricted keys and enumData support",
|
|
91
|
+
"propertyNames": {"$ref": "#/definitions/parameterName"},
|
|
92
|
+
"additionalProperties": {
|
|
93
|
+
"type": "object",
|
|
94
|
+
"description": "Extend Normal JSON schema to require an object and describe 'enumData' to map enum values to descriptions",
|
|
95
|
+
"properties": {
|
|
96
|
+
"description": {
|
|
97
|
+
"description": "A description of the parameter",
|
|
98
|
+
"type": "string"
|
|
99
|
+
},
|
|
100
|
+
"type": {
|
|
101
|
+
"description": "A puppet type string that describes a data type that will match the parameter value",
|
|
102
|
+
"type": "string"
|
|
103
|
+
},
|
|
104
|
+
"sensitive": {
|
|
105
|
+
"description": "Whether the task runner should treat the parameter value as sensitive",
|
|
106
|
+
"type": "boolean",
|
|
107
|
+
"default": false
|
|
108
|
+
},
|
|
109
|
+
"default": {
|
|
110
|
+
"description": "The default value to pass to the task implementation if the parameter isn't provided"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module PDK
|
|
2
|
+
class Config
|
|
3
|
+
# A collection of predefined validators for use with {PDK::Config::Value}.
|
|
4
|
+
#
|
|
5
|
+
# @example
|
|
6
|
+
# value :enabled do
|
|
7
|
+
# validate PDK::Config::Validator.boolean
|
|
8
|
+
# end
|
|
9
|
+
module Validator
|
|
10
|
+
# @return [Hash{Symbol => [Proc,String]}] a {PDK::Config::Value}
|
|
11
|
+
# validator that ensures that the value is either a TrueClass or
|
|
12
|
+
# FalseClass.
|
|
13
|
+
def self.boolean
|
|
14
|
+
{
|
|
15
|
+
proc: ->(value) { [true, false].include?(value) },
|
|
16
|
+
message: 'must be a boolean: true or false'
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @return [Hash{Symbol => [Proc,String]}] a {PDK::Config::Value}
|
|
21
|
+
# validator that ensures that the value is a String that matches the
|
|
22
|
+
# regex for a version 4 UUID.
|
|
23
|
+
def self.uuid
|
|
24
|
+
{
|
|
25
|
+
proc: ->(value) { value.match(/\A\h{8}(?:-\h{4}){3}-\h{12}\z/) },
|
|
26
|
+
message: 'must be a version 4 UUID'
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'pdk'
|
|
2
|
+
|
|
3
|
+
module PDK
|
|
4
|
+
class Config
|
|
5
|
+
# Parses a YAML document.
|
|
6
|
+
#
|
|
7
|
+
# @see PDK::Config::Namespace.parse_file
|
|
8
|
+
class YAML < Namespace
|
|
9
|
+
def parse_file(filename)
|
|
10
|
+
raise unless block_given?
|
|
11
|
+
|
|
12
|
+
data = load_data(filename)
|
|
13
|
+
return if data.nil? || data.empty?
|
|
14
|
+
|
|
15
|
+
require 'yaml'
|
|
16
|
+
|
|
17
|
+
data = if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0.pre1')
|
|
18
|
+
::YAML.safe_load(data, permitted_classes: [Symbol], permitted_symbols: [], aliases: true)
|
|
19
|
+
else
|
|
20
|
+
::YAML.safe_load(data, [Symbol], [], true)
|
|
21
|
+
end
|
|
22
|
+
return if data.nil?
|
|
23
|
+
|
|
24
|
+
data.each { |k, v| yield k, PDK::Config::Setting.new(k, self, v) }
|
|
25
|
+
rescue Psych::SyntaxError => e
|
|
26
|
+
raise PDK::Config::LoadError, format('Syntax error when loading %{file}: %{error}', file: filename, error: "#{e.problem} #{e.context}")
|
|
27
|
+
rescue Psych::DisallowedClass => e
|
|
28
|
+
raise PDK::Config::LoadError, format('Unsupported class in %{file}: %{error}', file: filename, error: e.message)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Serializes object data into a YAML string.
|
|
32
|
+
#
|
|
33
|
+
# @see PDK::Config::Namespace.serialize_data
|
|
34
|
+
def serialize_data(data)
|
|
35
|
+
require 'yaml'
|
|
36
|
+
|
|
37
|
+
::YAML.dump(data)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'pdk'
|
|
2
|
+
|
|
3
|
+
module PDK
|
|
4
|
+
class Config
|
|
5
|
+
# Parses a YAML document with a JSON schema.
|
|
6
|
+
#
|
|
7
|
+
# @see PDK::Config::Namespace.parse_file
|
|
8
|
+
class YAMLWithSchema < JSONSchemaNamespace
|
|
9
|
+
def parse_file(filename)
|
|
10
|
+
raise unless block_given?
|
|
11
|
+
|
|
12
|
+
data = load_data(filename)
|
|
13
|
+
data = '' if data.nil?
|
|
14
|
+
require 'yaml'
|
|
15
|
+
require 'json-schema'
|
|
16
|
+
|
|
17
|
+
@raw_data = ::YAML.safe_load(data, [Symbol], [], true)
|
|
18
|
+
@raw_data = {} if @raw_data.nil?
|
|
19
|
+
|
|
20
|
+
begin
|
|
21
|
+
# Ensure the parsed document is actually valid
|
|
22
|
+
validate_document!(@raw_data)
|
|
23
|
+
rescue ::JSON::Schema::ValidationError => e
|
|
24
|
+
raise PDK::Config::LoadError, format('The configuration file %{filename} is not valid: %{message}', filename: filename, message: e.message)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
require 'pdk/config/json_schema_setting'
|
|
28
|
+
|
|
29
|
+
schema_property_names.each do |key|
|
|
30
|
+
yield key, PDK::Config::JSONSchemaSetting.new(key, self, @raw_data[key])
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Remove all of the "known" settings from the schema and
|
|
34
|
+
# we're left with the settings that we don't manage.
|
|
35
|
+
self.unmanaged_settings = @raw_data.reject { |k, _| schema_property_names.include?(k) }
|
|
36
|
+
rescue Psych::SyntaxError => e
|
|
37
|
+
raise PDK::Config::LoadError, format('Syntax error when loading %{file}: %{error}', file: filename, error: "#{e.problem} #{e.context}")
|
|
38
|
+
rescue Psych::DisallowedClass => e
|
|
39
|
+
raise PDK::Config::LoadError, format('Unsupported class in %{file}: %{error}', file: filename, error: e.message)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Serializes object data into a YAML string.
|
|
43
|
+
#
|
|
44
|
+
# @see PDK::Config::Namespace.serialize_data
|
|
45
|
+
def serialize_data(data)
|
|
46
|
+
require 'yaml'
|
|
47
|
+
::YAML.dump(data)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
data/lib/pdk/config.rb
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
require 'pdk'
|
|
2
|
+
|
|
3
|
+
module PDK
|
|
4
|
+
class Config
|
|
5
|
+
autoload :IniFile, 'pdk/config/ini_file'
|
|
6
|
+
autoload :IniFileSetting, 'pdk/config/ini_file_setting'
|
|
7
|
+
autoload :JSON, 'pdk/config/json'
|
|
8
|
+
autoload :JSONSchemaNamespace, 'pdk/config/json_schema_namespace'
|
|
9
|
+
autoload :JSONSchemaSetting, 'pdk/config/json_schema_setting'
|
|
10
|
+
autoload :LoadError, 'pdk/config/errors'
|
|
11
|
+
autoload :Namespace, 'pdk/config/namespace'
|
|
12
|
+
autoload :Setting, 'pdk/config/setting'
|
|
13
|
+
autoload :Validator, 'pdk/config/validator'
|
|
14
|
+
autoload :YAML, 'pdk/config/yaml'
|
|
15
|
+
|
|
16
|
+
# Create a new instance of the PDK Configuration
|
|
17
|
+
# @param options [Hash[String => Object]] Optional hash to override configuration options
|
|
18
|
+
# @option options [String] 'system.path' Path to the system PDK configuration file
|
|
19
|
+
# @option options [String] 'system.module_defaults.path' Path to the system module answers PDK configuration file
|
|
20
|
+
# @option options [String] 'user.path' Path to the user PDK configuration file
|
|
21
|
+
# @option options [String] 'user.module_defaults.path' Path to the user module answers PDK configuration file
|
|
22
|
+
# @option options [PDK::Context::AbstractContext] 'context' The context that the configuration should be created in
|
|
23
|
+
def initialize(options = nil)
|
|
24
|
+
options = {} if options.nil?
|
|
25
|
+
@config_options = {
|
|
26
|
+
'system.path' => PDK::Config.system_config_path,
|
|
27
|
+
'system.module_defaults.path' => PDK::Config.system_answers_path,
|
|
28
|
+
'user.path' => PDK::Config.user_config_path,
|
|
29
|
+
'user.module_defaults.path' => PDK::AnswerFile.default_answer_file_path,
|
|
30
|
+
'context' => PDK.context
|
|
31
|
+
}.merge(options)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# The system level configuration settings.
|
|
35
|
+
# @return [PDK::Config::Namespace]
|
|
36
|
+
# @api private
|
|
37
|
+
def system_config
|
|
38
|
+
local_options = @config_options
|
|
39
|
+
@system_config ||= PDK::Config::JSON.new('system', file: local_options['system.path']) do
|
|
40
|
+
mount :module_defaults, PDK::Config::JSON.new(file: local_options['system.module_defaults.path'])
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# The user level configuration settings.
|
|
45
|
+
# @return [PDK::Config::Namespace]
|
|
46
|
+
# @api private
|
|
47
|
+
def user_config
|
|
48
|
+
local_options = @config_options
|
|
49
|
+
@user_config ||= PDK::Config::JSON.new('user', file: local_options['user.path']) do
|
|
50
|
+
mount :module_defaults, PDK::Config::JSON.new(file: local_options['user.module_defaults.path'])
|
|
51
|
+
|
|
52
|
+
# Display the feature flags
|
|
53
|
+
mount :pdk_feature_flags, PDK::Config::Namespace.new('pdk_feature_flags') do
|
|
54
|
+
setting 'available' do
|
|
55
|
+
default_to { PDK.available_feature_flags }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
setting 'requested' do
|
|
59
|
+
default_to { PDK.requested_feature_flags }
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# The project level configuration settings.
|
|
66
|
+
# @return [PDK::Config::Namespace]
|
|
67
|
+
# @api private
|
|
68
|
+
def project_config
|
|
69
|
+
context = @config_options['context']
|
|
70
|
+
@project ||= PDK::Config::Namespace.new('project') do
|
|
71
|
+
mount :environment, PDK::ControlRepo.environment_conf_as_config(File.join(context.root_path, 'environment.conf')) if context.is_a?(PDK::Context::ControlRepo)
|
|
72
|
+
|
|
73
|
+
mount :validate, PDK::Config::YAML.new('validate', file: File.join(context.root_path, 'pdk.yaml'), persistent_defaults: true) do
|
|
74
|
+
setting 'ignore' do
|
|
75
|
+
default_to { [] }
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Resolves *all* filtered settings from all namespaces
|
|
82
|
+
#
|
|
83
|
+
# @param filter [String] Only resolve setting names which match the filter. See PDK::Config::Namespace.be_resolved? for matching rules
|
|
84
|
+
# @return [Hash{String => Object}] All resolved settings for example {'user.module_defaults.author' => 'johndoe'}
|
|
85
|
+
def resolve(filter = nil)
|
|
86
|
+
all_scopes.values.reverse.reduce({}) do |result, method_name|
|
|
87
|
+
result.merge(send(method_name).resolve(filter))
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Returns a configuration setting by name. This name can either be a String, Array or parameters e.g. These are equivalent
|
|
92
|
+
# - PDK.config.get('user.a.b.c')
|
|
93
|
+
# - PDK.config.get(['user', 'a', 'b', 'c'])
|
|
94
|
+
# - PDK.config.get('user', 'a', 'b', 'c')
|
|
95
|
+
# @param root [Array[String], String] The root setting name or the entire setting name as a single string
|
|
96
|
+
# @param keys [String] The child names of the setting
|
|
97
|
+
# @return [PDK::Config::Namespace, Object, nil] The value of the configuration setting. Returns nil if it does no exist
|
|
98
|
+
def get(root, *keys)
|
|
99
|
+
return nil if root.nil? || root.empty?
|
|
100
|
+
|
|
101
|
+
if keys.empty?
|
|
102
|
+
case root
|
|
103
|
+
when Array
|
|
104
|
+
name = root
|
|
105
|
+
when String
|
|
106
|
+
name = split_key_string(root)
|
|
107
|
+
else
|
|
108
|
+
return nil
|
|
109
|
+
end
|
|
110
|
+
else
|
|
111
|
+
name = [root].concat(keys)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
get_within_scopes(name[1..], [name[0]])
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Returns a configuration setting by name, using scope precedence rules. If no scopes are passed, then all scopes are queried using the default precedence rules
|
|
118
|
+
# @setting_name [String, Array[String]] The setting name to retrieve without the leading scope name e.g. Use 'setting' instead of 'system.setting'
|
|
119
|
+
# @scopes [Nil, Array[String]] The list of scopes, in order, to query in turn for the setting_name. Invalid or missing scopes are ignored.
|
|
120
|
+
# @return [PDK::Config::Namespace, Object, nil] The value of the configuration setting. Returns nil if it does no exist
|
|
121
|
+
def get_within_scopes(setting_name, scopes = nil)
|
|
122
|
+
raise ArgumentError, format('Expected an Array but got \'%{klass}\' for scopes', klass: scopes.class) unless scopes.nil? || scopes.is_a?(Array)
|
|
123
|
+
raise ArgumentError, format('Expected an Array or String but got \'%{klass}\' for setting_name', klass: setting_name.class) unless setting_name.is_a?(Array) || setting_name.is_a?(String)
|
|
124
|
+
|
|
125
|
+
setting_arr = setting_name.is_a?(String) ? split_key_string(setting_name) : setting_name
|
|
126
|
+
all_scope_names = all_scopes.keys
|
|
127
|
+
|
|
128
|
+
# Use only valid scope names
|
|
129
|
+
scopes = scopes.nil? ? all_scope_names : scopes & all_scope_names
|
|
130
|
+
|
|
131
|
+
scopes.each do |scope_name|
|
|
132
|
+
value = traverse_object(send(all_scopes[scope_name]), *setting_arr)
|
|
133
|
+
return value unless value.nil?
|
|
134
|
+
end
|
|
135
|
+
nil
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Yields a configuration setting value by name, using scope precedence rules. If no scopes are passed, then all scopes are queried using the default precedence rules
|
|
139
|
+
# @setting_name [String, Array[String]] The setting name to retrieve without the leading scope name e.g. Use 'setting' instead of 'system.setting'
|
|
140
|
+
# @scopes [Nil, Array[String]] The list of scopes, in order, to query in turn for the setting_name. Invalid or missing scopes are ignored.
|
|
141
|
+
# @yield [PDK::Config::Namespace, Object] The value of the configuration setting. Does not yield if the setting does not exist or is nil
|
|
142
|
+
def with_scoped_value(setting_name, scopes = nil)
|
|
143
|
+
raise ArgumentError, 'must be passed a block' unless block_given?
|
|
144
|
+
|
|
145
|
+
value = get_within_scopes(setting_name, scopes)
|
|
146
|
+
yield value unless value.nil?
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Sets a configuration setting by name. This name can either be a String or an Array
|
|
150
|
+
# - PDK.config.set('user.a.b.c', ...)
|
|
151
|
+
# - PDK.config.set(['user', 'a', 'b', 'c'], ...)
|
|
152
|
+
# @param key [String, Array[String]] The name of the configuration key to change
|
|
153
|
+
# @param value [Object] The value to set the configuration setting to
|
|
154
|
+
# @param options [Hash] Changes the behaviour of the setting process
|
|
155
|
+
# @option options [Boolean] :force Disables any munging or array processing, and sets the value as it is. Default is false
|
|
156
|
+
# @return [Object] The new value of the configuration setting
|
|
157
|
+
def set(key, value, options = {})
|
|
158
|
+
options = {
|
|
159
|
+
force: false
|
|
160
|
+
}.merge(options)
|
|
161
|
+
|
|
162
|
+
names = key.is_a?(String) ? split_key_string(key) : key
|
|
163
|
+
raise ArgumentError, 'Invalid configuration names' if names.nil? || !names.is_a?(Array) || names.empty?
|
|
164
|
+
|
|
165
|
+
scope_name = names[0]
|
|
166
|
+
raise ArgumentError, format("Unknown configuration root '%{name}'", name: scope_name) if all_scopes[scope_name].nil?
|
|
167
|
+
|
|
168
|
+
deep_set_object(value, options[:force], send(all_scopes[scope_name]), *names[1..])
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def self.user_config_path
|
|
172
|
+
File.join(PDK::Util.configdir, 'user_config.json')
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def self.system_config_path
|
|
176
|
+
File.join(PDK::Util.system_configdir, 'system_config.json')
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def self.system_answers_path
|
|
180
|
+
File.join(PDK::Util.system_configdir, 'answers.json')
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def self.json_schemas_path
|
|
184
|
+
File.join(__dir__, 'config')
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# return nil if not exist
|
|
188
|
+
def self.json_schema(name)
|
|
189
|
+
File.join(json_schemas_path, "#{name}_schema.json")
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
private
|
|
193
|
+
|
|
194
|
+
# :nocov: This is a private method and is tested elsewhere
|
|
195
|
+
def traverse_object(object, *names)
|
|
196
|
+
return nil if object.nil? || !object.respond_to?(:[])
|
|
197
|
+
return nil if names.nil?
|
|
198
|
+
# It's possible to pass in empty names at the root traversal layer
|
|
199
|
+
# but this should _only_ happen at the root namespace level
|
|
200
|
+
if names.empty?
|
|
201
|
+
return (object.is_a?(PDK::Config::Namespace) ? object : nil)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
name = names.shift
|
|
205
|
+
value = object[name]
|
|
206
|
+
if names.empty?
|
|
207
|
+
return value if value.is_a?(PDK::Config::Namespace)
|
|
208
|
+
|
|
209
|
+
# Duplicate arrays and hashes so that they are isolated from changes being made
|
|
210
|
+
value.is_a?(Hash) || value.is_a?(Array) ? value.dup : value
|
|
211
|
+
else
|
|
212
|
+
traverse_object(value, *names)
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
# :nocov:
|
|
216
|
+
|
|
217
|
+
# :nocov: This is a private method and is tested elsewhere
|
|
218
|
+
# Takes a string representation of a setting and splits into its constituent setting parts e.g.
|
|
219
|
+
# 'user.a.b.c' becomes ['user', 'a', 'b', 'c']
|
|
220
|
+
# @return [Array[String]] The string split into each setting name as an array
|
|
221
|
+
def split_key_string(key)
|
|
222
|
+
raise ArgumentError, format('Expected a String but got \'%{klass}\'', klass: key.class) unless key.is_a?(String)
|
|
223
|
+
|
|
224
|
+
key.split('.')
|
|
225
|
+
end
|
|
226
|
+
# :nocov:
|
|
227
|
+
|
|
228
|
+
# :nocov:
|
|
229
|
+
# Returns all known scope names and their associated method name to call, to query the scope
|
|
230
|
+
# Note - Order is important. This dictates the resolution precedence order (topmost is processed first)
|
|
231
|
+
# @return [Hash[String, Symbol]] A hash of the scope name then method name to call to query the scope (as a Symbol)
|
|
232
|
+
def all_scopes
|
|
233
|
+
# Note - Order is important. This dictates the resolution precedence order (topmost is processed first)
|
|
234
|
+
{
|
|
235
|
+
'project' => :project_config,
|
|
236
|
+
'user' => :user_config,
|
|
237
|
+
'system' => :system_config
|
|
238
|
+
}.freeze
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# :nocov: This is a private method and is tested elsewhere
|
|
242
|
+
# Deeply traverses an object tree via `[]` and sets the last
|
|
243
|
+
# element to the value specified.
|
|
244
|
+
#
|
|
245
|
+
# Creating any missing parent hashes during the traversal
|
|
246
|
+
def deep_set_object(value, force, namespace, *names)
|
|
247
|
+
raise ArgumentError, 'Missing or invalid namespace' unless namespace.is_a?(PDK::Config::Namespace)
|
|
248
|
+
raise ArgumentError, 'Missing a name to set' if names.nil? || names.empty?
|
|
249
|
+
|
|
250
|
+
name = names.shift
|
|
251
|
+
current_value = namespace[name]
|
|
252
|
+
|
|
253
|
+
# If the next thing in the traversal chain is another namespace, set the value using that child namespace.
|
|
254
|
+
return deep_set_object(value, force, current_value, *names) if current_value.is_a?(PDK::Config::Namespace)
|
|
255
|
+
|
|
256
|
+
# We're at the end of the name traversal
|
|
257
|
+
if names.empty?
|
|
258
|
+
if force || !current_value.is_a?(Array)
|
|
259
|
+
namespace[name] = value
|
|
260
|
+
return value
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# Arrays are a special case if we're not forcing the value
|
|
264
|
+
namespace[name] = current_value << value unless current_value.include?(value)
|
|
265
|
+
return value
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Need to generate a deep hash using the current remaining names
|
|
269
|
+
# So given an origin *names of ['a', 'b', 'c', 'd'] and a value 'foo',
|
|
270
|
+
# we eventually want a hash of `{"b"=>{"c"=>{"d"=>"foo"}}}`
|
|
271
|
+
#
|
|
272
|
+
# The code above has already shifted the first element so we currently have
|
|
273
|
+
# name : 'a'
|
|
274
|
+
# names: ['b', 'c', 'd']
|
|
275
|
+
#
|
|
276
|
+
#
|
|
277
|
+
# First we need to pop off the last element ('d') in this case as we need to set that in the `reduce` call below
|
|
278
|
+
# So now we have:
|
|
279
|
+
# name : 'a'
|
|
280
|
+
# names: ['b', 'c']
|
|
281
|
+
# last_name : 'd'
|
|
282
|
+
last_name = names.pop
|
|
283
|
+
# Using reduce and an accumulator, we create the nested hash from the deepest value first. In this case the deepest value
|
|
284
|
+
# is the last_name, so the starting condition is {"d"=>"foo"}
|
|
285
|
+
# After the first iteration ('c'), the accumulator has {"c"=>{"d"=>"foo"}}}
|
|
286
|
+
# After the last iteration ('b'), the accumulator has {"b"=>{"c"=>{"d"=>"foo"}}}
|
|
287
|
+
hash_value = names.reverse.reduce(last_name => value) { |accumulator, item| { item => accumulator } }
|
|
288
|
+
|
|
289
|
+
# If the current value is nil, then it can't be a namespace or an existing value
|
|
290
|
+
# or
|
|
291
|
+
# If the current value is not a Hash and are forcing the change.
|
|
292
|
+
if current_value.nil? || (force && !current_value.is_a?(Hash))
|
|
293
|
+
namespace[name] = hash_value
|
|
294
|
+
return value
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
raise ArgumentError, format("Unable to set '%{key}' to '%{value}' as it is not a Hash", key: "#{namespace.name}.#{name}", value: hash_value) unless current_value.is_a?(Hash)
|
|
298
|
+
|
|
299
|
+
namespace[name] = current_value.merge(hash_value)
|
|
300
|
+
value
|
|
301
|
+
end
|
|
302
|
+
# :nocov:
|
|
303
|
+
end
|
|
304
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require 'pdk'
|
|
2
|
+
|
|
3
|
+
module PDK
|
|
4
|
+
module Context
|
|
5
|
+
# Represents a context for a directory based Control Repository
|
|
6
|
+
class ControlRepo < PDK::Context::AbstractContext
|
|
7
|
+
# @param repo_root [String] The root path for the control repo.
|
|
8
|
+
# @param context_path [String] The path where this context was created from e.g. Dir.pwd
|
|
9
|
+
# @see PDK::Context::AbstractContext
|
|
10
|
+
def initialize(repo_root, context_path)
|
|
11
|
+
super(context_path)
|
|
12
|
+
@root_path = repo_root
|
|
13
|
+
@environment_conf = nil
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @see PDK::Context::AbstractContext.pdk_compatible?
|
|
17
|
+
def pdk_compatible?
|
|
18
|
+
# Currently there is nothing to determine compatibility with the PDK for a
|
|
19
|
+
# Control Repo. For now assume everything is compatible
|
|
20
|
+
true
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# The modulepath setting for this control repository as an array of strings. These paths are relative
|
|
24
|
+
# and may contain interpolation strings (e.g. $basemodulepath)
|
|
25
|
+
# @see https://puppet.com/docs/puppet/latest/config_file_environment.html#allowed-settings
|
|
26
|
+
# @return [Array[String]] The modulepath setting for this control repository
|
|
27
|
+
def module_paths
|
|
28
|
+
return @module_paths unless @module_paths.nil?
|
|
29
|
+
|
|
30
|
+
value = environment_conf['modulepath'] || ''
|
|
31
|
+
# We have to use a hardcoded value here because File::PATH_SEPARATOR is ';' on Windows.
|
|
32
|
+
# As the environment.conf is only used on Puppet Server, it's always ':'
|
|
33
|
+
# Based on - https://github.com/puppetlabs/puppet/blob/f3e6d7e6d87f46408943a8e2176afb82ff6ea096/lib/puppet/settings/environment_conf.rb#L98-L106
|
|
34
|
+
@module_paths = value.split(':')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# The relative module_paths that exist on disk.
|
|
38
|
+
# @see https://puppet.com/docs/puppet/latest/config_file_environment.html#allowed-settings
|
|
39
|
+
# @return [Array[String]] The relative module paths on disk
|
|
40
|
+
def actualized_module_paths
|
|
41
|
+
@actualized_module_paths ||= module_paths.reject { |path| path.start_with?('$') }
|
|
42
|
+
.select { |path| PDK::Util::Filesystem.directory?(PDK::Util::Filesystem.expand_path(File.join(root_path, path))) }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# :nocov:
|
|
46
|
+
# @see PDK::Context::AbstractContext.display_name
|
|
47
|
+
def display_name
|
|
48
|
+
'a Control Repository context'
|
|
49
|
+
end
|
|
50
|
+
# :nocov:
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
# Memoization helper to read the environment.conf file.
|
|
55
|
+
# @return [PDK::Config::IniFile]
|
|
56
|
+
def environment_conf
|
|
57
|
+
@environment_conf ||= PDK::ControlRepo.environment_conf_as_config(File.join(root_path, 'environment.conf'))
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'pdk'
|
|
2
|
+
|
|
3
|
+
module PDK
|
|
4
|
+
module Context
|
|
5
|
+
# Represents a context for a Puppet Module
|
|
6
|
+
class Module < PDK::Context::AbstractContext
|
|
7
|
+
# @param module_root [String] The root path for the module.
|
|
8
|
+
# @param context_path [String] The path where this context was created from e.g. Dir.pwd
|
|
9
|
+
# @see PDK::Context::AbstractContext
|
|
10
|
+
def initialize(module_root, context_path)
|
|
11
|
+
super(context_path)
|
|
12
|
+
@root_path = module_root
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @see PDK::Context::AbstractContext.pdk_compatible?
|
|
16
|
+
def pdk_compatible?
|
|
17
|
+
PDK::Util.module_pdk_compatible?(root_path)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# :nocov:
|
|
21
|
+
# @see PDK::Context::AbstractContext.display_name
|
|
22
|
+
def display_name
|
|
23
|
+
'a Puppet Module context'
|
|
24
|
+
end
|
|
25
|
+
# :nocov:
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'pdk'
|
|
2
|
+
|
|
3
|
+
module PDK
|
|
4
|
+
module Context
|
|
5
|
+
# Represents a context which the PDK does not know. For example
|
|
6
|
+
# an empty directory
|
|
7
|
+
class None < PDK::Context::AbstractContext
|
|
8
|
+
# :nocov:
|
|
9
|
+
# @see PDK::Context::AbstractContext.display_name
|
|
10
|
+
def display_name
|
|
11
|
+
'an unknown context'
|
|
12
|
+
end
|
|
13
|
+
# :nocov:
|
|
14
|
+
|
|
15
|
+
# @see PDK::Context::AbstractContext.parent_context
|
|
16
|
+
def parent_context
|
|
17
|
+
# An unknown context has no parent
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|