# `IMDS.Provider`

The behaviour every provider implements, plus the helpers they share.

# `identity`

```elixir
@type identity() :: %{
  provider: provider(),
  instance_id: binary() | nil,
  instance_type: binary() | nil,
  region: binary() | nil,
  zone: binary() | nil,
  scope_id: binary() | nil,
  raw: map()
}
```

# `provider`

```elixir
@type provider() ::
  :aws
  | :gce
  | :azure
  | :oracle
  | :hetzner
  | :scaleway
  | :openstack
  | :digital_ocean
  | :akamai
  | :vultr
```

# `reason`

```elixir
@type reason() ::
  :not_detected
  | :token_failed
  | :decode_failed
  | :invalid_document
  | :invalid_path
  | :timeout
  | {:http, non_neg_integer()}
  | {:unknown_provider, term()}
  | term()
```

# `default_base_url`

```elixir
@callback default_base_url() :: binary()
```

Return the base URL of the metadata service.

# `detect`

```elixir
@callback detect(keyword()) :: :ok | {:error, :not_detected}
```

Answer `:ok` when the current instance is of this provider.

Every other result means that the instance is not of this provider.

# `identity`

```elixir
@callback identity(keyword()) :: {:ok, identity()} | {:error, reason()}
```

Return the normalized identity of the instance.

# `name`

```elixir
@callback name() :: provider()
```

Return the identifier of the provider.

# `request`

```elixir
@callback request(:get | :put, binary(), keyword()) ::
  {:ok, IMDS.HTTP.response()} | {:error, reason()}
```

Send one request to the metadata service and return the raw response.

A provider adds its own authentication to the request.

# `base_url`

```elixir
@spec base_url(module(), keyword()) :: binary()
```

Return the base URL of `module`, with a trailing `/` removed.

# `decode_json`

```elixir
@spec decode_json(binary()) :: {:ok, term()} | {:error, :decode_failed}
```

Decode a JSON body. An invalid body gives `{:error, :decode_failed}`.

# `decode_map`

```elixir
@spec decode_map(binary()) :: {:ok, map()} | {:error, :decode_failed}
```

Decode a JSON body that MUST hold an object.

# `fetch_binary`

```elixir
@spec fetch_binary(module(), binary(), keyword()) ::
  {:ok, binary()} | {:error, reason()}
```

Return the body of `path` when the provider answers with HTTP 200.

# `fetch_map`

```elixir
@spec fetch_map(module(), binary(), keyword()) :: {:ok, map()} | {:error, reason()}
```

Return the body of `path` decoded as a JSON object.

A body that is not a JSON object gives `{:error, :decode_failed}`.

# `fetch_value`

```elixir
@spec fetch_value(module(), binary(), keyword()) :: binary() | nil
```

Return the body of `path` with an empty body turned into `nil`.

A failed request gives `nil`, because a provider omits a field that the
metadata service does not report.

# `identifier`

```elixir
@spec identifier(term()) :: binary() | nil
```

Turn an instance identifier into a binary.

A metadata service reports the identifier as a number or as a string. Any
other type, and `nil`, gives `nil`.

# `normalize`

```elixir
@spec normalize(term()) :: term()
```

Turn an empty binary into `nil`.

The metadata services of the providers answer with an empty body or an empty
JSON string for a field that does not exist.

# `numeric?`

```elixir
@spec numeric?(term()) :: boolean()
```

Answer `true` when `value` holds only decimal digits.

# `send_request`

```elixir
@spec send_request(module(), :get | :put, binary(), keyword(), IMDS.HTTP.headers()) ::
  {:ok, IMDS.HTTP.response()} | {:error, term()}
```

Send `method` to `path` on the metadata service of `module`.

The provider gives its own headers. A `:put` request carries an empty body.

# `string_map`

```elixir
@spec string_map(map()) :: map()
```

Return a copy of `map` with every empty binary value turned into `nil`.

# `url`

```elixir
@spec url(module(), binary(), keyword()) :: binary()
```

Return the absolute URL of `path` on the base URL of `module`.

# `validate_path`

```elixir
@spec validate_path(binary()) :: :ok | {:error, :invalid_path}
```

Check a metadata path.

A path MUST start with `/`. The path part before the query or the fragment
MUST NOT hold a `.` or `..` segment, in plain or percent-encoded form.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
