class Puppet::Module::Task
Constants
- FORBIDDEN_EXTENSIONS
- MOUNTS
Attributes
metadata_file[R]
module[R]
name[R]
Public Class Methods
find_files(name, directory, metadata, executables, envname = nil)
click to toggle source
# File lib/puppet/module/task.rb 193 def self.find_files(name, directory, metadata, executables, envname = nil) 194 # PXP agent relies on 'impls' (which is the task file) being first if there is no metadata 195 find_implementations(name, directory, metadata, executables) + find_extra_files(metadata, envname) 196 end
is_task_name?(name)
click to toggle source
# File lib/puppet/module/task.rb 51 def self.is_task_name?(name) 52 return true if name =~ /^[a-z][a-z0-9_]*$/ 53 return false 54 end
is_tasks_executable_filename?(name)
click to toggle source
# File lib/puppet/module/task.rb 202 def self.is_tasks_executable_filename?(name) 203 is_tasks_filename?(name) && !name.end_with?('.json') 204 end
is_tasks_file?(path)
click to toggle source
# File lib/puppet/module/task.rb 56 def self.is_tasks_file?(path) 57 File.file?(path) && is_tasks_filename?(path) 58 end
is_tasks_filename?(path)
click to toggle source
Determine whether a file has a legal name for either a task's executable or metadata file.
# File lib/puppet/module/task.rb 61 def self.is_tasks_filename?(path) 62 name_less_extension = File.basename(path, '.*') 63 return false if not is_task_name?(name_less_extension) 64 FORBIDDEN_EXTENSIONS.each do |ext| 65 return false if path.end_with?(ext) 66 end 67 return true 68 end
is_tasks_metadata_filename?(name)
click to toggle source
# File lib/puppet/module/task.rb 198 def self.is_tasks_metadata_filename?(name) 199 is_tasks_filename?(name) && name.end_with?('.json') 200 end
new(pup_module, task_name, module_executables, metadata_file = nil)
click to toggle source
file paths must be relative to the modules task directory
# File lib/puppet/module/task.rb 222 def initialize(pup_module, task_name, module_executables, metadata_file = nil) 223 if !Puppet::Module::Task.is_task_name?(task_name) 224 raise InvalidName, _("Task names must start with a lowercase letter and be composed of only lowercase letters, numbers, and underscores") 225 end 226 227 name = task_name == "init" ? pup_module.name : "#{pup_module.name}::#{task_name}" 228 229 @module = pup_module 230 @name = name 231 @metadata_file = metadata_file 232 @module_executables = module_executables || [] 233 end
read_metadata(file)
click to toggle source
# File lib/puppet/module/task.rb 235 def self.read_metadata(file) 236 if file 237 content = Puppet::FileSystem.read(file, :encoding => 'utf-8') 238 content.empty? ? {} : Puppet::Util::Json.load(content) 239 end 240 rescue SystemCallError, IOError => err 241 msg = _("Error reading metadata: %{message}" % {message: err.message}) 242 raise InvalidMetadata.new(msg, 'puppet.tasks/unreadable-metadata') 243 rescue Puppet::Util::Json::ParseError => err 244 raise InvalidMetadata.new(err.message, 'puppet.tasks/unparseable-metadata') 245 end
tasks_in_module(pup_module)
click to toggle source
# File lib/puppet/module/task.rb 206 def self.tasks_in_module(pup_module) 207 task_files = Dir.glob(File.join(pup_module.tasks_directory, '*')) 208 .keep_if { |f| is_tasks_file?(f) } 209 210 module_executables = task_files.reject(&method(:is_tasks_metadata_filename?)).map.to_a 211 212 tasks = task_files.group_by { |f| task_name_from_path(f) } 213 214 tasks.map do |task, executables| 215 new_with_files(pup_module, task, executables, module_executables) 216 end 217 end
Private Class Methods
find_extra_files(metadata, envname = nil)
click to toggle source
Find task's required lib files and retrieve paths for both 'files' and 'implementation:files' metadata keys
# File lib/puppet/module/task.rb 82 def self.find_extra_files(metadata, envname = nil) 83 return [] if metadata.nil? 84 85 files = metadata.fetch('files', []) 86 unless files.is_a?(Array) 87 msg = _("The 'files' task metadata expects an array, got %{files}.") % {files: files} 88 raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata') 89 end 90 impl_files = metadata.fetch('implementations', []).flat_map do |impl| 91 file_array = impl.fetch('files', []) 92 unless file_array.is_a?(Array) 93 msg = _("The 'files' task metadata expects an array, got %{files}.") % {files: file_array} 94 raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata') 95 end 96 file_array 97 end 98 99 combined_files = files + impl_files 100 combined_files.uniq.flat_map do |file| 101 module_name, mount, endpath = file.split("/", 3) 102 # If there's a mount directory with no trailing slash this will be nil 103 # We want it to be empty to construct a path 104 endpath ||= '' 105 106 pup_module = Puppet::Module.find(module_name, envname) 107 if pup_module.nil? 108 msg = _("Could not find module %{module_name} containing task file %{filename}" % 109 {module_name: module_name, filename: endpath}) 110 raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata') 111 end 112 113 unless MOUNTS.include? mount 114 msg = _("Files must be saved in module directories that Puppet makes available via mount points: %{mounts}" % 115 {mounts: MOUNTS.join(', ')}) 116 raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata') 117 end 118 119 path = File.join(pup_module.path, mount, endpath) 120 unless File.absolute_path(path) == File.path(path).chomp('/') 121 msg = _("File pathnames cannot include relative paths") 122 raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata') 123 end 124 125 unless File.exist?(path) 126 msg = _("Could not find %{path} on disk" % { path: path }) 127 raise InvalidFile.new(msg) 128 end 129 130 last_char = file[-1] == '/' 131 if File.directory?(path) 132 unless last_char 133 msg = _("Directories specified in task metadata must include a trailing slash: %{dir}" % { dir: file } ) 134 raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata') 135 end 136 dir_files = Dir.glob("#{path}**/*").select { |f| File.file?(f) } 137 dir_files.map { |f| get_file_details(f, pup_module) } 138 else 139 if last_char 140 msg = _("Files specified in task metadata cannot include a trailing slash: %{file}" % { file: file } ) 141 raise InvalidMetadata.new(msg, 'puppet.task/invalid-metadata') 142 end 143 get_file_details(path, pup_module) 144 end 145 end 146 end
find_implementations(name, directory, metadata, executables)
click to toggle source
Executables list should contain the full path of all possible implementation files
# File lib/puppet/module/task.rb 150 def self.find_implementations(name, directory, metadata, executables) 151 basename = name.split('::')[1] || 'init' 152 # If 'implementations' is defined, it needs to mention at least one 153 # implementation, and everything it mentions must exist. 154 metadata ||= {} 155 if metadata.key?('implementations') 156 unless metadata['implementations'].is_a?(Array) 157 msg = _("Task metadata for task %{name} does not specify implementations as an array" % { name: name }) 158 raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata') 159 end 160 161 implementations = metadata['implementations'].map do |impl| 162 unless impl['requirements'].is_a?(Array) || impl['requirements'].nil? 163 msg = _("Task metadata for task %{name} does not specify requirements as an array" % { name: name }) 164 raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata') 165 end 166 path = executables.find { |real_impl| File.basename(real_impl) == impl['name'] } 167 unless path 168 msg = _("Task metadata for task %{name} specifies missing implementation %{implementation}" % { name: name, implementation: impl['name'] }) 169 raise InvalidTask.new(msg, 'puppet.tasks/missing-implementation', { missing: [impl['name']] } ) 170 end 171 { "name" => impl['name'], "path" => path } 172 end 173 return implementations 174 end 175 176 # If implementations isn't defined, then we use executables matching the 177 # task name, and only one may exist. 178 implementations = executables.select { |impl| File.basename(impl, '.*') == basename } 179 if implementations.empty? 180 msg = _('No source besides task metadata was found in directory %{directory} for task %{name}') % 181 { name: name, directory: directory } 182 raise InvalidTask.new(msg, 'puppet.tasks/no-implementation') 183 elsif implementations.length > 1 184 msg =_("Multiple executables were found in directory %{directory} for task %{name}; define 'implementations' in metadata to differentiate between them") % 185 { name: name, directory: implementations[0] } 186 raise InvalidTask.new(msg, 'puppet.tasks/multiple-implementations') 187 end 188 189 [{ "name" => File.basename(implementations.first), "path" => implementations.first }] 190 end
get_file_details(path, mod)
click to toggle source
# File lib/puppet/module/task.rb 70 def self.get_file_details(path, mod) 71 # This gets the path from the starting point onward 72 # For files this should be the file subpath from the metadata 73 # For directories it should be the directory subpath plus whatever we globbed 74 # Partition matches on the first instance it finds of the parameter 75 name = "#{mod.name}#{path.partition(mod.path).last}" 76 77 { "name" => name, "path" => path } 78 end
new_with_files(pup_module, name, task_files, module_executables)
click to toggle source
# File lib/puppet/module/task.rb 270 def self.new_with_files(pup_module, name, task_files, module_executables) 271 metadata_file = task_files.find { |f| is_tasks_metadata_filename?(f) } 272 Puppet::Module::Task.new(pup_module, name, module_executables, metadata_file) 273 end
task_name_from_path(path)
click to toggle source
Abstracted here so we can add support for subdirectories later
# File lib/puppet/module/task.rb 277 def self.task_name_from_path(path) 278 return File.basename(path, '.*') 279 end
Public Instance Methods
==(other)
click to toggle source
# File lib/puppet/module/task.rb 260 def ==(other) 261 self.name == other.name && 262 self.module == other.module 263 end
files()
click to toggle source
# File lib/puppet/module/task.rb 251 def files 252 @files ||= self.class.find_files(@name, @module.tasks_directory, metadata, @module_executables, environment_name) 253 end
metadata()
click to toggle source
# File lib/puppet/module/task.rb 247 def metadata 248 @metadata ||= self.class.read_metadata(@metadata_file) 249 end
validate()
click to toggle source
# File lib/puppet/module/task.rb 255 def validate 256 files 257 true 258 end
Private Instance Methods
environment_name()
click to toggle source
# File lib/puppet/module/task.rb 265 def environment_name 266 @module.environment.respond_to?(:name) ? @module.environment.name : 'production' 267 end