lib/convection/model/template.rb in convection-2.2.29 vs lib/convection/model/template.rb in convection-2.3.0

- old
+ new

@@ -295,10 +295,48 @@ result.merge(resource_collection.resources) end end def diff(other, stack_ = nil, retain: false) - render(stack_, retain: retain).diff(other).map { |diff| Diff.new(diff[0], *diff[1]) } + # We want to accurately show when the DeletionPolicy is getting deleted and also when resources are going to be retained. + # Sample DeletionPolicy Removal output + # us-east-1 compare Compare local state of stack test-logs-deletion with remote template + # us-east-1 delete Resources.sgtestConvectionDeletion.DeletionPolicy + # + # Sample Mixed Retain/Delete Resources + # us-east-1 retain Resources.ELBLoggingPolicy.DependsOn.AWS::S3::BucketPolicy.0 + # us-east-1 retain Resources.ELBLoggingPolicy.DeletionPolicy + # us-east-1 delete Resources.sgtestConvectionDeletion.Type + # us-east-1 delete Resources.sgtestConvectionDeletion.Properties.AWS::EC2::SecurityGroup.GroupDescription + # us-east-1 delete Resources.sgtestConvectionDeletion.Properties.AWS::EC2::SecurityGroup.VpcId + # + events = render(stack_, retain: retain).diff(other).map { |diff| Diff.new(diff[0], *diff[1]) } + + # Top level events (changes to the resource directly) have keys with a format "Resources.{NAME}.{KEY}". + # So we can count the number of separators to find them. + top_level_events = events.select { |event| event.key.count('.') <= 2 } + + # We know something's a deleted resource when it has a top level "Type" attribute. + type_suffix = '.Type'.freeze + deleted_resources = top_level_events.select do |event| + event.action == :delete && event.key.end_with?(type_suffix) + end + deleted_resources.map! { |event| event.key[0...-type_suffix.length] } + + # We know something's a retainable resource when it has a top level "DeletionPolicy" attribute. + delete_policy_suffix = '.DeletionPolicy'.freeze + retainable_resources = top_level_events.select do |event| + event.action == :delete && event.key.end_with?(delete_policy_suffix) && event.theirs == 'Retain' + end + retainable_resources.map! { |event| event.key[0...-delete_policy_suffix.length] } + retainable_resources.keep_if { |name| deleted_resources.include?(name) } + + events.each do |event| + retained = retainable_resources.any? { |name| event.action == :delete && event.key.start_with?(name) } + event.action = :retain if retained + end + + events end def to_json(stack_ = nil, pretty = false, retain: false) rendered_stack = render(stack_, retain: retain) validate(rendered_stack)