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

coryodaniel / k8s / 807e93631268e5fd52ca29e3e4755088cf11bf27-PR-262

pending completion
807e93631268e5fd52ca29e3e4755088cf11bf27-PR-262

Pull #262

github

mruoss
add possibility to wait for delete
Pull Request #262: add possibility to wait for delete

6 of 6 new or added lines in 1 file covered. (100.0%)

732 of 1009 relevant lines covered (72.55%)

44.92 hits per line

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

82.35
/lib/k8s/operation/path.ex
1
defmodule K8s.Operation.Path do
2
  @moduledoc "Generates Kubernetes REST API Paths"
3
  alias K8s.Operation.Error
4
  @path_params [:namespace, :name, :path, :logpath]
5

6
  @doc false
7
  @spec path_params() :: list(atom)
8
  def path_params, do: @path_params
×
9

10
  @doc """
11
  Generates the API path for a `K8s.Operation`.
12

13
  ## Examples
14

15
  Generate a path for a cluster scoped resource:
16

17
      iex> resource = K8s.Resource.build("apps/v1", "CertificateSigningRequest", "foo")
18
      ...> operation = %K8s.Operation{
19
      ...>  method: :put,
20
      ...>  verb: :update,
21
      ...>  data: resource,
22
      ...>  path_params: [name: "foo"],
23
      ...>  api_version: "apps/v1",
24
      ...>  name: "certificatesigningrequests"
25
      ...> }
26
      ...> K8s.Operation.Path.build(operation)
27
      {:ok, "/apis/apps/v1/certificatesigningrequests/foo"}
28

29

30
      iex> resource = K8s.Resource.build("apps/v1", "CertificateSigningRequest", "foo")
31
      ...> operation = %K8s.Operation{
32
      ...>  method: :put,
33
      ...>  verb: :update,
34
      ...>  data: resource,
35
      ...>  path_params: [name: "foo", namespace: nil],
36
      ...>  api_version: "apps/v1",
37
      ...>  name: "certificatesigningrequests"
38
      ...> }
39
      ...> K8s.Operation.Path.build(operation)
40
      {:ok, "/apis/apps/v1/certificatesigningrequests/foo"}
41

42
  Generate a path for a namespace scoped resource:
43

44
      iex> resource = K8s.Resource.build("v1", "Pod", "default", "foo")
45
      ...> operation = %K8s.Operation{
46
      ...>  method: :put,
47
      ...>  verb: :update,
48
      ...>  data: resource,
49
      ...>  path_params: [namespace: "default", name: "foo"],
50
      ...>  api_version: "v1",
51
      ...>  name: "pods"
52
      ...> }
53
      ...> K8s.Operation.Path.build(operation)
54
      {:ok, "/api/v1/namespaces/default/pods/foo"}
55

56
  Generate a path for a namespace scoped resource on the collection: (ie create, list)
57

58
      iex> resource = K8s.Resource.build("v1", "Pod", "default", "foo")
59
      ...> operation = %K8s.Operation{
60
      ...>  method: :post,
61
      ...>  verb: :create,
62
      ...>  data: resource,
63
      ...>  path_params: [namespace: "default", name: "foo"],
64
      ...>  api_version: "v1",
65
      ...>  name: "pods"
66
      ...> }
67
      ...> K8s.Operation.Path.build(operation)
68
      {:ok, "/api/v1/namespaces/default/pods"}
69

70
  Generating a listing path for a namespace:
71

72
      iex> operation = %K8s.Operation{
73
      ...>  method: :get,
74
      ...>  verb: :list,
75
      ...>  data: nil,
76
      ...>  path_params: [namespace: "default"],
77
      ...>  api_version: "v1",
78
      ...>  name: "pods"
79
      ...> }
80
      ...> K8s.Operation.Path.build(operation)
81
      {:ok, "/api/v1/namespaces/default/pods"}
82

83
  Generating a listing path for a all namespaces:
84

85
      iex> operation = %K8s.Operation{
86
      ...>  method: :get,
87
      ...>  verb: :list_all_namespaces,
88
      ...>  data: nil,
89
      ...>  path_params: [namespace: "default"],
90
      ...>  api_version: "v1",
91
      ...>  name: "pods"
92
      ...> }
93
      ...> K8s.Operation.Path.build(operation)
94
      {:ok, "/api/v1/pods"}
95

96
  Generating a listing path for a watching all namespaces:
97

98
      iex> operation = %K8s.Operation{
99
      ...>  method: :get,
100
      ...>  verb: :watch_all_namespaces,
101
      ...>  data: nil,
102
      ...>  path_params: [namespace: "default"],
103
      ...>  api_version: "v1",
104
      ...>  name: "pods"
105
      ...> }
106
      ...> K8s.Operation.Path.build(operation)
107
      {:ok, "/api/v1/pods"}
108

109
  Generating a path for a subresource:
110

111
      iex> operation = %K8s.Operation{
112
      ...>  method: :get,
113
      ...>  verb: :get,
114
      ...>  data: nil,
115
      ...>  path_params: [namespace: "default", name: "foo"],
116
      ...>  api_version: "v1",
117
      ...>  name: "pods/status"
118
      ...> }
119
      ...> K8s.Operation.Path.build(operation)
120
      {:ok, "/api/v1/namespaces/default/pods/foo/status"}
121

122
  Deleting a collection:
123

124
      iex> operation = %K8s.Operation{
125
      ...>  method: :delete,
126
      ...>  verb: :deletecollection,
127
      ...>  data: nil,
128
      ...>  path_params: [namespace: "default"],
129
      ...>  api_version: "v1",
130
      ...>  name: "pods"
131
      ...> }
132
      ...> K8s.Operation.Path.build(operation)
133
      {:ok, "/api/v1/namespaces/default/pods"}
134
  """
