Class: EC2::Snapshot::Replicator::Engine

Inherits:
Object
  • Object
show all
Defined in:
lib/ec2/snapshot/replicator/engine.rb

Constant Summary collapse

SOURCE_SNAPSHOT_ID_TAG_KEY =
'SourceSnapshotId'
DELETE_AFTER_TAG_KEY =
'DeleteAfter'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ Engine

Returns a new instance of Engine.



12
13
14
15
16
17
18
19
# File 'lib/ec2/snapshot/replicator/engine.rb', line 12

def initialize(config)
  @config = config

  set_credentials

  @source_ec2 = Aws::EC2::Resource.new(region: @config.source_region)
  @destination_ec2 = Aws::EC2::Resource.new(region: @config.destination_region)
end

Instance Attribute Details

#destination_ec2Object (readonly)

Returns the value of attribute destination_ec2.



10
11
12
# File 'lib/ec2/snapshot/replicator/engine.rb', line 10

def destination_ec2
  @destination_ec2
end

#source_ec2Object (readonly)

Returns the value of attribute source_ec2.



10
11
12
# File 'lib/ec2/snapshot/replicator/engine.rb', line 10

def source_ec2
  @source_ec2
end

Instance Method Details

#delete_snapshotsObject



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/ec2/snapshot/replicator/engine.rb', line 126

def delete_snapshots
  Logger.info ">>> deleting snapshots..."
  @destination_ec2.snapshots(owner_ids: [@config.owner_id]).each do |snapshot|
    tag = snapshot.tags.find {|t| t.key == DELETE_AFTER_TAG_KEY }

    unless tag
      Logger.debug "[#{snapshot.id}] tag #{DELETE_AFTER_TAG_KEY} is not found."
      next
    end

    delete_after = Time.at(tag.value.to_i)
    if delete_after < Time.now
      Logger.info "[#{snapshot.id}] deleting..."
      ask_continue("Delete #{snapshot.id}.")
      snapshot.delete
    end
  end
end

#mark_deleted_snapshotsObject



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/ec2/snapshot/replicator/engine.rb', line 83

def mark_deleted_snapshots
  Logger.info ">>> marking deleted snapshots..."

  destination_snapshots = @destination_ec2.snapshots(owner_ids: [@config.owner_id]).select do |snapshot|
    if snapshot.tags.find {|t| t.key == DELETE_AFTER_TAG_KEY }
      next false
    end

    unless snapshot.tags.find {|t| t.key == SOURCE_SNAPSHOT_ID_TAG_KEY }
      Logger.debug "[#{snapshot.id}] tag #{SOURCE_SNAPSHOT_ID_TAG_KEY} is not found."
      next false
    end

    true
  end

  source_snapshot_ids = destination_snapshots.map do |snapshot|
    snapshot.tags.find {|t| t.key == SOURCE_SNAPSHOT_ID_TAG_KEY }.value
  end

  # The maximum number of filter values specified on a single call is 200
  source_snapshots = source_snapshot_ids.each_slice(190).flat_map do |ids|
    @source_ec2.snapshots(owner_ids: [@config.owner_id], filters: [{name: 'snapshot-id', values: ids}]).to_a
  end

  destination_snapshots.each do |snapshot|
    source_snapshot_id = snapshot.tags.find {|t| t.key == SOURCE_SNAPSHOT_ID_TAG_KEY }.value
    if source_snapshots.find {|s| s.id == source_snapshot_id }
      Logger.debug "[#{snapshot.id}] source snapshot (#{source_snapshot_id}) exists"
    else
      Logger.info "[#{snapshot.id}] creating #{DELETE_AFTER_TAG_KEY} tag because source snapshot (#{source_snapshot_id}) is deleted."

      delete_after = (Time.now + @config.delay_deletion_sec).to_i
      ask_continue("Create a tag #{DELETE_AFTER_TAG_KEY}:#{delete_after}.")
      snapshot.create_tags(
        tags: [
          {key: DELETE_AFTER_TAG_KEY, value: delete_after.to_s},
        ],
      )
    end
  end
end

#replicate_snapshotsObject



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/ec2/snapshot/replicator/engine.rb', line 37

def replicate_snapshots
  Logger.info ">>> replicating snapshots..."

  source_snapshots = @source_ec2.snapshots(owner_ids: [@config.owner_id])
  source_snapshot_ids = source_snapshots.map {|s| s.id }

  # The maximum number of filter values specified on a single call is 200
  destination_snapshots = source_snapshot_ids.each_slice(190).flat_map do |ids|
    @destination_ec2.snapshots(
      owner_ids: [@config.owner_id],
      filters: [{name: "tag:#{SOURCE_SNAPSHOT_ID_TAG_KEY}", values: ids}]
    ).to_a
  end

  source_snapshots.each do |snapshot|
    destination_snapshot = destination_snapshots.find do |s|
      s.tags.find do |t|
        t.key == SOURCE_SNAPSHOT_ID_TAG_KEY &&
          t.value == snapshot.id
      end
    end

    if destination_snapshot
      Logger.debug "[#{snapshot.id}] already replicated"
    else
      Logger.info "[#{snapshot.id}] replicating..."

      ask_continue("Copy snapshot.")

      res = @destination_ec2.snapshot(snapshot.id).copy(
        source_region: @config.source_region,
        destination_region: @config.destination_region,
        description: "(replicated) #{snapshot.description}",
      )

      Logger.debug "[#{res.snapshot_id}] created in #{@config.destination_region}"
      copied_snapshot = @destination_ec2.snapshot(res.snapshot_id)
      copied_snapshot.create_tags(
        tags: [
          {key: SOURCE_SNAPSHOT_ID_TAG_KEY, value: snapshot.id},
        ],
      )
    end
  end
end

#run_onceObject



31
32
33
34
35
# File 'lib/ec2/snapshot/replicator/engine.rb', line 31

def run_once
  replicate_snapshots
  mark_deleted_snapshots
  delete_snapshots
end

#startObject



21
22
23
24
25
26
27
28
29
# File 'lib/ec2/snapshot/replicator/engine.rb', line 21

def start
  Logger.info "start loop"
  while true
    run_once

    Logger.info "sleeping for #{@config.interval_sec} sec..."
    sleep @config.interval_sec
  end
end