Class: EC2Launcher::Terminator

Inherits:
Object
  • Object
show all
Includes:
AWSInitializer, BackoffRunner
Defined in:
lib/ec2launcher/terminator.rb

Instance Method Summary collapse

Methods included from BackoffRunner

#run_with_backoff, #test_with_backoff

Methods included from AWSInitializer

#initialize_aws

Constructor Details

#initialize(config_directory) ⇒ Terminator

Returns a new instance of Terminator.



17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/ec2launcher/terminator.rb', line 17

def initialize(config_directory)
  @log = Logger.new 'ec2launcher'
  log_output = Outputter.stdout
  log_output.formatter = PatternFormatter.new :pattern => "%m"
  @log.outputters = log_output

  ##############################
  # Load configuration data
  ##############################
  config_wrapper = ConfigWrapper.new(config_directory)

  @config = config_wrapper.config
  @environments = config_wrapper.environments
end

Instance Method Details

#remove_snapshots(ec2, attachments) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/ec2launcher/terminator.rb', line 117

def remove_snapshots(ec2, attachments)
  # Iterate over over volumes to find snapshots
  @log.info("Searching for snapshots...")
  snapshots = []
  attachments.each do |attachment|
    volume_snaps = ec2.snapshots.filter("volume-id", attachment.volume.id)
    volume_snaps.each {|volume_snapshot| snapshots << volume_snapshot }
  end

  @log.info("Deleting #{snapshots.size} snapshots...")
  snapshots.each do |snap|
    run_with_backoff(30, 1, "Deleting snapshot #{snap.id}") do
      snap.delete
    end
  end
end

#remove_volume(ec2, instance, device, volume) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/ec2launcher/terminator.rb', line 134

def remove_volume(ec2, instance, device, volume)
  @log.info("  Detaching #{volume.id}...")
  run_with_backoff(30, 1, "detaching #{volume.id}") do
    volume.detach_from(instance, device)
  end

  # Wait for volume to fully detach
  detached = test_with_backoff(120, 1, "waiting for #{volume.id} to detach") do
    volume.status == :available
  end

  # Volume failed to detach - do a force detatch instead
  unless detached
    @log.info("  Failed to detach #{volume.id}")
    run_with_backoff(60, 1, "force detaching #{volume.id}") do
      unless volume.status == :available
        volume.detach_from(instance, device, {:force => true})
      end
    end
    # Wait for volume to fully detach
    detached = test_with_backoff(120, 1, "waiting for #{volume.id} to force detach") do
      volume.status == :available
    end
  end

  @log.info("  Deleting volume #{volume.id}")
  run_with_backoff(30, 1, "delete volume #{volume.id}") do
    volume.delete
  end
end

#remove_volumes(ec2, attachments) ⇒ Object



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/ec2launcher/terminator.rb', line 165

def remove_volumes(ec2, attachments)
  @log.info("Cleaning up volumes...")

  AWS.memoize do
    removal_threads = []
    attachments.each do |attachment|
      if attachment.exists? && ! attachment.delete_on_termination
        removal_threads << Thread.new {
          remove_volume(ec2, attachment.instance, attachment.device, attachment.volume)
        }
      end
    end

    removal_threads.each {|t| t.join }
  end
end

#terminate(server_name, access_key, secret, snapshot_removal = true) ⇒ Object

Terminates a given server instance.

@param server_name Name of the server instance @param access_key Amazon IAM access key @param secret Amazon IAM secret key @param snapshot_removal Remove EBS snapshots for EBS volumes attached to the instance.



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
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
108
109
110
111
112
113
114
115
# File 'lib/ec2launcher/terminator.rb', line 38

def terminate(server_name, access_key, secret, snapshot_removal = true)
  ##############################
  # Initialize AWS and create EC2 connection
  ##############################
  initialize_aws(access_key, secret)
  ec2 = AWS::EC2.new

  ##############################
  # Find instance
  ##############################
  instance = nil
  AWS.memoize do
    instances = ec2.instances.filter("tag:Name", server_name)
    instances.each do |i|
      unless i.status == :shutting_down || i.status == :terminated
        instance = i
        break
      end # unless status
    end # instance loop
  end # memoize

  if instance
    environment_name = nil
    AWS.memoize do
      environment_name = instance.tags["environment"].strip
    end

    ##############################
    # ENVIRONMENT
    ##############################
    unless @environments.has_key? environment_name
      @log.fatal "Environment not found: '#{environment_name}'"
      exit 2
    end
    @environment = @environments[environment_name]

    ##############################
    # Create Route53 connection
    ##############################
    aws_route53 = nil
    aws_route53 = AWS::Route53.new if @environment.route53_zone_id
    route53 = EC2Launcher::Route53.new(aws_route53, @environment.route53_zone_id, @log)

    ##############################
    # EBS Volumes
    ##############################
    # Find EBS volumes
    attachments = nil
    AWS.memoize do
      attachments = instance.block_device_mappings.values

      # Remove snapshots
      remove_snapshots(ec2, attachments) if snapshot_removal

      # Remove volumes, if necessary
      remove_volumes(ec2, attachments)
    end

    private_ip_address = instance.private_ip_address
    
    run_with_backoff(30, 1, "terminating instance: #{server_name} [#{instance.instance_id}]") do
      instance.terminate
    end

    if route53
      @log.info("Deleting A record from Route53: #{server_name} => #{private_ip_address}")
      route53.delete_record_by_name(server_name, 'A')
    end

    @log.info("Deleting node/client from Chef: #{server_name}")
    node_result = `echo "Y" |knife node delete #{server_name}`
    client_result = `echo "Y" |knife client delete #{server_name}`
    @log.debug("Deleted Chef node: #{node_result}")
    @log.debug("Deleted Chef client: #{client_result}")
  else
    @log.error("Unable to find instance: #{server_name}")
  end
end