135
  @spec build(K8s.Operation.t()) :: {:ok, binary} | {:error, Error.t()}
136
  def build(%K8s.Operation{} = operation) do
137
    path_template = to_path(operation)
95✔
138
    required_params = K8s.Operation.Path.find_params(path_template)
95✔
139
    provided_params = Keyword.keys(operation.path_params)
95✔
140

141
    case required_params -- provided_params do
95✔
142
      [] ->
95✔
143
        {:ok, replace_path_vars(path_template, operation.path_params)}
95✔
144

145
      missing_params ->
146
        msg = "Missing required params: #{inspect(missing_params)}"
×
147
        {:error, %Error{message: msg}}
148
    end
149
  end
150

151
  @doc """
152
  Replaces path variables with options.
153

154
  ## Examples
155

156
      iex> K8s.Operation.Path.replace_path_vars("/foo/{name}", name: "bar")
157
      "/foo/bar"
158

159
  """
160
  @spec replace_path_vars(binary(), keyword()) :: binary()
161
  def replace_path_vars(path_template, opts) do
162
    Regex.replace(~r/\{(\w+?)\}/, path_template, fn _, var ->
95✔
163
      opts[String.to_existing_atom(var)]
122✔
164
    end)
165
  end
166

167
  @doc """
168
  Find valid path params in a URL path.
169

170
  ## Examples
171

172
      iex> K8s.Operation.Path.find_params("/foo/{name}")
173
      [:name]
174

175
      iex> K8s.Operation.Path.find_params("/foo/{namespace}/bar/{name}")
176
      [:namespace, :name]
177

178
      iex> K8s.Operation.Path.find_params("/foo/bar")
179
      []
180

181
  """
182
  @spec find_params(binary()) :: list(atom())
183
  def find_params(path_with_args) do
184
    ~r/{([a-z]+)}/
185
    |> Regex.scan(path_with_args)
186
    |> Enum.map(fn match -> match |> List.last() |> String.to_existing_atom() end)
95✔
187
  end
188

189
  @spec to_path(K8s.Operation.t()) :: binary
190
  defp to_path(%K8s.Operation{path_params: params} = operation) do
191
    has_namespace = !is_nil(params[:namespace])
95✔
192
    namespaced = has_namespace && params[:namespace] != :all
95✔
193

194
    prefix =
95✔
195
      case String.contains?(operation.api_version, "/") do
95✔
196
        true -> "/apis/#{operation.api_version}"
31✔
197
        false -> "/api/#{operation.api_version}"
64✔
198
      end
199

200
    suffix =
95✔
201
      operation.name
95✔
202
      |> String.split("/")
203
      |> resource_path_suffix(operation.verb)
95✔
204

205
    build_path(prefix, suffix, namespaced, operation.verb)
95✔
206
  end
207

208
  @spec resource_path_suffix(list(binary), atom) :: binary
209
  defp resource_path_suffix([name], verb), do: name_param(name, verb)
93✔
210

211
  defp resource_path_suffix([name, subresource], verb),
212
    do: name_with_subresource_param(name, subresource, verb)
2✔
213

214
  @spec name_param(binary, atom) :: binary
215
  defp name_param(resource_name, :create), do: resource_name
6✔
216
  defp name_param(resource_name, :list_all_namespaces), do: resource_name
×
217
  defp name_param(resource_name, :list), do: resource_name
34✔
218
  defp name_param(resource_name, :deletecollection), do: resource_name
3✔
219
  defp name_param(resource_name, :watch_all_namespaces), do: resource_name
×
220
  defp name_param(resource_name, _), do: "#{resource_name}/{name}"
50✔
221

222
  @spec name_with_subresource_param(binary, binary, atom) :: binary
223
  defp name_with_subresource_param(resource_name, subresource, _),
224
    do: "#{resource_name}/{name}/#{subresource}"
2✔
225

226
  @spec build_path(binary, binary, boolean, atom) :: binary
227
  defp build_path(prefix, suffix, true, :watch_all_namespaces), do: "#{prefix}/#{suffix}"
×
228
  defp build_path(prefix, suffix, true, :list_all_namespaces), do: "#{prefix}/#{suffix}"
×
229
  defp build_path(prefix, suffix, true, _), do: "#{prefix}/namespaces/{namespace}/#{suffix}"
70✔
230
  defp build_path(prefix, suffix, false, _), do: "#{prefix}/#{suffix}"
25✔
231
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