Class: ChefDK::PolicyfileCompiler

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/chef-dk/policyfile_compiler.rb

Constant Summary collapse

DEFAULT_DEMAND_CONSTRAINT =
'>= 0.0.0'.freeze
SOURCE_TYPES_WITH_FIXED_VERSIONS =

Cookbooks from these sources lock that cookbook to exactly one version

[:git, :path].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ui: nil) ⇒ PolicyfileCompiler

Returns a new instance of PolicyfileCompiler.



57
58
59
60
61
62
63
64
# File 'lib/chef-dk/policyfile_compiler.rb', line 57

def initialize(ui: nil)
  @storage_config = Policyfile::StorageConfig.new
  @dsl = Policyfile::DSL.new(storage_config)
  @artifact_server_cookbook_location_specs = {}

  @ui = ui || UI.null
  @install_report = Policyfile::Reports::Install.new(ui: @ui, policyfile_compiler: self)
end

Instance Attribute Details

#dslObject (readonly)

Returns the value of attribute dsl.



53
54
55
# File 'lib/chef-dk/policyfile_compiler.rb', line 53

def dsl
  @dsl
end

#install_reportObject (readonly)

Returns the value of attribute install_report.



55
56
57
# File 'lib/chef-dk/policyfile_compiler.rb', line 55

def install_report
  @install_report
end

#storage_configObject (readonly)

Returns the value of attribute storage_config.



54
55
56
# File 'lib/chef-dk/policyfile_compiler.rb', line 54

def storage_config
  @storage_config
end

Class Method Details

.evaluate(policyfile_string, policyfile_filename, ui: nil) ⇒ Object



39
40
41
42
43
# File 'lib/chef-dk/policyfile_compiler.rb', line 39

def self.evaluate(policyfile_string, policyfile_filename, ui: nil)
  compiler = new(ui: ui)
  compiler.evaluate_policyfile(policyfile_string, policyfile_filename)
  compiler
end

Instance Method Details

#all_cookbook_location_specsObject



131
132
133
134
135
136
137
138
# File 'lib/chef-dk/policyfile_compiler.rb', line 131

def all_cookbook_location_specs
  # in the installation proces, we create "artifact_server_cookbook_location_specs"
  # for any cookbook that isn't sourced from a single-version source (e.g.,
  # path and git only support one version at a time), but we might have
  # specs for them to track additional version constraint demands. Merging
  # in this order ensures the artifact_server_cookbook_location_specs "win".
  cookbook_location_specs.merge(@artifact_server_cookbook_location_specs)
end

#artifacts_graphObject



190
191
192
# File 'lib/chef-dk/policyfile_compiler.rb', line 190

def artifacts_graph
  remote_artifacts_graph.merge(local_artifacts_graph)
end

#build {|@dsl| ... } ⇒ Object

Yields:



240
241
242
243
# File 'lib/chef-dk/policyfile_compiler.rb', line 240

def build
  yield @dsl
  self
end

#cookbook_location_spec_for(cookbook_name) ⇒ Object



72
73
74
# File 'lib/chef-dk/policyfile_compiler.rb', line 72

def cookbook_location_spec_for(cookbook_name)
  cookbook_location_specs[cookbook_name]
end

#cookbook_version_fixed?(cookbook_name) ⇒ Boolean

Returns:

  • (Boolean)


223
224
225
226
227
228
229
# File 'lib/chef-dk/policyfile_compiler.rb', line 223

def cookbook_version_fixed?(cookbook_name)
  if cookbook_location_spec = cookbook_location_spec_for(cookbook_name)
    cookbook_location_spec.version_fixed?
  else
    false
  end
end

#cookbooks_in_run_listObject



231
232
233
234
235
236
237
238
# File 'lib/chef-dk/policyfile_compiler.rb', line 231

def cookbooks_in_run_list
  combined_run_lists = expanded_named_run_lists.values.inject(expanded_run_list.to_a) do |accum_run_lists, run_list|
    accum_run_lists |= run_list.to_a
  end

  recipes = combined_run_lists.map {|recipe| recipe.name }
  recipes.map { |r| r[/^([^:]+)/, 1] }
end

#create_spec_for_cookbook(cookbook_name, version) ⇒ Object



125
126
127
128
129
# File 'lib/chef-dk/policyfile_compiler.rb', line 125

def create_spec_for_cookbook(cookbook_name, version)
  source_options = default_source.source_options_for(cookbook_name, version)
  spec = Policyfile::CookbookLocationSpecification.new(cookbook_name, "= #{version}", source_options, storage_config)
  @artifact_server_cookbook_location_specs[cookbook_name] = spec
end

#default_attributesObject



100
101
102
# File 'lib/chef-dk/policyfile_compiler.rb', line 100

def default_attributes
  dsl.node_attributes.combined_default.to_hash
end

#error!Object



66
67
68
69
70
# File 'lib/chef-dk/policyfile_compiler.rb', line 66

def error!
  unless errors.empty?
    raise PolicyfileError, errors.join("\n")
  end
end

#evaluate_policyfile(policyfile_string, policyfile_filename) ⇒ Object



245
246
247
248
249
# File 'lib/chef-dk/policyfile_compiler.rb', line 245

def evaluate_policyfile(policyfile_string, policyfile_filename)
  storage_config.use_policyfile(policyfile_filename)
  @dsl.eval_policyfile(policyfile_string)
  self
end

#expanded_named_run_listsObject



86
87
88
89
90
91
# File 'lib/chef-dk/policyfile_compiler.rb', line 86

