22
40
$ puppet module list
23
41
/etc/puppet/modules
25
/usr/share/puppet/modules
42
├── bodepd-create_resources (v0.0.1)
43
├── puppetlabs-bacula (v0.0.2)
44
├── puppetlabs-mysql (v0.0.1)
45
├── puppetlabs-sqlite (v0.0.1)
46
└── puppetlabs-stdlib (v2.2.1)
47
/usr/share/puppet/modules (no modules installed)
49
List installed modules in a tree view:
51
$ puppet module list --tree
53
└─┬ puppetlabs-bacula (v0.0.2)
54
├── puppetlabs-stdlib (v2.2.1)
55
├─┬ puppetlabs-mysql (v0.0.1)
56
│ └── bodepd-create_resources (v0.0.1)
57
└── puppetlabs-sqlite (v0.0.1)
58
/usr/share/puppet/modules (no modules installed)
29
60
List installed modules from a specified environment:
31
$ puppet module list --env 'test'
62
$ puppet module list --environment production
64
├── bodepd-create_resources (v0.0.1)
65
├── puppetlabs-bacula (v0.0.2)
66
├── puppetlabs-mysql (v0.0.1)
67
├── puppetlabs-sqlite (v0.0.1)
68
└── puppetlabs-stdlib (v2.2.1)
69
/usr/share/puppet/modules (no modules installed)
35
71
List installed modules from a specified modulepath:
37
$ puppet module list --modulepath /tmp/facts1:/tmp/facts2
73
$ puppet module list --modulepath /usr/share/puppet/modules
74
/usr/share/puppet/modules (no modules installed)
44
77
when_invoked do |options|
45
78
Puppet[:modulepath] = options[:modulepath] if options[:modulepath]
46
environment = Puppet::Node::Environment.new(options[:env])
79
environment = Puppet::Node::Environment.new(options[:environment])
48
81
environment.modules_by_path
51
when_rendering :console do |modules_by_path|
84
when_rendering :console do |modules_by_path, options|
53
modules_by_path.each do |path, modules|
56
version_string = mod.version ? "(#{mod.version})" : ''
57
output << " #{mod.name} #{version_string}\n"
87
Puppet[:modulepath] = options[:modulepath] if options[:modulepath]
88
environment = Puppet::Node::Environment.new(options[:production])
91
:non_semantic_version => {
92
:title => "Non semantic version dependency"
95
:title => "Missing dependency"
97
:version_mismatch => {
98
:title => "Module '%s' (v%s) fails to meet some dependencies:"
103
error_types.each_key do |type|
104
@unmet_deps[type] = Hash.new do |hash, key|
105
hash[key] = { :errors => [], :parent => nil }
109
# Prepare the unmet dependencies for display on the console.
110
environment.modules.sort_by {|mod| mod.name}.each do |mod|
111
unmet_grouped = Hash.new { |h,k| h[k] = [] }
112
unmet_grouped = mod.unmet_dependencies.inject(unmet_grouped) do |acc, dep|
113
acc[dep[:reason]] << dep
116
unmet_grouped.each do |type, deps|
118
unmet_grouped[type].sort_by { |dep| dep[:name] }.each do |dep|
119
dep_name = dep[:name].gsub('/', '-')
120
installed_version = dep[:mod_details][:installed_version]
121
version_constraint = dep[:version_constraint]
122
parent_name = dep[:parent][:name].gsub('/', '-')
123
parent_version = dep[:parent][:version]
125
msg = "'#{parent_name}' (#{parent_version})"
126
msg << " requires '#{dep_name}' (#{version_constraint})"
127
@unmet_deps[type][dep[:name]][:errors] << msg
128
@unmet_deps[type][dep[:name]][:parent] = {
129
:name => dep[:parent][:name],
130
:version => parent_version
132
@unmet_deps[type][dep[:name]][:version] = installed_version
138
# Display unmet dependencies by category.
139
error_display_order = [:non_semantic_version, :version_mismatch, :missing]
140
error_display_order.each do |type|
141
unless @unmet_deps[type].empty?
142
@unmet_deps[type].keys.sort_by {|dep| dep }.each do |dep|
143
name = dep.gsub('/', '-')
144
title = error_types[type][:title]
145
errors = @unmet_deps[type][dep][:errors]
146
version = @unmet_deps[type][dep][:version]
149
when :version_mismatch
150
title % [name, version] + "\n"
151
when :non_semantic_version
152
title + " '#{name}' (v#{version}):\n"
154
title + " '#{name}':\n"
157
errors.each { |error_string| msg << " #{error_string}\n" }
158
Puppet.warning msg.chomp
163
environment.modulepath.each do |path|
164
modules = modules_by_path[path]
165
no_mods = modules.empty? ? ' (no modules installed)' : ''
166
output << "#{path}#{no_mods}\n"
169
# The modules with fewest things depending on them will be the
170
# parent of the tree. Can't assume to start with 0 dependencies
171
# since dependencies may be cyclical.
172
modules_by_num_requires = modules.sort_by {|m| m.required_by.size}
174
tree = list_build_tree(modules_by_num_requires, [], nil,
175
:label_unmet => true, :path => path, :label_invalid => false)
178
modules.sort_by { |mod| mod.forge_name or mod.name }.each do |mod|
179
tree << list_build_node(mod, path, :label_unmet => false,
180
:path => path, :label_invalid => true)
184
output << Puppet::ModuleTool.format_tree(tree)
191
# Prepare a list of module objects and their dependencies for print in a
194
# Returns an Array of Hashes
200
# :text => "puppetlabs-bacula (v0.0.2)",
202
# { :text => "puppetlabs-stdlib (v2.2.1)", :dependencies => [] },
204
# :text => "puppetlabs-mysql (v1.0.0)"
207
# :text => "bodepd-create_resources (v0.0.1)",
208
# :dependencies => []
212
# { :text => "puppetlabs-sqlite (v0.0.1)", :dependencies => [] },
217
# When the above data structure is passed to Puppet::ModuleTool.build_tree
218
# you end up with something like this:
220
# /etc/puppet/modules
221
# └─┬ puppetlabs-bacula (v0.0.2)
222
# ├── puppetlabs-stdlib (v2.2.1)
223
# ├─┬ puppetlabs-mysql (v1.0.0)
224
# │ └── bodepd-create_resources (v0.0.1)
225
# └── puppetlabs-sqlite (v0.0.1)
227
def list_build_tree(list, ancestors=[], parent=nil, params={})
229
next if @seen[(mod.forge_name or mod.name)]
230
node = list_build_node(mod, parent, params)
231
@seen[(mod.forge_name or mod.name)] = true
233
unless ancestors.include?(mod)
234
node[:dependencies] ||= []
235
missing_deps = mod.unmet_dependencies.select do |dep|
236
dep[:reason] == :missing
238
missing_deps.map do |mis_mod|
239
str = "#{colorize(:bg_red, 'UNMET DEPENDENCY')} #{mis_mod[:name].gsub('/', '-')} "
240
str << "(#{colorize(:cyan, mis_mod[:version_constraint])})"
241
node[:dependencies] << { :text => str }
243
node[:dependencies] += list_build_tree(mod.dependencies_as_modules,
244
ancestors + [mod], mod, params)
251
# Prepare a module object for print in a tree view. Each node in the tree
252
# must be a Hash in the following format:
254
# { :text => "puppetlabs-mysql (v1.0.0)" }
256
# The value of a module's :text is affected by three (3) factors: the format
257
# of the tree, it's dependency status, and the location in the modulepath
258
# relative to it's parent.
262
def list_build_node(mod, parent, params)
264
str << (mod.forge_name ? mod.forge_name.gsub('/', '-') : mod.name)
265
str << ' (' + colorize(:cyan, mod.version ? "v#{mod.version}" : '???') + ')'
267
unless File.dirname(mod.path) == params[:path]
268
str << " [#{File.dirname(mod.path)}]"
271
if @unmet_deps[:version_mismatch].include?(mod.forge_name)
272
if params[:label_invalid]
273
str << ' ' + colorize(:red, 'invalid')
274
elsif parent.respond_to?(:forge_name)
275
unmet_parent = @unmet_deps[:version_mismatch][mod.forge_name][:parent]
276
if (unmet_parent[:name] == parent.forge_name &&
277
unmet_parent[:version] == "v#{parent.version}")
278
str << ' ' + colorize(:red, 'invalid')