Skip to content

Commit

Permalink
Unmasking errors in config, cluster, and discover
Browse files Browse the repository at this point in the history
Add bang version of methods to raise when auto registering cluster at
boot.
  • Loading branch information
coryodaniel committed Jun 29, 2019
1 parent 431fe73 commit bc415b5
Show file tree
Hide file tree
Showing 18 changed files with 198 additions and 102 deletions.
15 changes: 13 additions & 2 deletions .credo.exs
Expand Up @@ -64,7 +64,8 @@
# You can customize the priority of any check
# Priority values are: `low, normal, high, higher`
#
{Credo.Check.Design.AliasUsage, [priority: :low, if_nested_deeper_than: 2, if_called_more_often_than: 0]},
{Credo.Check.Design.AliasUsage,
[priority: :low, if_nested_deeper_than: 2, if_called_more_often_than: 0]},
# You can also customize the exit_status of each check.
# If you don't want TODO comments to cause `mix credo` to fail, just
# set this value to 0 (zero).
Expand Down Expand Up @@ -135,7 +136,17 @@
#
{Credo.Check.Consistency.MultiAliasImportRequireUse, false},
{Credo.Check.Design.DuplicatedCode, false},
{Credo.Check.Readability.Specs}
{Credo.Check.Readability.Specs},
{Credo.Check.Refactor.ABCSize, false},
{Credo.Check.Refactor.AppendSingleItem, false},
{Credo.Check.Refactor.DoubleBooleanNegation, false},
{Credo.Check.Refactor.VariableRebinding, false},
{Credo.Check.Warning.MapGetUnsafePass, false},
{Credo.Check.Warning.UnsafeToAtom, false}

#
# Custom checks can be created using `mix credo.gen.check`.
#
]
}
]
Expand Down
1 change: 1 addition & 0 deletions .dialyzer_ignore.exs
@@ -1,3 +1,4 @@
[
{"lib/k8s/cluster.ex", :pattern_match, 52},
~r/RequestOptions.*__impl__.*does\ not\ exist\./
]
18 changes: 18 additions & 0 deletions coveralls.json
@@ -0,0 +1,18 @@
{
"skip_files": [
"test/support/",
"lib/k8s/sys/logger.ex"
],
"default_stop_words": [
"defdelegate",
"defmodule",
"defevent",
"defrecord",
"defstruct",
"defimpl",
"def.+(.+\/\/.+).+do"
],
"coverage_options": {
"treat_no_relevant_lines_as_covered": true
}
}
4 changes: 2 additions & 2 deletions docs/usage.md
Expand Up @@ -269,14 +269,14 @@ Register a staging cluster:

```elixir
staging_conf = K8s.Conf.from_file("~/.kube/config")
staging = K8s.Cluster.register(:staging, staging_conf)
{:ok, staging} = K8s.Cluster.register(:staging, staging_conf)
```

Register a prod cluster:

```elixir
prod_conf = K8s.Conf.from_service_account() # or from_file/2
prod = K8s.Cluster.register(:prod, staging_conf)
{:ok, prod} = K8s.Cluster.register(:prod, staging_conf)
```

Get a list of all deployments in the `default` prod namespace:
Expand Down
4 changes: 2 additions & 2 deletions lib/k8s.ex
Expand Up @@ -8,8 +8,8 @@ defmodule K8s do
"""
def init do
:ets.new(K8s.Conf, [:set, :public, :named_table])
:ets.new(K8s.Group, [:set, :public, :named_table])
K8s.Cluster.register_clusters()
:ets.new(K8s.Cluster.Group, [:set, :public, :named_table])
K8s.Cluster.auto_register_clusters!()
end

@doc false
Expand Down
3 changes: 2 additions & 1 deletion lib/k8s/behaviours/discovery_provider.ex
Expand Up @@ -3,5 +3,6 @@ defmodule K8s.Behaviours.DiscoveryProvider do
Kubernetes API Discovery behavior
"""

@callback resource_definitions_by_group(atom, keyword) :: list(map)
@callback resource_definitions_by_group(atom, keyword) ::
{:ok, list(map)} | {:error, HTTPoison.Response.t()}
end
2 changes: 0 additions & 2 deletions lib/k8s/client/runner/base.ex
Expand Up @@ -100,8 +100,6 @@ defmodule K8s.Client.Runner.Base do
http_headers,
http_opts
)
else
error -> error
end
end

