class Sfn::Utils::StackExporter
Stack serialization helper
Constants
- DEFAULT_CHEF_ENVIRONMENT
default chef environment name
- DEFAULT_EXPORT_STRUCTURE
default structure of export payload
- DEFAULT_OPTIONS
default instance options
Attributes
@return [Hash]
@return [Miasma::Models::Orchestration::Stack]
@return [Hash]
Public Class Methods
Create new instance
@param stack [Miasma::Models::Orchestration::Stack] @param options [Hash] @option options [KnifeCloudformation::Provider] :provider @option options [TrueClass, FalseClass] :chef_popsicle freeze run list @option options [Array<String>] :ignored_parameters @option options [String] :chef_environment_parameter
# File lib/sfn/utils/stack_exporter.rb, line 57 def initialize(stack, options = {}) @stack = stack @options = DEFAULT_OPTIONS.merge(options) @stack_export = Smash.new end
Public Instance Methods
Export stack
@return [Hash] exported stack
# File lib/sfn/utils/stack_exporter.rb, line 66 def export @stack_export = Smash.new(DEFAULT_EXPORT_STRUCTURE).tap do |stack_export| [:parameters, :capabilities, :notification_topics].each do |key| if val = stack.send(key) stack_export[:stack][key] = val end end stack_export[:stack][:template] = stack.template stack_export[:generator][:timestamp] = Time.now.to_i stack_export[:generator][:provider] = stack.provider.connection.provider if chef_popsicle? && defined?(Chef) freeze_runlists(stack_export) end remove_ignored_parameters(stack_export) stack_export[:stack][:template] = _to_json( stack_export[:stack][:template] ) end end
Provide query methods on options hash
@param args [Object] argument list @return [Object]
# File lib/sfn/utils/stack_exporter.rb, line 90 def method_missing(*args) m = args.first.to_s if m.end_with?("?") && options.has_key?(k = m.sub("?", "").to_sym) !!options[k] else super end end
Protected Instance Methods
Find latest available cookbook version within the configured environment
@param cookbook [String] name of cookbook @return [Chef::Version]
# File lib/sfn/utils/stack_exporter.rb, line 139 def allowed_cookbook_version(cookbook) restriction = environment.cookbook_versions[cookbook] requirement = Gem::Requirement.new(restriction) Chef::CookbookVersion.available_versions(cookbook).detect do |v| requirement.satisfied_by?(Gem::Version.new(v)) end end
Apply Join function
@param delim [String] join delimiter @param args [String, Hash] items to join @return [String]
# File lib/sfn/utils/stack_exporter.rb, line 263 def cf_join(delim, args) args.map do |arg| if arg.is_a?(Hash) cf_replace(arg) else arg.to_s end end.join(delim) end
Apply Ref function
@param ref_name [Hash] @return [Object] value in parameters
# File lib/sfn/utils/stack_exporter.rb, line 249 def cf_ref(ref_name) if stack.parameters.has_key?(ref_name) stack.parameters[ref_name] else raise KeyError.new("No parameter found with given reference name (#{ref_name}). " << "Only parameter based references supported!") end end
Apply cloudformation function to data
@param hsh [Object] stack template item @return [Object]
# File lib/sfn/utils/stack_exporter.rb, line 230 def cf_replace(hsh) if hsh.is_a?(Hash) case hsh.keys.first when "Fn::Join" cf_join(*hsh.values.first) when "Ref" cf_ref(hsh.values.first) else hsh end else hsh end end
Environment name to use when interacting with Chef
@param export [Hash] current export state @return [String] environment name
# File lib/sfn/utils/stack_exporter.rb, line 119 def chef_environment_name(export) if chef_environment_parameter? name = export[:stack][:options][:parameters][options[:chef_environment_parameter]] end name || DEFAULT_CHEF_ENVIRONMENT end
@return [Chef::Environment]
# File lib/sfn/utils/stack_exporter.rb, line 127 def environment unless @env @env = Chef::Environment.load("_default") end @env end
Extract the runlist item. Fully expands roles and provides version pegged runlist.
@param item [Chef::RunList::RunListItem, Array<String>] @return [Hash] new chef configuration hash @note this will expand all roles
# File lib/sfn/utils/stack_exporter.rb, line 153 def extract_runlist_item(item) rl_item = item.is_a?(Chef::RunList::RunListItem) ? item : Chef::RunList::RunListItem.new(item) static_content = Mash.new(:run_list => []) if rl_item.recipe? cookbook, recipe = rl_item.name.split("::") peg_version = allowed_cookbook_version(cookbook) static_content[:run_list] << "recipe[#{[cookbook, recipe || "default"].join("::")}@#{peg_version}]" elsif rl_item.role? role = Chef::Role.load(rl_item.name) role.run_list.each do |item| static_content = Chef::Mixin::DeepMerge.merge(static_content, extract_runlist_item(item)) end static_content = Chef::Mixin::DeepMerge.merge( static_content, Chef::Mixin::DeepMerge.merge(role.default_attributes, role.override_attributes) ) else raise TypeError.new("Unknown chef run list item encountered: #{rl_item.inspect}") end static_content end
Freeze chef run lists
@param exported [Hash] stack export @return [Hash]
# File lib/sfn/utils/stack_exporter.rb, line 195 def freeze_runlists(exported) first_runs = locate_runlists(exported) first_runs.each do |first_run| unpack_and_freeze_runlist(first_run) end exported end
Locate chef run lists within data collection
@param thing [Enumerable] collection from export @return [Enumerable] updated collection from export
# File lib/sfn/utils/stack_exporter.rb, line 207 def locate_runlists(thing) result = [] case thing when Hash if thing["content"] && thing["content"]["run_list"] result << thing["content"] else thing.each do |k, v| result += locate_runlists(v) end end when Array thing.each do |v| result += locate_runlists(v) end end result end
Remove parameter values from export that are configured to be ignored
@param export [Hash] stack export @return [Hash]
# File lib/sfn/utils/stack_exporter.rb, line 106 def remove_ignored_parameters(export) options[:ignored_parameters].each do |param| if export[:stack][:options][:parameters] export[:stack][:options][:parameters].delete(param) end end export end
Expand any detected chef run lists and freeze them within the stack template
@param first_run [Hash] chef first run hash @return [Hash]
# File lib/sfn/utils/stack_exporter.rb, line 179 def unpack_and_freeze_runlist(first_run) extracted_runlists = first_run["run_list"].map do |item| extract_runlist_item(cf_replace(item)) end first_run.delete("run_list") first_run.replace( extracted_runlists.inject(first_run) do |memo, first_run_item| Chef::Mixin::DeepMerge.merge(memo, first_run_item) end ) end