Class: Automan::RDS::Snapshot

Inherits:
Base
  • Object
show all
Includes:
Mixins::Utils
Defined in:
lib/automan/rds/snapshot.rb

Constant Summary

Constants included from Mixins::AwsCaller

Mixins::AwsCaller::S3_PROTO

Instance Attribute Summary

Attributes inherited from Base

#logger, #wait

Attributes included from Mixins::AwsCaller

#as, #cfn, #eb, #ec, #ec2, #elb, #r53, #rds, #s3

Instance Method Summary collapse

Methods included from Mixins::Utils

#region_from_az

Methods inherited from Base

add_option, #log_options, #wait_until

Methods included from Mixins::AwsCaller

#account, #configure_aws, #looks_like_s3_path?, #parse_s3_path, #s3_object_exists?, #s3_read

Constructor Details

#initialize(options = {}) ⇒ Snapshot

Returns a new instance of Snapshot.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/automan/rds/snapshot.rb', line 16

def initialize(options={})
  @prune = true
  @wait_for_completion = false
  @max_snapshots = 50
  super
  @wait = Wait.new({
    delay: 30,
    attempts: 20, # 20 x 30s == 10m
    debug: true,
    rescuer:  WaitRescuer.new(),
    logger:   @logger
  })

  # DEPRECATED: use --max-snapshots instead
  if ENV['MAX_SNAPSHOTS']
    @max_snapshots = ENV['MAX_SNAPSHOTS'].to_i
  end
end

Instance Method Details

#available?(snapshot) ⇒ Boolean

Returns:

  • (Boolean)


197
198
199
# File 'lib/automan/rds/snapshot.rb', line 197

def available?(snapshot)
  snapshot.status == 'available'
end

#count_snapshotsObject



250
251
252
253
254
255
256
257
258
259
# File 'lib/automan/rds/snapshot.rb', line 250

def count_snapshots
  log_options
  db = find_db
  if db.nil?
    logger.info "Database not found"
  else
    logger.info "Number of snapshots for database #{db.id} is #{snapshot_count}"
    logger.info "Number of prunable snapshots for database #{db.id} is #{prunable_snapshots.count}"
  end
end

#createObject



74
75
76
77
78
79
80
81
82
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
# File 'lib/automan/rds/snapshot.rb', line 74

def create
  log_options

  db = find_db

  if db.nil? || !db.exists?
    if quiet
      logger.warn "Database for #{environment} does not exist"
      exit
    else
      raise DatabaseDoesNotExistError, "Database for #{environment} does not exist"
    end
  end

  myname = name.nil? ? default_snapshot_name(db) : name.dup

  wait_until_database_available(db)

  logger.info "Creating snapshot #{myname} for #{db.id}"
  snap = db.create_snapshot(myname)

  if prune == true
    logger.info "Setting snapshot to be prunable and tagging environment"
    set_prunable_and_env(snap)
  end

  if wait_for_completion == true
    wait.until do
      logger.info "Waiting for snapshot to complete..."
      snapshot_finished?(snap)
    end
    logger.info "Snapshot finished (or timed out)."
  end
end

#database_available?(database) ⇒ Boolean

Returns:

  • (Boolean)


49
50
51
# File 'lib/automan/rds/snapshot.rb', line 49

def database_available?(database)
  database.db_instance_status == 'available'
end

#db_arn(database) ⇒ Object



136
137
138
139
# File 'lib/automan/rds/snapshot.rb', line 136

def db_arn(database)
  region = region_from_az(database.availability_zone_name)
  "arn:aws:rds:#{region}:#{}:db:#{database.id}"
end

#db_environment(db) ⇒ Object



116
117
118
# File 'lib/automan/rds/snapshot.rb', line 116

def db_environment(db)
  return db_tags(db)['Name']
end

#db_tags(db) ⇒ Object



170
171
172
173
# File 'lib/automan/rds/snapshot.rb', line 170

def db_tags(db)
  arn = db_arn(db)
  resource_tags(arn)
end

#default_snapshot_name(db) ⇒ Object



120
121
122
123
124
125
# File 'lib/automan/rds/snapshot.rb', line 120

def default_snapshot_name(db)
  env   = db_environment db
  stime = Time.new.strftime("%Y-%m-%dT%H-%M")

  return env + "-" + stime
end

#deleteObject



163
164
165
166
167
168
# File 'lib/automan/rds/snapshot.rb', line 163

def delete
  log_options

  logger.info "Deleting snapshot #{name}"
  rds.db_snapshots[name].delete
end

#find_dbObject

TODO move this and dependecies to an RDS utils class so we can re-use it



39
40
41
42
43
44
45
46
47
# File 'lib/automan/rds/snapshot.rb', line 39

def find_db
  db = nil
  if !database.nil?
    db = rds.db_instances[database]
  elsif !environment.nil?
    db = find_db_by_environment(environment)
  end
  db
end

#find_db_by_environment(environment) ⇒ Object



127
128
129
130
131
132
133
134
# File 'lib/automan/rds/snapshot.rb', line 127

