class Biosphere::CLI::TerraformPlanning
Public Instance Methods
build_terraform_targetting_plan(deployment, changes)
click to toggle source
# File lib/biosphere/cli/terraformplanning.rb, line 344 def build_terraform_targetting_plan(deployment, changes) # This function will output an array of objects which describe a proper and safe # plan for terraform resources to be applied. # # We can include following sets in this array: # - resources which do not belong to any group # - resources which belong to a group where count(group) == 1 # - a single resource from each group where count(group) > 1 # # Each item is an object with the following fields: # - :resource_name # - :target_group (might be null) # - :reason (human readable reason) # - :action (symbol :not_picked, :relaunch, :change, :create, :destroy) # plan = TerraformPlan.new() group_changes_map = {} resource_to_target_group_map = {} resources_not_in_any_target_group = {} deployment.all_resources.each do |resource| belong_to_target_group = false resource_name = resource[:type] + "." + resource[:name] deployment.target_groups.each do |group_name, resources| if resources.include?(resource_name) resource_to_target_group_map[resource_name] = group_name belong_to_target_group = true end end if !belong_to_target_group resources_not_in_any_target_group[resource_name] = { :resource_name => resource_name, :target_group => "", :reason => :no_target_group, :action => :relaunch } end end # Easy case first: new resources. We just want to lookup the group so that we can show that to the user changes[:new_resources].each do |change| group = resource_to_target_group_map[change] if group plan.items << { :resource_name => change, :target_group => group, :reason => "new resource", :action => :create } else plan.items << { :resource_name => change, :target_group => "", :reason => "new resource", :action => :create } end end # Easy case first: new resources. We just want to lookup the group so that we can show that to the user changes[:changes].each do |change| group = resource_to_target_group_map[change] if group plan.items << { :resource_name => change, :target_group => group, :reason => "non-destructive change", :action => :change } else plan.items << { :resource_name => change, :target_group => "", :reason => "non-destructive change", :action => :change } end end # Relaunches are more complex: we need to bucket resources based on group, so that we can later pick just one change from each group changes[:relaunches].each do |change| group = resource_to_target_group_map[change] if group group_changes_map[group] = (group_changes_map[group] ||= []) << change elsif resources_not_in_any_target_group[change] # this handles a change to a resource which does not belong to any target group plan.items << resources_not_in_any_target_group[change] else # this handles the case where a resource was removed from the definition and # now terraform wants to destroy this resource plan.items << { :resource_name => change, :target_group => "", :reason => "resource definition has been removed", :action => :destroy } end end # Handle safe groups: just one changing resource in the group safe_groups = group_changes_map.select { |name, resources| resources.length <= 1 } safe_groups.each do |group_name, resources| resources.each do |resource_name| plan.items << { :resource_name => resource_name, :target_group => group_name, :reason => "only member in its group", :action => :relaunch } end end # Handle problematic groups: select one from each group where count(group) > 1 problematic_groups = group_changes_map.select { |name, resources| resources.length > 1 } problematic_groups.each do |group_name, resources| original_length = resources.length plan.items << { :resource_name => resources.shift, :target_group => group_name, :reason => "group has total #{original_length} resources. Picked this as the first", :action => :relaunch } resources.each do |resource_name| plan.items << { :resource_name => resource_name, :target_group => group_name, :reason => "not selected from this group", :action => :not_picked } end end return plan end
generate_plan(deployment, tf_output_str, tf_graph_str = nil)
click to toggle source
# File lib/biosphere/cli/terraformplanning.rb, line 294 def generate_plan(deployment, tf_output_str, tf_graph_str = nil) if tf_graph_str @graph = Biosphere::TerraformGraph.new @graph.load(tf_graph_str) end data = parse_terraform_plan_output(tf_output_str) plan = build_terraform_targetting_plan(deployment, data) if @graph plan = @graph.filter_tf_plan(plan) end return plan end
parse_terraform_plan_output(str)
click to toggle source
returns object which contains interesting information on the terraform plan output.
:relaunches contains a list of resources which will be changed by a relaunch
# File lib/biosphere/cli/terraformplanning.rb, line 316 def parse_terraform_plan_output(str) relaunches = [] changes = [] new_resources = [] lines = str.split("\n") lines.each do |line| # the gsub is to strip possible ansi colors away # the match is to pick the TF notation about how the resource is about to change following the resource name itself m = line.gsub(/\e\[[0-9;]*m/, "").match(/^([-~+\/]+)\s(\S+)/) if m # If the resource action contains a minus ('-' or '-/+') then # we know that the action will be destructive. if m[1].match(/[-]/) relaunches << m[2] elsif m[1] == "~" changes << m[2] elsif m[1] == "+" new_resources << m[2] end end end return { :relaunches => relaunches, :changes => changes, :new_resources => new_resources, } end