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.

Parameters:

  • endpoint (String)

    Bosh registry endpoint

Returns:

  • (String)

    Bosh registry hostname



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.

Returns:



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_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.

Returns:

  • (String)

    Bosh registry endpoint



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.

Returns:

  • (String)

    OpenStack server name



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.

Returns:

  • (Hash)

    Agent Settings



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.

Parameters:

  • uri (String)

    URI to request

Returns:



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.

Returns:

  • (Hash)

    OpenStack user data



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(META_DATA_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.

Returns:

  • (String)

    OpenStack user data



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.

Parameters:

  • ip (Resolv::IPv4)

    Bosh registry IP address

  • endpoint (String)

    Bosh registry endpoint

Returns:

  • (String)

    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.

Returns:

  • (Logger)

    Bosh Agent 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.

Parameters:

  • user_data (Hash)

    OpenStack user data (generated by the CPI)

Returns:

  • (String)

    Bosh registry endpoint



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.

Parameters:

  • hostname (String)

    Bosh registry hostname

  • nameservers (Array)

    Array containing nameserver address

Returns:

  • (Resolv::IPv4)

    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.

Parameters:

  • raw_user_data (String)

    Raw OpenStack user data

Returns:

  • (Hash)

    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