Class: Bolt::Puppetfile

Inherits:
Object
  • Object
show all
Defined in:
lib/bolt/puppetfile.rb,
lib/bolt/puppetfile/module.rb,
lib/bolt/puppetfile/installer.rb

Defined Under Namespace

Classes: Installer, Module

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(modules = []) ⇒ Puppetfile

Returns a new instance of Puppetfile.



14
15
16
17
# File 'lib/bolt/puppetfile.rb', line 14

def initialize(modules = [])
  @modules = Set.new
  add_modules(modules)
end

Instance Attribute Details

#modulesObject (readonly)

Returns the value of attribute modules.



12
13
14
# File 'lib/bolt/puppetfile.rb', line 12

def modules
  @modules
end

Class Method Details

.parse(path) ⇒ Object

Loads a Puppetfile and parses its module specifications, returning a Bolt::Puppetfile object with the modules set.



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/bolt/puppetfile.rb', line 22

def self.parse(path)
  require 'puppetfile-resolver'
  require 'puppetfile-resolver/puppetfile/parser/r10k_eval'

  begin
    parsed = ::PuppetfileResolver::Puppetfile::Parser::R10KEval.parse(File.read(path))
  rescue StandardError => e
    raise Bolt::Error.new(
      "Unable to parse Puppetfile #{path}: #{e.message}",
      'bolt/puppetfile-parsing'
    )
  end

  unless parsed.valid?
    raise Bolt::ValidationError,
          "Unable to parse Puppetfile #{path}"
  end

  modules = parsed.modules.map do |mod|
    Bolt::Puppetfile::Module.new(mod.owner, mod.name, mod.version)
  end

  new(modules)
end

Instance Method Details

#add_modules(modules) ⇒ Object

Adds to the set of modules.



145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/bolt/puppetfile.rb', line 145

def add_modules(modules)
  modules.each do |mod|
    case mod
    when Bolt::Puppetfile::Module
      @modules << mod
    when Hash
      @modules << Bolt::Puppetfile::Module.from_hash(mod)
    else
      raise Bolt::ValidationError, "Module must be a Bolt::Puppetfile::Module or Hash."
    end
  end

  @modules
end

#resolveObject

Resolves module dependencies using the puppetfile-resolver library. The resolver will return a document model including all module dependencies and the latest version that can be installed for each. The document model is parsed and turned into a Set of Bolt::Puppetfile::Module objects.



76
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
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/bolt/puppetfile.rb', line 76

def resolve
  require 'puppetfile-resolver'

  # Build the document model from the modules.
  model = PuppetfileResolver::Puppetfile::Document.new('')

  @modules.each do |mod|
    model.add_module(
      PuppetfileResolver::Puppetfile::ForgeModule.new(mod.title).tap do |tap|
        tap.version = mod.version || :latest
      end
    )
  end

  # Make sure the Puppetfile model is valid.
  unless model.valid?
    raise Bolt::ValidationError,
          "Unable to resolve dependencies for modules: #{@modules.map(&:title).join(', ')}"
  end

  # Create the resolver using the Puppetfile model. nil disables Puppet
  # version restrictions.
  resolver = PuppetfileResolver::Resolver.new(model, nil)

  # Configure and resolve the dependency graph, catching any errors
  # raised by puppetfile-resolver and re-raising them as Bolt errors.
  begin
    result = resolver.resolve(
      cache:                 nil,
      ui:                    nil,
      module_paths:          [],
      allow_missing_modules: true
    )
  rescue StandardError => e
    raise Bolt::Error.new(e.message, 'bolt/puppetfile-resolver-error')
  end

  # Validate that the modules exist.
  missing_graph = result.specifications.select do |_name, spec|
    spec.instance_of? PuppetfileResolver::Models::MissingModuleSpecification
  end

  if missing_graph.any?
    titles = model.modules.each_with_object({}) do |mod, acc|
      acc[mod.name] = mod.title
    end

    names = titles.values_at(*missing_graph.keys)
    plural = names.count == 1 ? '' : 's'

    raise Bolt::Error.new(
      "Unknown module name#{plural} #{names.join(', ')}",
      'bolt/unknown-modules'
    )
  end

  # Filter the dependency graph to only include module specifications. This
  # will only remove the Puppet version specification, which is not needed.
  specs = result.specifications.select do |_name, spec|
    spec.instance_of? PuppetfileResolver::Models::ModuleSpecification
  end

  @modules = specs.map do |_name, spec|
    Bolt::Puppetfile::Module.new(spec.owner, spec.name, spec.version.to_s)
  end.to_set
end

#write(path, force: false) ⇒ Object

Writes a Puppetfile that includes specifications for each of the modules.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/bolt/puppetfile.rb', line 50

def write(path, force: false)
  if File.exist?(path) && !force
    raise Bolt::FileError.new(
      "Cannot overwrite existing Puppetfile at #{path}. To forcibly overwrite, "\
      "run with the '--force' option.",
      path
    )
  end

  File.open(path, 'w') do |file|
    file.puts '# This Puppetfile is managed by Bolt. Do not edit.'
    modules.each { |mod| file.puts mod.to_spec }
    file.puts
  end
rescue SystemCallError => e
  raise Bolt::FileError.new(
    "#{e.message}: unable to write Puppetfile.",
    path
  )
end