Expand Down
87 changes: 62 additions & 25 deletions lib/k8s/cluster.ex
Expand Up @@ -4,32 +4,67 @@ defmodule K8s.Cluster do
"""

@discovery Application.get_env(:k8s, :discovery_provider, K8s.Discovery)
@dialyzer {:no_return, register!: 2, auto_register_clusters!: 0}

defmodule RegistrationException do
defexception [:message, :error]

@impl true
def exception(error) do
msg = "Failed to register cluster: #{inspect(error)}"
%K8s.Cluster.RegistrationException{message: msg, error: error}
end
end

@doc """
Register a new cluster to use with `K8s.Client`
## Examples
iex> conf = K8s.Conf.from_file("./test/support/kube-config.yaml")
...> :test_cluster = K8s.Cluster.register(:test_cluster, conf)
:test_cluster
...> K8s.Cluster.register(:test_cluster, conf)
{:ok, :test_cluster}
"""
@spec register(atom, K8s.Conf.t()) :: atom
@spec register(atom, K8s.Conf.t()) :: {:ok, atom()} | {:error, any()}
def register(cluster_name, conf) do
{duration, _result} =
:timer.tc(fn ->
:ets.insert(K8s.Conf, {cluster_name, conf})
groups = @discovery.resource_definitions_by_group(cluster_name)

Enum.each(groups, fn %{"groupVersion" => gv, "resources" => rs} ->
cluster_group_key = K8s.Group.cluster_key(cluster_name, gv)
:ets.insert(K8s.Group, {cluster_group_key, gv, rs})
end)
end)

K8s.Sys.Event.cluster_registered(%{duration: duration}, %{name: cluster_name})
cluster_name
with true <- :ets.insert(K8s.Conf, {cluster_name, conf}),
{:ok, groups} <- @discovery.resource_definitions_by_group(cluster_name) do
insert_groups(cluster_name, groups)
K8s.Sys.Event.cluster_registered(%{}, %{cluster: cluster_name})
{:ok, cluster_name}
end
end

@doc """
Register a new cluster to use with `K8s.Client`
## Examples
iex> conf = K8s.Conf.from_file("./test/support/kube-config.yaml")
...> K8s.Cluster.register!(:test_cluster, conf)
:test_cluster
"""
@spec register!(atom, K8s.Conf.t()) :: any | no_return
def register!(cluster_name, conf) do
case register(cluster_name, conf) do
{:ok, _} ->
cluster_name

{:error, error} ->
raise K8s.Cluster.RegistrationException, error
end
end

@spec insert_groups(atom, list(map)) :: no_return
defp insert_groups(cluster_name, groups) do
Enum.each(groups, fn %{"groupVersion" => gv, "resources" => rs} ->
cluster_group_key = K8s.Cluster.Group.cluster_key(cluster_name, gv)
:ets.insert(K8s.Cluster.Group, {cluster_group_key, gv, rs})
end)

nil
end

@doc """
Expand All @@ -44,16 +79,15 @@ defmodule K8s.Cluster do
{:ok, "https://localhost:6443/apis/apps/v1/namespaces/default/deployments/nginx"}
"""
@spec url_for(K8s.Operation.t(), atom) :: {:ok, binary} | {:error, atom} | {:error, binary}
@spec url_for(K8s.Operation.t(), atom) :: {:ok, binary} | {:error, atom(), binary()}
def url_for(%K8s.Operation{} = operation, cluster_name) do
%{group_version: group_version, kind: kind, verb: verb} = operation
{:ok, conf} = K8s.Cluster.conf(cluster_name)

with {:ok, resource} <- K8s.Group.find_resource(cluster_name, group_version, kind),
{:ok, path} <- K8s.Path.build(group_version, resource, verb, operation.path_params) do
with {:ok, resource} <- K8s.Cluster.Group.find_resource(cluster_name, group_version, kind),
{:ok, path} <-
K8s.Cluster.Path.build(group_version, resource, verb, operation.path_params) do
{:ok, Path.join(conf.url, path)}
else
error -> error
end
end

Expand All @@ -75,7 +109,9 @@ defmodule K8s.Cluster do
end

@doc """
Registers clusters automatically from `config.exs`
Registers clusters automatically from all configuration sources.
See the [usage guide](https://hexdocs.pm/k8s/usage.html#registering-clusters) for more details on configuring connection details.
## Examples
Expand All @@ -100,7 +136,8 @@ defmodule K8s.Cluster do
}
```
"""
def register_clusters do
@spec auto_register_clusters! :: no_return
def auto_register_clusters! do
clusters = K8s.Config.clusters()

Enum.each(clusters, fn {name, details} ->
Expand All @@ -117,10 +154,10 @@ defmodule K8s.Cluster do
K8s.Conf.from_file(conf_path, opts)
end

K8s.Cluster.register(name, conf)
K8s.Cluster.register!(name, conf)
end)

clusters
nil
end

@doc """
Expand Down
14 changes: 8 additions & 6 deletions lib/k8s/group.ex → lib/k8s/cluster/group.ex
@@ -1,4 +1,4 @@
defmodule K8s.Group do
defmodule K8s.Cluster.Group do
@moduledoc """
Kubernetes API Groups
"""
Expand All @@ -7,9 +7,11 @@ defmodule K8s.Group do
Finds a resource definition by group version and kind
"""
@spec find_resource(binary | atom, binary, binary | atom) ::
{:ok, map} | {:error, atom() | binary}
{:ok, map}
| {:error, :unsupported_kind, binary()}
| {:error, :unsupported_group_version, binary()}
def find_resource(cluster_name, group_version, kind) do
case :ets.lookup(K8s.Group, cluster_key(cluster_name, group_version)) do
case :ets.lookup(K8s.Cluster.Group, cluster_key(cluster_name, group_version)) do
[] ->
{:error, :unsupported_group_version, group_version}

Expand All @@ -25,17 +27,17 @@ defmodule K8s.Group do
resource = Enum.find(resources, &match_resource_by_name(&1, kind))

case resource do
nil -> {:error, :unsupported_kind}
nil -> {:error, :unsupported_kind, kind}
resource -> {:ok, resource}
end
end

@doc """
Creates a ETS key for `K8s.Group` per `K8s.Cluster`
Creates a ETS key for `K8s.Cluster.Group` per `K8s.Cluster`
## Examples
iex. K8s.Group.cluster_key(:dev, "apps/v1")
iex. K8s.Cluster.Group.cluster_key(:dev, "apps/v1")
"dev/apps/v1"
"""
Expand Down

0 comments on commit bc415b5

Please sign in to comment.