Class: Bosh::Agent::Infrastructure::Openstack::Registry

Inherits:
Object
  • Object
show all
Defined in:
lib/bosh_agent/infrastructure/openstack/registry.rb

Constant Summary collapse

HTTP_API_TIMEOUT =
300
HTTP_CONNECT_TIMEOUT =
30
META_DATA_URI =
"http://169.254.169.254/latest"
USER_DATA_FILE =
File.join(File::SEPARATOR, "var", BOSH_APP_USER, "bosh", "user_data.json")

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.user_dataObject

Returns the value of attribute user_data.



7
8
9
# File 'lib/bosh_agent/infrastructure/openstack/registry.rb', line 7

def user_data
  @user_data
end

Class Method Details

.extract_registry_hostname(endpoint) ⇒ String

Extracts the hostname from the Bosh registry endpoint.



117
118
119
120
121
122
123
# File 'lib/bosh_agent/infrastructure/openstack/registry.rb', line 117

def extract_registry_hostname(endpoint)
  match = endpoint.match(%r{https*://([^:]+):})
  unless match && match.size == 2
    raise LoadSettingsError, "Cannot extract Bosh registry hostname from #{endpoint}"
  end
  match[1]
end

.get_openssh_keyString

Gets the OpenSSH public key. First we try to get it from the OpenStack meta data endpoint, if we fail, then we fallback to the injected user data file.



27
28
29
30
31
32
33
34
35
36
# File 'lib/bosh_agent/infrastructure/openstack/registry.rb', line 27

def get_openssh_key
  get_uri( + "/meta-data/public-keys/0/openssh-key")
rescue LoadSettingsError => e
  logger.info("Failed to get OpenSSH public key from OpenStack meta data endpoint: #{e.message}")
  user_data = parse_user_data(get_user_data_from_file)
  unless user_data.has_key?("openssh") && user_data["openssh"].has_key?("public_key")
    raise LoadSettingsError, "Cannot get OpenSSH public key from injected user data file: #{user_data.inspect}"
  end
  user_data["openssh"]["public_key"]
end

.get_registry_endpointString

Gets the Bosh registry endpoint from OpenStack user data.



80
81
82
83
84
85
86
# File 'lib/bosh_agent/infrastructure/openstack/registry.rb', line 80

def get_registry_endpoint
  user_data = get_user_data
  unless user_data.has_key?("registry") && user_data["registry"].has_key?("endpoint")
    raise LoadSettingsError, "Cannot get Bosh registry endpoint from user data #{user_data.inspect}"
  end
  lookup_registry_endpoint(user_data)
end

.get_server_nameString

Gets the server name from OpenStack user data.



68
69
70
71
72
73
74
# File 'lib/bosh_agent/infrastructure/openstack/registry.rb', line 68

def get_server_name
  user_data = get_user_data
  unless user_data.has_key?("server") && user_data["server"].has_key?("name")
    raise LoadSettingsError, "Cannot get OpenStack server name from user data #{user_data.inspect}"
  end
  user_data["server"]["name"]
end

.get_settingsHash

Gets the settings for this agent from the Bosh registry.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/bosh_agent/infrastructure/openstack/registry.rb', line 42

def get_settings
  @registry_endpoint ||= get_registry_endpoint
  url = "#{@registry_endpoint}/instances/#{get_server_name}/settings"
  raw_response = get_uri(url)

  registry_data = Yajl::Parser.parse(raw_response)
  unless registry_data.is_a?(Hash) && registry_data.has_key?("settings")
    raise LoadSettingsError, "Invalid response received from Bosh registry, " \
                             "got #{registry_data.class}: #{registry_data}"
  end

  settings = Yajl::Parser.parse(registry_data["settings"])
  unless settings.is_a?(Hash)
    raise(LoadSettingsError, "Invalid settings received from Bosh registry, " \
                             "got #{settings.class}: #{settings}")
  end

  settings
rescue Yajl::ParseError => e
  raise LoadSettingsError, "Cannot parse settings from Bosh registry, got #{raw_response} - #{e.message}"
end

.get_uri(uri) ⇒ String

Sends GET request to an specified URI.



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/bosh_agent/infrastructure/openstack/registry.rb', line 198

def get_uri(uri)
  client = HTTPClient.new
  client.send_timeout = HTTP_API_TIMEOUT
  client.receive_timeout = HTTP_API_TIMEOUT
  client.connect_timeout = HTTP_CONNECT_TIMEOUT

  headers = {"Accept" => "application/json"}
  response = client.get(uri, {}, headers)
  unless response.status == 200
    raise LoadSettingsError, "Endpoint #{uri} returned HTTP #{response.status}"
  end

  response.body
rescue HTTPClient::TimeoutError
  raise LoadSettingsError, "Timed out reading endpoint #{uri}"
rescue HTTPClient::BadResponseError => e
  raise LoadSettingsError, "Received bad HTTP response from endpoint #{uri}: #{e.inspect}"
rescue URI::Error, SocketError, Errno::ECONNREFUSED, SystemCallError => e
  raise LoadSettingsError, "Error requesting endpoint #{uri}: #{e.inspect}"
end

.get_user_dataHash

Gets the OpenStack user data. First we try to get it from the OpenStack user data endpoint, if we fail, then we fallback to the injected user data file.



151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/bosh_agent/infrastructure/openstack/registry.rb', line 151

def get_user_data
  return @user_data if @user_data
  begin
    raw_user_data = get_uri( + "/user-data")
  rescue LoadSettingsError => e
    logger.info("Failed to get user data from OpenStack user data endpoint: #{e.message}")
    raw_user_data = get_user_data_from_file
  end

  logger.info("OpenStack user data: #{raw_user_data.inspect}")
  @user_data = parse_user_data(raw_user_data)
end

.get_user_data_from_fileString

Gets the OpenStack user data from the injected user data file.



168
169
170
171
172
# File 'lib/bosh_agent/infrastructure/openstack/registry.rb', line 168

def get_user_data_from_file
  File.read(USER_DATA_FILE)
rescue SystemCallError => e
  raise LoadSettingsError, "Failed to get user data from OpenStack injected user data file: #{e.message}"
end

.inject_registry_ip_address(ip, endpoint) ⇒ String

Injects an IP address in the Bosh registry endpoint.



142
143
144
# File 'lib/bosh_agent/infrastructure/openstack/registry.rb', line 142

def inject_registry_ip_address(ip, endpoint)
  endpoint.sub(%r{//[^:]+:}, "//#{ip}:")
end

.loggerLogger

Returns the logger.



18
19
20
# File 'lib/bosh_agent/infrastructure/openstack/registry.rb', line 18

def logger
  Bosh::Agent::Config.logger
end

.lookup_registry_endpoint(user_data) ⇒ String

If the Bosh registry endpoint is specified with a Bosh DNS name, i.e. 0.registry.default.openstack.bosh, then the agent needs to lookup the name and insert the IP address, as the agent doesn’t update resolv.conf until after the bootstrap is run.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/bosh_agent/infrastructure/openstack/registry.rb', line 95

def lookup_registry_endpoint(user_data)
  registry_endpoint = user_data["registry"]["endpoint"]

  # If user data doesn't contain dns info, there is noting we can do, so just return the endpoint
  return registry_endpoint if user_data["dns"].nil? || user_data["dns"]["nameserver"].nil?

  # If the endpoint is an IP address, just return the endpoint
  registry_hostname = extract_registry_hostname(registry_endpoint)
  return registry_endpoint unless (IPAddr.new(registry_hostname) rescue(nil)).nil?

  nameservers = user_data["dns"]["nameserver"]
  registry_ip = lookup_registry_ip_address(registry_hostname, nameservers)
  inject_registry_ip_address(registry_ip, registry_endpoint)
rescue Resolv::ResolvError => e
  raise LoadSettingsError, "Cannot lookup #{registry_hostname} using #{nameservers.join(", ")}: #{e.inspect}"
end

.lookup_registry_ip_address(hostname, nameservers) ⇒ Resolv::IPv4

Lookups for the Bosh registry IP address.



131
132
133
134
# File 'lib/bosh_agent/infrastructure/openstack/registry.rb', line 131

def lookup_registry_ip_address(hostname, nameservers)
  resolver = Resolv::DNS.new(:nameserver => nameservers)
  resolver.getaddress(hostname)
end

.parse_user_data(raw_user_data) ⇒ Hash

Parses the OpenStack user data.



179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/bosh_agent/infrastructure/openstack/registry.rb', line 179

def parse_user_data(raw_user_data)
  begin
    user_data = Yajl::Parser.parse(raw_user_data)
  rescue Yajl::ParseError => e
    raise LoadSettingsError, "Cannot parse user data #{raw_user_data.inspect}: #{e.message}"
  end

  unless user_data.is_a?(Hash)
    raise LoadSettingsError, "Invalid user data format, Hash expected, got #{user_data.class}: #{user_data}"
  end

  user_data
end