Class: Biosphere::CLI::TerraformPlanning
- Inherits:
-
Object
- Object
- Biosphere::CLI::TerraformPlanning
- Defined in:
- lib/biosphere/cli/terraformplanning.rb
Defined Under Namespace
Classes: TerraformPlan
Instance Method Summary collapse
- #build_terraform_targetting_plan(deployment, changes) ⇒ Object
- #generate_plan(deployment, tf_output_str, tf_graph_str = nil) ⇒ Object
-
#parse_terraform_plan_output(str) ⇒ Object
returns object which contains interesting information on the terraform plan output.
Instance Method Details
#build_terraform_targetting_plan(deployment, changes) ⇒ Object
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 |
# 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) ⇒ Object
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
# 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) ⇒ Object
returns object which contains interesting information on the terraform plan output.
:relaunches contains a list of resources which will be changed by a relaunch
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 |
# 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 |