def expanded_named_run_lists
  named_run_lists.inject({}) do |expanded, (name, run_list_items)|
    expanded[name] = Chef::RunList.new(*run_list_items)
    expanded
  end
end

#expanded_run_listObject



76
77
78
79
# File 'lib/chef-dk/policyfile_compiler.rb', line 76

def expanded_run_list
  # doesn't support roles yet...
  Chef::RunList.new(*run_list)
end

#fixed_version_cookbooks_specsObject



251
252
253
254
255
# File 'lib/chef-dk/policyfile_compiler.rb', line 251

def fixed_version_cookbooks_specs
  @fixed_version_cookbooks_specs ||= cookbook_location_specs.select do |_cookbook_name, cookbook_location_spec|
    cookbook_location_spec.version_fixed?
  end
end

#graphObject



150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/chef-dk/policyfile_compiler.rb', line 150

def graph
  @graph ||= Solve::Graph.new.tap do |g|
    artifacts_graph.each do |name, dependencies_by_version|
      dependencies_by_version.each do |version, dependencies|
        artifact = g.artifact(name, version)
        dependencies.each do |dep_name, constraint|
          artifact.dependency(dep_name, constraint)
        end
      end
    end
  end
end

#graph_demandsObject



177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/chef-dk/policyfile_compiler.rb', line 177

def graph_demands
  cookbooks_for_demands.map do |cookbook_name|
    spec = cookbook_location_spec_for(cookbook_name)
    if spec.nil?
      [ cookbook_name, DEFAULT_DEMAND_CONSTRAINT ]
    elsif spec.version_fixed?
      [ cookbook_name, "= #{spec.version}" ]
    else
      [ cookbook_name, spec.version_constraint.to_s ]
    end
  end
end

#graph_solutionObject

Compilation Methods



144
145
146
147
148
# File 'lib/chef-dk/policyfile_compiler.rb', line 144

def graph_solution
  return @solution if @solution
  cache_fixed_version_cookbooks
  @solution = Solve.it!(graph, graph_demands)
end

#installObject



112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/chef-dk/policyfile_compiler.rb', line 112

def install
  ensure_cache_dir_exists

  graph_solution.each do |cookbook_name, version|
    spec = cookbook_location_spec_for(cookbook_name)
    if spec.nil? or !spec.version_fixed?
      spec = create_spec_for_cookbook(cookbook_name, version)
      install_report.installing_cookbook(spec)
      spec.ensure_cached
    end
  end
end

#local_artifacts_graphObject

Gives a dependency graph for cookbooks that are source from an alternate location. These cookbooks could have a different set of dependencies compared to an unmodified copy upstream. For example, the community site may have a cookbook “apache2” at version “1.10.4”, which the user has forked on github and modified the dependencies without changing the version number. To accomodate this, the local_artifacts_graph should be merged over the upstream’s artifacts graph.



201
202
203
204
205
206
207
208
# File 'lib/chef-dk/policyfile_compiler.rb', line 201

def local_artifacts_graph
  cookbook_location_specs.inject({}) do |local_artifacts, (cookbook_name, cookbook_location_spec)|
    if cookbook_location_spec.version_fixed?
      local_artifacts[cookbook_name] = { cookbook_location_spec.version => cookbook_location_spec.dependencies }
    end
    local_artifacts
  end
end

#lockObject



108
109
110
# File 'lib/chef-dk/policyfile_compiler.rb', line 108

def lock
  @policyfile_lock ||= PolicyfileLock.build_from_compiler(self, storage_config)
end

#normalized_named_run_listsObject



93
94
95
96
97
98
# File 'lib/chef-dk/policyfile_compiler.rb', line 93

def normalized_named_run_lists
  expanded_named_run_lists.inject({}) do |normalized,(name, run_list)|
    normalized[name] = run_list.map { |i| normalize_recipe(i) }
    normalized
  end
end

#normalized_run_listObject

copy of the expanded_run_list, properly formatted for use in a lockfile



82
83
84
# File 'lib/chef-dk/policyfile_compiler.rb', line 82

def normalized_run_list
  expanded_run_list.map { |i| normalize_recipe(i) }
end

#override_attributesObject



104
105
106
# File 'lib/chef-dk/policyfile_compiler.rb', line 104

def override_attributes
  dsl.node_attributes.combined_override.to_hash
end

#remote_artifacts_graphObject



210
211
212
# File 'lib/chef-dk/policyfile_compiler.rb', line 210

def remote_artifacts_graph
  default_source.universe_graph
end

#solution_dependenciesObject



163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/chef-dk/policyfile_compiler.rb', line 163

def solution_dependencies
  solution_deps = Policyfile::SolutionDependencies.new

  all_cookbook_location_specs.each do |name, spec|
    solution_deps.add_policyfile_dep(name, spec.version_constraint)
  end

  graph_solution.each do |name, version|
    transitive_deps = artifacts_graph[name][version]
    solution_deps.add_cookbook_dep(name, version, transitive_deps)
  end
  solution_deps
end

#version_constraint_for(cookbook_name) ⇒ Object



214
215
216
217
218
219
220
221
# File 'lib/chef-dk/policyfile_compiler.rb', line 214

def version_constraint_for(cookbook_name)
  if (cookbook_location_spec = cookbook_location_spec_for(cookbook_name)) and cookbook_location_spec.version_fixed?
    version = cookbook_location_spec.version
    "= #{version}"
  else
    DEFAULT_DEMAND_CONSTRAINT
  end
end