Packages

An adapter for the openstex library for making calls to Openstack compliant Rackspace services.

Current section

Files

Jump to
openstex_adapters_rackspace lib rackspace keystone.ex
Raw

lib/rackspace/keystone.ex

defmodule Openstex.Adapters.Rackspace.Keystone do
@moduledoc :false
alias Openstex.Adapters.Rackspace.Utils
import Openstex.Utils, only: [ets_tablename: 1]
@behaviour Openstex.Adapter.Keystone
@get_identity_retries 5
@get_identity_interval 1000
@update_identity_buffer (10 * 1000)
# Public Openstex.Adapter.Keystone callbacks
def start_link(openstex_client) do
GenServer.start_link(__MODULE__, openstex_client, [name: openstex_client])
end
def start_link(openstex_client, _opts) do
start_link(openstex_client)
end
def identity(openstex_client) do
get_identity(openstex_client)
end
def get_xauth_token(openstex_client) do
get_identity(openstex_client) |> Map.get(:token) |> Map.get(:id)
end
# Genserver Callbacks
def init(openstex_client) do
:erlang.process_flag(:trap_exit, :true)
create_ets_table(openstex_client)
identity = Utils.create_identity(openstex_client)
:ets.insert(ets_tablename(openstex_client), {:identity, identity})
milliseconds_to_expiry = (get_seconds_to_expiry(identity) * 1000) - @update_identity_buffer
timer_ref = Process.send_after(self(), :update_identity, milliseconds_to_expiry)
{:ok, {openstex_client, identity, timer_ref}}
end
def handle_call(:update_identity, _from, {openstex_client, identity, timer_ref}) do
{:reply, :ok, update_identity({openstex_client, identity, timer_ref})}
end
def handle_info(:update_identity, {openstex_client, identity, timer_ref}) do
{:noreply, update_identity({openstex_client, identity, timer_ref})}
end
def handle_info(_info, {openstex_client, identity, timer_ref}) do
{:noreply, {openstex_client, identity, timer_ref}}
end
def terminate(_reason, {openstex_client, _identity, _timer_ref}) do
:ets.delete(ets_tablename(openstex_client))
:ok
end
# private
defp get_identity(openstex_client) do
unless genserver_exists?(openstex_client), do: start_link(openstex_client)
get_identity(openstex_client, 0)
end
defp get_identity(openstex_client, index) do
retry = fn(openstex_client, index) ->
if index > @get_identity_retries do
raise "Cannot retrieve openstack identity, #{__ENV__.module}, #{__ENV__.line}, client: #{openstex_client}"
else
:timer.sleep(@get_identity_interval)
get_identity(openstex_client, index + 1)
end
end
if ets_tablename(openstex_client) in :ets.all() do
table = :ets.lookup(ets_tablename(openstex_client), :identity)
case table do
[identity: identity] ->
identity
[] -> retry.(openstex_client, index)
end
else
retry.(openstex_client, index)
end
end
defp update_identity({openstex_client, _identity, timer_ref}) do
new_identity = Utils.create_identity(openstex_client)
:ets.insert(ets_tablename(openstex_client), {:identity, new_identity})
milliseconds_to_expiry = (get_seconds_to_expiry(new_identity) * 1000) - @update_identity_buffer
:timer.cancel(timer_ref)
timer_ref = Process.send_after(self(), :update_identity, milliseconds_to_expiry)
{openstex_client, new_identity, timer_ref}
end
defp get_seconds_to_expiry(identity) do
iso_time = identity.token.expires
(
DateTime.from_iso8601(iso_time)
|> Tuple.to_list()
|> Enum.at(1)
|> DateTime.to_unix()
) -
(DateTime.utc_now() |> DateTime.to_unix())
end
defp create_ets_table(openstex_client) do
ets_options = [
:set, # type
:protected, # read - all, write this process only.
:named_table,
{:heir, :none}, # don't let any process inherit the table. when the ets table dies, it dies.
{:write_concurrency, :false},
{:read_concurrency, :true}
]
unless ets_tablename(openstex_client) in :ets.all() do
:ets.new(ets_tablename(openstex_client), ets_options)
end
end
defp genserver_exists?(client) do
Process.whereis(client) != :nil
end
end