def find_db_by_environment(environment)
  rds.db_instances.each do |db|
    if db_environment(db) == environment
      return db
    end
  end
  return nil
end

#get_all_snapshotsObject



201
202
203
# File 'lib/automan/rds/snapshot.rb', line 201

def get_all_snapshots
  rds.snapshots
end

#latestObject



261
262
263
264
265
266
267
268
269
270
# File 'lib/automan/rds/snapshot.rb', line 261

def latest
  log_options
  logger.info "Finding most recent snapshot for #{environment}"

  tags = { 'Environment' => environment }
  s = snapshots_with_tags(tags).sort_by {|s| s.created_at}.last

  logger.info "Most recent snapshot is #{s.id}"
  s.id
end

#prune_snapshotsObject



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/automan/rds/snapshot.rb', line 222

def prune_snapshots
  logger.info "Pruning old db snapshots"

  tags = {
    'Environment' => environment,
    'Type'        => type,
    'CanPrune'    => 'yes'
  }
  sorted_snaps = nil
  AWS.memoize do
    sorted_snaps = snapshots_with_tags(tags).sort_by {|s| s.created_at}
  end

  logger.info "snaps #{sorted_snaps.count} max #{max_snapshots}"

  if sorted_snaps.count >= max_snapshots
    logger.info "Too many snapshots ( #{sorted_snaps.count} >= #{max_snapshots})."
    number_to_delete = sorted_snaps.count - max_snapshots + 1
    logger.info "deleting #{number_to_delete}."

    condemned = sorted_snaps.take(number_to_delete)
    condemned.each do |snap|
      logger.info "Deleting #{snap.id}"
      snap.delete
    end
  end
end

#resource_tags(arn) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/automan/rds/snapshot.rb', line 180

def resource_tags(arn)
  opts = {
    resource_name: arn
  }
  response = rds.client.list_tags_for_resource opts

  unless response.successful?
    raise RequestFailedError "list_tags_for_resource failed: #{response.error}"
  end

  result = {}
  response.data[:tag_list].each do |t|
    result[ t[:key] ] = t[:value]
  end
  result
end

#set_prunable_and_env(snapshot) ⇒ Object

tag with CanPrune



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/automan/rds/snapshot.rb', line 147

def set_prunable_and_env(snapshot)
  opts = {
    resource_name: snapshot_arn(snapshot),
    tags: [
      { key: 'CanPrune',    value: 'yes'},
      { key: 'Environment', value: environment},
      { key: 'Type',        value: type }
    ]
  }
  response = rds.client.add_tags_to_resource opts

  unless response.successful?
    raise RequestFailedError "add_tags_to_resource failed: #{response.error}"
  end
end

#snapshot_arn(snapshot) ⇒ Object



141
142
143
144
# File 'lib/automan/rds/snapshot.rb', line 141

def snapshot_arn(snapshot)
  region = region_from_az(snapshot.availability_zone_name)
  "arn:aws:rds:#{region}:#{}:snapshot:#{snapshot.id}"
end

#snapshot_countObject



68
69
70
71
72
# File 'lib/automan/rds/snapshot.rb', line 68

def snapshot_count
  AWS.memoize do
    rds.db_instances[find_db.id].snapshots.count
  end
end

#snapshot_finished?(snapshot) ⇒ Boolean

Returns:

  • (Boolean)


109
110
111
112
113
114
# File 'lib/automan/rds/snapshot.rb', line 109

def snapshot_finished?(snapshot)
  unless snapshot.exists?
    return false
  end
  snapshot.status == "available"
end

#snapshot_has_tags?(snapshot, tags) ⇒ Boolean

tags = => ‘mytagvalue’, …

Returns:

  • (Boolean)


206
207
208
209
210
211
212
213
214
215
# File 'lib/automan/rds/snapshot.rb', line 206

def snapshot_has_tags?(snapshot, tags)
  snap_tags = snapshot_tags(snapshot)
  tags.each do |tk,tv|
    if snap_tags[tk] != tv
      #logger.info("qtags: #{tags.to_json} snap_tags: #{snap_tags.to_json}")
      return false
    end
  end
  true
end

#snapshot_tags(snapshot) ⇒ Object



175
176
177
178
# File 'lib/automan/rds/snapshot.rb', line 175

def snapshot_tags(snapshot)
  arn = snapshot_arn(snapshot)
  resource_tags(arn)
end

#snapshots_with_tags(tags) ⇒ Object

tags = => ‘mytagvalue’, …



218
219
220
# File 'lib/automan/rds/snapshot.rb', line 218

def snapshots_with_tags(tags)
  rds.snapshots.select {|s| snapshot_has_tags?(s, tags)}
end

#wait_until_database_available(database) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/automan/rds/snapshot.rb', line 53

def wait_until_database_available(database)
  state_wait = Wait.new({
    delay:    10,
    attempts: 20,
    debug:    true,
    rescuer:  WaitRescuer.new(),
    logger:   @logger
  })

  state_wait.until do
    logger.info "Waiting for database #{database.id} to be available"
    database_available?(database)
  end
end