Class: Puppet::Module::Task
Defined Under Namespace
Classes: Error, InvalidFile, InvalidMetadata, InvalidName, InvalidTask, TaskNotFound
Constant Summary collapse
- FORBIDDEN_EXTENSIONS =
%w{.conf .md}- MOUNTS =
%w[lib files tasks]
Instance Attribute Summary collapse
-
#metadata ⇒ Object
readonly
Returns the value of attribute metadata.
-
#metadata_file ⇒ Object
readonly
Returns the value of attribute metadata_file.
-
#module ⇒ Object
readonly
Returns the value of attribute module.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
Class Method Summary collapse
-
.find_files(files, mod) ⇒ Object
Find task’s required lib files and retrieve paths for both ‘files’ and ‘implementation:files’ metadata keys.
-
.find_implementations(name, directory, metadata, executables) ⇒ Object
Copied from TaskInstantiator so we can use the Error classes here TODO: harmonize on one implementation Executables list should contain the full path of all possible implementation files.
- .get_file_details(path, mod) ⇒ Object
- .is_task_name?(name) ⇒ Boolean
- .is_tasks_executable_filename?(name) ⇒ Boolean
-
.is_tasks_filename?(path) ⇒ Boolean
Determine whether a file has a legal name for either a task’s executable or metadata file.
- .is_tasks_metadata_filename?(name) ⇒ Boolean
- .tasks_in_module(pup_module) ⇒ Object
Instance Method Summary collapse
- #==(other) ⇒ Object
- #files ⇒ Object
- #implementations ⇒ Object
-
#initialize(pup_module, task_name, module_executables, metadata_file = nil) ⇒ Task
constructor
file paths must be relative to the modules task directory.
- #read_metadata(file) ⇒ Object
- #validate ⇒ Object
Constructor Details
#initialize(pup_module, task_name, module_executables, metadata_file = nil) ⇒ Task
file paths must be relative to the modules task directory
196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/puppet/module/task.rb', line 196 def initialize(pup_module, task_name, module_executables, = nil) if !Puppet::Module::Task.is_task_name?(task_name) raise InvalidName, _("Task names must start with a lowercase letter and be composed of only lowercase letters, numbers, and underscores") end name = task_name == "init" ? pup_module.name : "#{pup_module.name}::#{task_name}" @module = pup_module @name = name = @module_executables = module_executables || [] end |
Instance Attribute Details
#metadata ⇒ Object (readonly)
Returns the value of attribute metadata.
193 194 195 |
# File 'lib/puppet/module/task.rb', line 193 def end |
#metadata_file ⇒ Object (readonly)
Returns the value of attribute metadata_file.
193 194 195 |
# File 'lib/puppet/module/task.rb', line 193 def end |
#module ⇒ Object (readonly)
Returns the value of attribute module.
193 194 195 |
# File 'lib/puppet/module/task.rb', line 193 def module @module end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
193 194 195 |
# File 'lib/puppet/module/task.rb', line 193 def name @name end |
Class Method Details
.find_files(files, mod) ⇒ Object
Find task’s required lib files and retrieve paths for both ‘files’ and ‘implementation:files’ metadata keys
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/puppet/module/task.rb', line 77 def self.find_files(files, mod) env = mod.environment.respond_to?(:name) ? mod.environment.name : 'production' file_list = files.flat_map do |file| module_name, mount, endpath = file.split("/", 3) # If there's a mount directory with no trailing slash this will be nil # We want it to be empty to construct a path endpath ||= '' pup_module = Puppet::Module.find(module_name, env) if pup_module.nil? msg = _("Could not find module %{module_name} containing task file %{filename}" % {module_name: module_name, filename: endpath}) raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata') end unless MOUNTS.include? mount msg = _("Files must be saved in module directories that Puppet makes available via mount points: %{mounts}" % {mounts: MOUNTS.join(', ')}) raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata') end path = File.join(pup_module.path, mount, endpath) unless File.absolute_path(path) == File.path(path).chomp('/') msg = _("File pathnames cannot include relative paths") raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata') end unless File.exist?(path) msg = _("Could not find %{path} on disk" % { path: path }) raise InvalidFile.new(msg) end last_char = file[-1] == '/' if File.directory?(path) unless last_char msg = _("Directories specified in task metadata must include a trailing slash: %{dir}" % { dir: file } ) raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata') end dir_files = Dir.glob("#{path}**/*").select { |f| File.file?(f) } files = dir_files.map { |f| get_file_details(f, pup_module) } else if last_char msg = _("Files specified in task metadata cannot include a trailing slash: %{file}" % { file: file } ) raise InvalidMetadata.new(msg, 'puppet.task/invalid-metadata') end files = get_file_details(path, pup_module) end files end return file_list end |
.find_implementations(name, directory, metadata, executables) ⇒ Object
Copied from TaskInstantiator so we can use the Error classes here TODO: harmonize on one implementation Executables list should contain the full path of all possible implementation files
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/puppet/module/task.rb', line 134 def self.find_implementations(name, directory, , executables) basename = name.split('::')[1] || 'init' # If 'implementations' is defined, it needs to mention at least one # implementation, and everything it mentions must exist. ||= {} if .key?('implementations') unless ['implementations'].is_a?(Array) msg = _("Task metadata for task %{name} does not specify implementations as an array" % { name: name }) raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata') end implementations = ['implementations'].map do |impl| path = executables.find { |real_impl| File.basename(real_impl) == impl['name'] } unless path msg = _("Task metadata for task %{name} specifies missing implementation %{implementation}" % { name: name, implementation: impl['name'] }) raise InvalidTask.new(msg, 'puppet.tasks/missing-implementation', { missing: [impl['name']] } ) end { "name" => impl['name'], "requirements" => impl.fetch('requirements', []), "path" => path } end return implementations end # If implementations isn't defined, then we use executables matching the # task name, and only one may exist. implementations = executables.select { |impl| File.basename(impl, '.*') == basename } if implementations.empty? msg = _('No source besides task metadata was found in directory %{directory} for task %{name}') % { name: name, directory: directory } raise InvalidTask.new(msg, 'puppet.tasks/no-implementation') elsif implementations.length > 1 msg =_("Multiple executables were found in directory %{directory} for task %{name}; define 'implementations' in metadata to differentiate between them") % { name: name, directory: implementations[0] } raise InvalidTask.new(msg, 'puppet.tasks/multiple-implementations') end [{ "name" => File.basename(implementations.first), "path" => implementations.first, "requirements" => [] }] end |
.get_file_details(path, mod) ⇒ Object
65 66 67 68 69 70 71 72 73 |
# File 'lib/puppet/module/task.rb', line 65 def self.get_file_details(path, mod) # This gets the path from the starting point onward # For files this should be the file subpath from the metadata # For directories it should be the directory subpath plus whatever we globbed # Partition matches on the first instance it finds of the parameter name = "#{mod.name}#{path.partition(mod.path).last}" { "name" => name, "path" => path } end |
.is_task_name?(name) ⇒ Boolean
50 51 52 53 |
# File 'lib/puppet/module/task.rb', line 50 def self.is_task_name?(name) return true if name =~ /^[a-z][a-z0-9_]*$/ return false end |
.is_tasks_executable_filename?(name) ⇒ Boolean
176 177 178 |
# File 'lib/puppet/module/task.rb', line 176 def self.is_tasks_executable_filename?(name) is_tasks_filename?(name) && !name.end_with?('.json') end |
.is_tasks_filename?(path) ⇒ Boolean
Determine whether a file has a legal name for either a task’s executable or metadata file.
56 57 58 59 60 61 62 63 |
# File 'lib/puppet/module/task.rb', line 56 def self.is_tasks_filename?(path) name_less_extension = File.basename(path, '.*') return false if not is_task_name?(name_less_extension) FORBIDDEN_EXTENSIONS.each do |ext| return false if path.end_with?(ext) end return true end |
.is_tasks_metadata_filename?(name) ⇒ Boolean
172 173 174 |
# File 'lib/puppet/module/task.rb', line 172 def self.(name) is_tasks_filename?(name) && name.end_with?('.json') end |
.tasks_in_module(pup_module) ⇒ Object
180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/puppet/module/task.rb', line 180 def self.tasks_in_module(pup_module) task_files = Dir.glob(File.join(pup_module.tasks_directory, '*')) .keep_if { |f| is_tasks_filename?(f) } module_executables = task_files.reject(&method(:is_tasks_metadata_filename?)).map.to_a tasks = task_files.group_by { |f| task_name_from_path(f) } tasks.map do |task, executables| new_with_files(pup_module, task, executables, module_executables) end end |
Instance Method Details
#==(other) ⇒ Object
250 251 252 253 |
# File 'lib/puppet/module/task.rb', line 250 def ==(other) self.name == other.name && self.module == other.module end |
#files ⇒ Object
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'lib/puppet/module/task.rb', line 226 def files md = outer_files = [] impl_lib_files = [] lib_files = [] unless md.nil? outer_files = md['files'] if md.key?('files') # There's definitely a more elegant way to do this... if md.key?('implementations') md['implementations'].each { |impl| impl_lib_files << impl['files'] if impl.key?('files') } end lib_files = self.class.find_files((impl_lib_files.flatten.uniq + outer_files).uniq, @module) end task_file = implementations.map {|imp| { 'name' => imp['name'], 'path' => imp['path'] } } # PXP agent relies on 'impls' (which is the task file) being first if there is no metadata task_file + lib_files end |
#implementations ⇒ Object
222 223 224 |
# File 'lib/puppet/module/task.rb', line 222 def implementations @implementations ||= self.class.find_implementations(@name, @module.tasks_directory, , @module_executables) end |
#read_metadata(file) ⇒ Object
209 210 211 212 213 214 215 216 |
# File 'lib/puppet/module/task.rb', line 209 def (file) Puppet::Util::Json.load(Puppet::FileSystem.read(file, :encoding => 'utf-8')) if file rescue SystemCallError, IOError => err msg = _("Error reading metadata: %{message}" % {message: err.}) raise InvalidMetadata.new(msg, 'puppet.tasks/unreadable-metadata') rescue Puppet::Util::Json::ParseError => err raise InvalidMetadata.new(err., 'puppet.tasks/unparseable-metadata') end |
#validate ⇒ Object
245 246 247 248 |
# File 'lib/puppet/module/task.rb', line 245 def validate implementations true end |