Packages
k8s
0.2.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/runner/wait.ex
defmodule K8s.Client.Runner.Wait do
@moduledoc """
Waiting functionality for `K8s.Client`.
Note: This is built using repeated GET operations rather than using a [watch](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.13/#watch-list-deployment-v1-apps) operation w/ `fieldSelector`.
"""
alias K8s.Operation
alias K8s.Client.Runner.{Base, Wait}
@typedoc "A wait configuration"
@type t :: %__MODULE__{
timeout: pos_integer,
sleep: pos_integer,
eval: any | (any -> any),
find: list(binary) | (any -> any),
timeout_after: NaiveDateTime.t(),
processor: (map(), map() -> {:ok, map} | {:error, binary})
}
defstruct [:timeout, :sleep, :eval, :find, :timeout_after, :processor]
@doc """
Continually perform a GET based operation until a condition is met.
## Example
Checking the number of job completions:
```elixir
op = K8s.Client.get("batch/v1", :job, namespace: "default", name: "sleep")
opts = [find: ["status", "succeeded"], eval: 1, timeout: 60]
resp = K8s.Client.Runner.Wait.run(op, cluster_name, opts)
```
"""
@spec run(Operation.t(), binary, keyword(atom())) ::
{:ok, map()} | {:error, binary()}
def run(op = %Operation{method: :get}, cluster_name, opts) do
conditions =
Wait
|> struct(opts)
|> process_opts()
case conditions do
{:ok, opts} -> run_operation(op, cluster_name, opts)
error -> error
end
end
def run(op, _, _), do: {:error, "Only HTTP GET operations are supported. #{inspect(op)}"}
defp process_opts(%Wait{eval: nil}), do: {:error, ":eval is required"}
defp process_opts(%Wait{find: nil}), do: {:error, ":find is required"}
defp process_opts(opts) when is_map(opts) do
timeout = Map.get(opts, :timeout) || 30
sleep = Map.get(opts, :sleep) || 1
now = NaiveDateTime.utc_now()
timeout_after = NaiveDateTime.add(now, timeout, :second)
processor = Map.get(opts, :processor) || (&Base.run/2)
processed =
opts
|> Map.put(:timeout, timeout)
|> Map.put(:sleep, sleep * 1000)
|> Map.put(:timeout_after, timeout_after)
|> Map.put(:processor, processor)
{:ok, processed}
end
defp run_operation(op, cluster_name, opts = %Wait{timeout_after: timeout_after}) do
case timed_out?(timeout_after) do
true -> {:error, :timeout}
false -> evaluate_operation(op, cluster_name, opts)
end
end
defp evaluate_operation(
op,
cluster_name,
opts = %Wait{processor: processor, sleep: sleep, eval: eval, find: find}
) do
with {:ok, resp} <- processor.(op, cluster_name),
true <- satisfied?(resp, find, eval) do
{:ok, resp}
else
_not_satisfied ->
Process.sleep(sleep)
run_operation(op, cluster_name, opts)
end
end
defp satisfied?(resp = %{}, find, eval) when is_list(find) do
value = get_in(resp, find)
compare(value, eval)
end
defp satisfied?(resp = %{}, find, eval) when is_function(find) do
value = find.(resp)
compare(value, eval)
end
defp compare(value, eval) when not is_function(eval), do: value == eval
defp compare(value, eval) when is_function(eval), do: eval.(value)
defp timed_out?(timeout_after) do
case NaiveDateTime.compare(NaiveDateTime.utc_now(), timeout_after) do
:gt -> true
_ -> false
end
end
end