Class: Zonify::AWS

Inherits:
Object
  • Object
show all
Defined in:
lib/zonify.rb

Overview

Set up for AWS interfaces and access to EC2 instance metadata.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ AWS

Returns a new instance of AWS.



34
35
36
37
38
39
40
41
42
43
# File 'lib/zonify.rb', line 34

def initialize(opts={})
  @ec2      = opts[:ec2]
  @elb      = opts[:elb]
  @r53      = opts[:r53]
  @ec2_proc = opts[:ec2_proc]
  @elb_proc = opts[:elb_proc]
  @r53_proc = opts[:r53_proc]
  @reverse  = opts[:reverse]
  @zone     = opts[:zone]
end

Instance Attribute Details

#ec2_procObject (readonly)

Returns the value of attribute ec2_proc.



33
34
35
# File 'lib/zonify.rb', line 33

def ec2_proc
  @ec2_proc
end

#elb_procObject (readonly)

Returns the value of attribute elb_proc.



33
34
35
# File 'lib/zonify.rb', line 33

def elb_proc
  @elb_proc
end

#r53_procObject (readonly)

Returns the value of attribute r53_proc.



33
34
35
# File 'lib/zonify.rb', line 33

def r53_proc
  @r53_proc
end

Class Method Details

.create(options) ⇒ Object

Initialize all AWS interfaces with the same access keys and logger (probably what you want to do). These are set up lazily; unused interfaces will not be initialized.



19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/zonify.rb', line 19

def create(options)
  options_ec2 = options.merge( :provider=>'AWS',
                               :connection_options=>{:nonblock=>false} )
  options_ec2.delete(:reverse)
  options_ec2.delete(:zone)
  ec2 = Proc.new{|| Fog::Compute.new(options_ec2) }
  options_elb = options_ec2.dup.delete_if{|k, _| k == :provider }
  elb = Proc.new{|| Fog::AWS::ELB.new(options_elb) }
  options_r53 = options_ec2.dup.delete_if{|k, _| k == :region }
  r53 = Proc.new{|| Fog::DNS.new(options_r53) }
  options.merge!(:ec2_proc=>ec2, :elb_proc=>elb, :r53_proc=>r53)
  Zonify::AWS.new(options)
end

.local_instance_idObject

Retrieve the EC2 instance ID of this instance.



12
13
14
15
# File 'lib/zonify.rb', line 12

def local_instance_id
  s = `curl -s http://169.254.169.254/latest/meta-data/instance-id`
  s.strip if $?.success?
end

Instance Method Details

#apply(changes, comment = 'Synced with Zonify tool.') ⇒ Object

Apply a changeset to the records in Route53. The records must all be under the same zone and suffix.



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
# File 'lib/zonify.rb', line 84

def apply(changes, comment='Synced with Zonify tool.')
  filtered = changes.select{|change| change[:value].length > 100 }
  # For all the SRV records that were considered too long, get the names of
  # the associated weighted CNAMEs.
  filtered_correlates = filtered.map do |change|
    case change[:name]
    when /^_[*][.]_[*][.]/
      change[:name][6, change[:name].length]
    end
  end.compact
  keep = changes.select do |change|
    change[:value].length <= 100 and not
      filtered_correlates.include?(change[:name])
  end
  unless keep.empty?
    suffix  = keep.first[:name] # Works because of longest submatch rule.
    zone, _ = route53_zone(suffix)
    Zonify.chunk_changesets(keep).each do |changeset|
      rekeyed = changeset.map do |record|
        record.inject({}) do |acc, pair|
          k, v = pair
          k_ = k == :value ? :resource_records : k
          acc[k_] = v
          acc
        end
      end
      begin
        r53.change_resource_record_sets(zone.id, rekeyed, :comment=>comment)
      rescue Fog::Errors::Error => e
        STDERR.puts("Failed with some records, due to:\n#{e}")
      end
    end
  end
  filtered
end

#ec2Object



47
48
49
# File 'lib/zonify.rb', line 47

def ec2
  @ec2 ||= @ec2_proc.call
end

#ec2_zoneObject

Generate DNS entries based on EC2 instances, security groups and ELB load balancers under the user’s AWS account.



58
59
60
# File 'lib/zonify.rb', line 58

def ec2_zone
  Zonify.tree(Zonify.zone(instances, load_balancers))
end

#eip_scanObject



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/zonify.rb', line 152

def eip_scan
  addresses = eips.map{|eip| eip.public_ip }
  result = {}
  addresses.each{|a| result[a] = [] }
  r53.zones.sort_by{|zone| zone.domain.reverse }.each do |zone|
    zone.records.all!.each do |rr|
      check = case rr.type
              when 'CNAME'
                rr.value.map do |s|
                  Zonify.ec2_dns_to_ip(s)
                end.compact
              when 'A','AAAA'
                rr.value
              end
      check ||= []
      found = addresses.select{|a| check.member? a }.sort
      unless found.empty?
        name = Zonify.read_octal(rr.name)
        found.each{|a| result[a] << name }
      end
    end
  end
  result
end

#eipsObject



149
150
151
# File 'lib/zonify.rb', line 149

def eips
  ec2.addresses
end

#elbObject



50
51
52
# File 'lib/zonify.rb', line 50

def elb
  @elb ||= @elb_proc.call
end

#instancesObject



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/zonify.rb', line 119

def instances
  ec2.servers.inject({}) do |acc, i|
    dns = if reverse_public_ip? and not i.public_ip_address.nil?
            pub = i.public_ip_address
            dns = "ec2-#{pub.gsub(".", "-")}.compute-1.amazonaws.com"
          else
            i.dns_name or i.private_dns_name
          end
    # The default hostname for EC2 instances is derived from their internal
    # DNS entry.
    terminal_states = %w| terminated shutting-down |
    unless dns.nil? or dns.empty? or terminal_states.member? i.state
      groups = (i.groups or [])
      attrs = { :sg => groups,
                :tags => (i.tags or []),
                :dns => Zonify.dot_(dns).downcase }
      if i.private_dns_name
        attrs[:priv] = i.private_dns_name.split('.').first.downcase
      end
      acc[i.id] = attrs
    end
    acc
  end
end

#load_balancersObject



143
144
145
146
147
148
# File 'lib/zonify.rb', line 143

def load_balancers
  elb.load_balancers.map do |elb|
    { :instances => elb.instances,
      :prefix    => Zonify.cut_down_elb_name(elb.dns_name.downcase) }
  end
end

#r53Object



53
54
55
# File 'lib/zonify.rb', line 53

def r53
  @r53 ||= @r53_proc.call
end

#reverse_public_ip?Boolean

Returns:

  • (Boolean)


44
45
46
# File 'lib/zonify.rb', line 44

def reverse_public_ip?
  !!@reverse
end

#route53_zone(suffix) ⇒ Object

Retrieve Route53 zone data – the zone ID as well as resource records – relevant to the given suffix. When there is any ambiguity, the zone with the longest name is chosen.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/zonify.rb', line 64

def route53_zone(suffix)
  suffix_ = Zonify.dot_(suffix)
  relevant_zone = r53.zones.select do |zone|
    if @zone
      @zone == zone.id
    else
      suffix_.end_with?(zone.domain)
    end
  end.sort_by{|zone| zone.domain.length }.last
  if relevant_zone
    relevant_records = relevant_zone.records.all!.map do |rr|
      if rr.name.end_with?(suffix_)
        rr.attributes.merge(:name=>Zonify.read_octal(rr.name))
      end
    end.compact
    [relevant_zone, Zonify.tree_from_right_aws(relevant_records)]
  end
end