• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

coryodaniel / k8s / 8c87563ddc8f8f3173844831ec008136b027e6c5-PR-279

11 Sep 2023 03:11AM UTC coverage: 84.008% (-0.2%) from 84.205%
8c87563ddc8f8f3173844831ec008136b027e6c5-PR-279

Pull #279

github

web-flow
Bump actions/checkout from 3.6.0 to 4.0.0

Bumps [actions/checkout](https://github.com/actions/checkout) from 3.6.0 to 4.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3.6.0...v4.0.0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #279: Bump actions/checkout from 3.6.0 to 4.0.0

851 of 1013 relevant lines covered (84.01%)

211.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.89
/lib/k8s/client/runner/wait.ex
1
defmodule K8s.Client.Runner.Wait do
2
  @moduledoc """
3
  Waiting functionality for `K8s.Client`.
4

5
  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`.
6
  """
7

8
  alias K8s.{Conn, Operation}
9
  alias K8s.Client.Runner.{Base, Wait}
10
  alias K8s.Operation.Error
11

12
  @typedoc "A wait configuration"
13
  @type t :: %__MODULE__{
14
          timeout: pos_integer,
15
          sleep: pos_integer,
16
          eval: any | (any -> any),
17
          find: list(binary) | (any -> any),
18
          timeout_after: NaiveDateTime.t(),
19
          processor: (map(), map() -> {:ok, map} | {:error, Error.t()})
20
        }
21
  defstruct [:timeout, :sleep, :eval, :find, :timeout_after, :processor]
22

23
  @doc """
24
  Continually perform a GET based operation until a condition is met.
25

26
  ## Example
27

28
  This follow example will wait 60 seconds for the field `status.succeeded` to equal `1`.
29

30
  ```elixir
31
  op = K8s.Client.get("batch/v1", :job, namespace: "default", name: "sleep")
32
  opts = [find: ["status", "succeeded"], eval: 1, timeout: 60]
33
  {:ok, conn} = K8s.Conn.from_file("test/support/kube-config.yaml")
34
  resp = K8s.Client.Runner.Wait.run(conn, op, opts)
35
  ```
36
  """
37
  @spec run(Operation.t(), keyword()) ::
38
          {:ok, :deleted} | {:ok, map()} | {:error, :timeout | Error.t()}
39

40
  def run(%Operation{conn: %Conn{} = conn} = op, opts), do: run(conn, op, opts)
1✔
41

42
  @spec run(Conn.t(), Operation.t(), keyword()) ::
43
          {:ok, :deleted} | {:ok, map()} | {:error, :timeout | Error.t()}
44
  def run(%Conn{} = conn, %Operation{method: :get} = op, opts) do
45
    conditions =
16✔
46
      Wait
47
      |> struct(opts)
48
      |> process_opts()
49

50
    case conditions do
16✔
51
      {:ok, opts} -> run_operation(conn, op, opts)
14✔
52
      error -> error
2✔
53
    end
54
  end
55

56
  def run(%Conn{} = conn, %Operation{method: :delete} = op, opts) do
57
    case Base.run(conn, op) do
1✔
58
      {:ok, _} ->
59
        run(
1✔
60
          conn,
61
          struct(op, method: :get),
62
          Keyword.merge(opts,
63
            processor: &get_deleted_processor/2,
64
            find: &Function.identity/1,
65
            eval: :deleted
66
          )
67
        )
68

69
      error ->
70
        error
×
71
    end
72
  end
73

74
  def run(op, _, _),
1✔
75
    do:
76
      {:error,
77
       %Error{message: "Only HTTP GET and DELETE operations are supported. #{inspect(op)}"}}
78

79
  @spec get_deleted_processor(Conn.t(), Operation.t()) :: {:ok, :deleted} | {:error, :exists}
80
  defp get_deleted_processor(conn, op) do
81
    case Base.run(conn, op) do
1✔
82
      {:error, %K8s.Client.APIError{reason: "NotFound"}} -> {:ok, :deleted}
1✔
83
      {:ok, _} -> {:error, :exists}
×
84
    end
85
  end
86

87
  @spec process_opts(Wait.t() | map) :: {:error, Error.t()} | {:ok, map}
88
  defp process_opts(%Wait{eval: nil}), do: {:error, %Error{message: ":eval is required"}}
1✔
89
  defp process_opts(%Wait{find: nil}), do: {:error, %Error{message: ":find is required"}}
1✔
90

91
  defp process_opts(opts) when is_map(opts) do
92
    timeout = Map.get(opts, :timeout) || 30
14✔
93
    sleep = Map.get(opts, :sleep) || 1
14✔
94
    now = NaiveDateTime.utc_now()
14✔
95
    timeout_after = NaiveDateTime.add(now, timeout, :second)
14✔
96
    processor = Map.get(opts, :processor) || (&Base.run/2)
14✔
97

98
    processed =
14✔
99
      opts
100
      |> Map.put(:timeout, timeout)
101
      |> Map.put(:sleep, sleep * 1000)
102
      |> Map.put(:timeout_after, timeout_after)
103
      |> Map.put(:processor, processor)
104

105
    {:ok, processed}
106
  end
107

108
  @spec run_operation(Conn.t(), Operation.t(), Wait.t()) :: {:error, :timeout} | {:ok, any}
109
  defp run_operation(
110
         %Conn{} = conn,
111
         %Operation{} = op,
112
         %Wait{timeout_after: timeout_after} = opts
113
       ) do
114
    case timed_out?(timeout_after) do
104✔
115
      true -> {:error, :timeout}
×
116
      false -> evaluate_operation(conn, op, opts)
104✔
117
    end
118
  end
119

120
  @spec evaluate_operation(Conn.t(), Operation.t(), Wait.t()) :: {:error, :timeout} | {:ok, any}
121
  defp evaluate_operation(
122
         %Conn{} = conn,
123
         %Operation{} = op,
124
         %Wait{processor: processor, sleep: sleep, eval: eval, find: find} = opts
125
       ) do
126
    with {:ok, resp} <- processor.(conn, op),
104✔
127
         true <- satisfied?(resp, find, eval) do
104✔
128
      {:ok, resp}
129
    else
130
      _not_satisfied ->
131
        Process.sleep(sleep)
90✔
132
        run_operation(conn, op, opts)
90✔
133
    end
134
  end
135

136
  @spec satisfied?(map, function | list, any) :: boolean
137
  defp satisfied?(resp, find, eval) when is_list(find) do
138
    value = get_in(resp, find)
102✔
139
    compare(value, eval)
102✔
140
  end
141

142
  defp satisfied?(resp, find, eval) when is_function(find) do
143
    value = find.(resp)
2✔
144
    compare(value, eval)
2✔
145
  end
146

147
  @spec compare(any, any) :: boolean
148
  defp compare(value, eval) when not is_function(eval), do: value == eval
29✔
149
  defp compare(value, eval) when is_function(eval), do: eval.(value)
75✔
150

151
  @spec timed_out?(NaiveDateTime.t()) :: boolean
152
  defp timed_out?(timeout_after) do
153
    case NaiveDateTime.compare(NaiveDateTime.utc_now(), timeout_after) do
104✔
154
      :gt -> true
×
155
      _ -> false
104✔
156
    end
157
  end
158
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc