Packages
k8s
0.5.0
2.8.0
2.7.0
2.6.2
2.6.1
2.6.0
2.5.0
2.4.2
2.4.1
2.4.0
2.3.0
2.2.0
2.1.1
2.1.0
2.0.3
2.0.2
2.0.1
2.0.0
2.0.0-rc.6
2.0.0-rc.5
2.0.0-rc.4
2.0.0-rc.3
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
1.2.1
1.2.0
1.1.10
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.0
1.0.0-rc1
0.5.2
0.5.1
0.5.0
0.5.0-rc.2
0.5.0-rc.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
retired
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.3
Kubernetes API Client for Elixir
Current section
Files
Jump to
Current section
Files
lib/k8s/client/http_provider.ex
defmodule K8s.Client.HTTPProvider do
@moduledoc """
HTTPoison and Jason based `K8s.Client.Provider`
"""
@behaviour K8s.Client.Provider
alias K8s.Conn.RequestOptions
@impl true
def request(method, url, body, headers, opts) do
{duration, response} = :timer.tc(HTTPoison, :request, [method, url, body, headers, opts])
measurements = %{duration: duration}
metadata = %{method: method}
case handle_response(response) do
{:ok, any} ->
K8s.Sys.Event.http_request_succeeded(measurements, metadata)
{:ok, any}
{:error, any} ->
K8s.Sys.Event.http_request_failed(measurements, metadata)
{:error, any}
end
end
@doc """
Handle HTTPoison responses and errors
## Examples
Parses successful JSON responses:
iex> body = ~s({"foo": "bar"})
...> K8s.Client.HTTPProvider.handle_response({:ok, %HTTPoison.Response{status_code: 200, body: body}})
{:ok, %{"foo" => "bar"}}
Parses successful JSON responses:
iex> body = "line 1\\nline 2\\nline 3\\n"
...> K8s.Client.HTTPProvider.handle_response({:ok, %HTTPoison.Response{status_code: 200, body: body, headers: [{"Content-Type", "text/plain"}]}})
{:ok, "line 1\\nline 2\\nline 3\\n"}
Handles unauthorized responses:
iex> K8s.Client.HTTPProvider.handle_response({:ok, %HTTPoison.Response{status_code: 401}})
{:error, :unauthorized}
Handles not found responses:
iex> K8s.Client.HTTPProvider.handle_response({:ok, %HTTPoison.Response{status_code: 404}})
{:error, :not_found}
Passes through HTTPoison 4xx responses:
iex> K8s.Client.HTTPProvider.handle_response({:ok, %HTTPoison.Response{status_code: 410, body: "Gone"}})
{:error, %HTTPoison.Response{status_code: 410, body: "Gone"}}
Passes through HTTPoison error responses:
iex> K8s.Client.HTTPProvider.handle_response({:error, %HTTPoison.Error{reason: "Foo"}})
{:error, %HTTPoison.Error{reason: "Foo"}}
"""
@impl true
def handle_response(resp) do
case resp do
{:ok, %HTTPoison.Response{status_code: code, body: body, headers: headers}}
when code in 200..299 ->
content_type = List.keyfind(headers, "Content-Type", 0)
{:ok, decode(body, content_type)}
{:ok, %HTTPoison.AsyncResponse{id: ref}} ->
{:ok, ref}
{:ok, %HTTPoison.Response{status_code: 401}} ->
{:error, :unauthorized}
{:ok, %HTTPoison.Response{status_code: 404}} ->
{:error, :not_found}
{:ok, %HTTPoison.Response{status_code: code} = resp} when code in 400..599 ->
{:error, resp}
{:error, %HTTPoison.Error{} = err} ->
{:error, err}
end
end
@doc """
Generates HTTP headers from `K8s.Conn.RequestOptions`
* Adds `{"Accept", "application/json"}` to all requests.
* Adds `Content-Type` base on HTTP method.
## Example
Sets `Content-Type` to `application/merge-patch+json` for PATCH operations
iex> opts = %K8s.Conn.RequestOptions{headers: [{"Authorization", "Basic AF"}]}
...> K8s.Client.HTTPProvider.headers(:patch, opts)
[{"Accept", "application/json"}, {"Content-Type", "application/merge-patch+json"}, {"Authorization", "Basic AF"}]
Sets `Content-Type` to `application/json` for all other operations
iex> opts = %K8s.Conn.RequestOptions{headers: [{"Authorization", "Basic AF"}]}
...> K8s.Client.HTTPProvider.headers(:get, opts)
[{"Accept", "application/json"}, {"Content-Type", "application/json"}, {"Authorization", "Basic AF"}]
"""
@impl true
def headers(method, %RequestOptions{} = opts) do
defaults = [{"Accept", "application/json"}, content_type_header(method)]
defaults ++ opts.headers
end
@spec content_type_header(atom()) :: {binary(), binary()}
defp content_type_header(:patch) do
{"Content-Type", "application/merge-patch+json"}
end
defp content_type_header(_http_method) do
{"Content-Type", "application/json"}
end
@spec decode(binary(), {binary(), binary()} | nil) :: list | map | nil
defp decode(body, {_, "text/plain"}), do: body
defp decode(body, _default_json_decoder) do
case Jason.decode(body) do
{:ok, data} -> data
{:error, _} -> nil
end
end
end