Skip to content

Commit

Permalink
Merge 610ac24 into 1418934
Browse files Browse the repository at this point in the history
  • Loading branch information
coryodaniel committed Jul 3, 2019
2 parents 1418934 + 610ac24 commit 58f944a
Show file tree
Hide file tree
Showing 14 changed files with 884 additions and 2,934 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Refactored tests on DynamicHTTPProvider
- Refactored discovery to use `K8s.Cluster.Discovery`
- Refactored Operation.kind -> Operation.name

## [0.2.13] - 2019-06-27

Expand Down
7 changes: 7 additions & 0 deletions Makefile
@@ -1,4 +1,5 @@
.PHONY: help clean deps all test tdd cov test/all lint analyze get-and-test-master
.PHONY: mock.dupes mock.groups

help: ## Show this help
help:
Expand Down Expand Up @@ -47,3 +48,9 @@ analyze: ## Run dialyzer

get/%: ## Add a new swagger spec to the test suite
curl -sfSL https://raw.githubusercontent.com/kubernetes/kubernetes/release-$*/api/openapi-spec/swagger.json -o test/support/swagger/$*.json

mock.dupes: ## List duplicates in resource_definitions mock (this should be empty)
jq '.[].groupVersion' test/support/discovery/resource_definitions.json | uniq -d

mock.groups: ## List of all groups in resource_definitions mock
jq '.[].groupVersion' test/support/discovery/resource_definitions.json
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -52,7 +52,7 @@ Most functions are also written using doctests.
* [K8s.Resource doctests](https://hexdocs.pm/k8s/K8s.Resource.html)
* [K8s.Version doctests](https://hexdocs.pm/k8s/K8s.Version.html)

## Testing `K8s` operations in your application.
## Testing `K8s` operations in your application

`K8s` ships with a [`K8s.Client.DynamicHTTPProvider`](./lib/k8s/client/dynamic_http_provider.ex) for stubbing HTTP responses to kubernetes API requests.

Expand Down Expand Up @@ -109,7 +109,7 @@ make test/${NEW_VERSION_NUMBER}

Mock discovery [responses](.test/support/discovery) exist to simulate runtime API discovery using the [`FileDriver`](./lib/k8s/cluster/discover/file_driver.ex)

If new resources or APIs were added to kubernetes in the new version you will likely see one of these errors: `unsupported_group_version` and `unsupported_kind`.
If new resources or APIs were added to kubernetes in the new version you will likely see one of these errors: `unsupported_group_version` and `unsupported_resource`.

### Unsupported Group Version errors

Expand Down
28 changes: 14 additions & 14 deletions lib/k8s/client.ex
Expand Up @@ -83,7 +83,7 @@ defmodule K8s.Client do
method: :get,
verb: :get,
group_version: "v1",
kind: "Pod",
name: "Pod",
path_params: [namespace: "test", name: "nginx-pod"],
}
"""
Expand All @@ -104,7 +104,7 @@ defmodule K8s.Client do
method: :get,
verb: :get,
group_version: "apps/v1",
kind: "Deployment",
name: "Deployment",
path_params: [namespace: "test", name: "nginx"]
}
Expand All @@ -113,7 +113,7 @@ defmodule K8s.Client do
method: :get,
verb: :get,
group_version: "apps/v1",
kind: :deployment,
name: :deployment,
path_params: [namespace: "test", name: "nginx"]}
"""
Expand All @@ -137,7 +137,7 @@ defmodule K8s.Client do
method: :get,
verb: :list,
group_version: "v1",
kind: "Pod",
name: "Pod",
path_params: [namespace: "default"]
}
Expand All @@ -146,7 +146,7 @@ defmodule K8s.Client do
method: :get,
verb: :list_all_namespaces,
group_version: "apps/v1",
kind: "Deployment",
name: "Deployment",
path_params: []
}
Expand Down Expand Up @@ -206,7 +206,7 @@ defmodule K8s.Client do
path_params: [namespace: "test"],
verb: :create,
group_version: "apps/v1",
kind: "Deployment",
name: "Deployment",
resource: %{
"apiVersion" => "apps/v1",
"kind" => "Deployment",
Expand Down Expand Up @@ -306,7 +306,7 @@ defmodule K8s.Client do
method: :patch,
verb: :patch,
group_version: "apps/v1",
kind: "Deployment",
name: "Deployment",
path_params: [namespace: "test", name: "nginx"],
resource: %{
"apiVersion" => "apps/v1",
Expand Down Expand Up @@ -392,7 +392,7 @@ defmodule K8s.Client do
method: :put,
verb: :update,
group_version: "apps/v1",
kind: "Deployment",
name: "Deployment",
path_params: [namespace: "test", name: "nginx"],
resource: %{
"apiVersion" => "apps/v1",
Expand Down Expand Up @@ -482,7 +482,7 @@ defmodule K8s.Client do
method: :delete,
verb: :delete,
group_version: "apps/v1",
kind: "Deployment",
name: "Deployment",
path_params: [namespace: "test", name: "nginx"]
}
Expand All @@ -500,7 +500,7 @@ defmodule K8s.Client do
method: :delete,
verb: :delete,
group_version: "apps/v1",
kind: "Deployment",
name: "Deployment",
path_params: [namespace: "test", name: "nginx"]
}
Expand All @@ -518,7 +518,7 @@ defmodule K8s.Client do
method: :delete,
verb: :deletecollection,
group_version: "extensions/v1beta1",
kind: "PodSecurityPolicy",
name: "PodSecurityPolicy",
path_params: []
}
Expand All @@ -527,7 +527,7 @@ defmodule K8s.Client do
method: :delete,
verb: :deletecollection,
group_version: "storage.k8s.io/v1",
kind: "StorageClass",
name: "StorageClass",
path_params: []
}
"""
Expand All @@ -546,7 +546,7 @@ defmodule K8s.Client do
method: :delete,
verb: :deletecollection,
group_version: "apps/v1beta1",
kind: "ControllerRevision",
name: "ControllerRevision",
path_params: [namespace: "default"]
}
Expand All @@ -555,7 +555,7 @@ defmodule K8s.Client do
method: :delete,
verb: :deletecollection,
group_version: "apps/v1",
kind: "Deployment",
name: "Deployment",
path_params: [namespace: "staging"]
}
"""
Expand Down
12 changes: 6 additions & 6 deletions lib/k8s/cluster.ex
Expand Up @@ -16,11 +16,11 @@ defmodule K8s.Cluster do
"""
@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)
def url_for(%K8s.Operation{} = operation, cluster) do
%{group_version: group_version, name: name, verb: verb} = operation
{:ok, conf} = K8s.Cluster.conf(cluster)

with {:ok, resource} <- K8s.Cluster.Group.find_resource(cluster_name, group_version, kind),
with {:ok, resource} <- K8s.Cluster.Group.find_resource(cluster, group_version, name),
{:ok, path} <-
K8s.Cluster.Path.build(group_version, resource, verb, operation.path_params) do
{:ok, Path.join(conf.url, path)}
Expand All @@ -38,8 +38,8 @@ defmodule K8s.Cluster do
{:ok, "https://localhost:6443"}
"""
@spec base_url(atom) :: {:ok, binary()} | {:error, atom} | {:error, binary}
def base_url(cluster_name) do
with {:ok, conf} <- K8s.Cluster.conf(cluster_name) do
def base_url(cluster) do
with {:ok, conf} <- K8s.Cluster.conf(cluster) do
{:ok, conf.url}
end
end
Expand Down
31 changes: 31 additions & 0 deletions lib/k8s/cluster/discovery.ex
Expand Up @@ -17,6 +17,12 @@ defmodule K8s.Cluster.Discovery do

@typedoc """
Resource definition identifier. Format: `{groupVersion, kind, name}`
Where `kind` is the kubernetes resource type and `name` is the name of the resource, which includes sub-resources.
E.g.:
* `{"apps/v1", "Deployment", "deployments"}
* `{"apps/v1", "Deployment", "deployments/status"}
"""
@type resource_definition_identifier_t :: {binary(), binary(), binary()}

Expand All @@ -38,6 +44,10 @@ defmodule K8s.Cluster.Discovery do

@doc """
Lists identifiers from Kubernetes resource definitions returned from `resource_definitions/2`.
## Examples
iex> K8s.Cluster.Discovery.resource_identifiers(:test)
[{"auditregistration.k8s.io/v1alpha1", "AuditSink", "auditsinks"}, {"settings.k8s.io/v1alpha1", "PodPreset", "podpresets"}, {"apiregistration.k8s.io/v1", "APIService", "apiservices"}]
"""
@spec resource_identifiers(atom(), Keyword.t() | nil) ::
{:ok, list(resource_definition_identifier_t)} | {:error, atom()}
Expand All @@ -47,6 +57,27 @@ defmodule K8s.Cluster.Discovery do
end
end

@doc """
Lists all identifiers in group from Kubernetes resource definitions returned from `resource_definitions/2`.
## Examples
iex> K8s.Cluster.Discovery.resource_identifiers(:test, "apps/v1")
[{"apps/v1", "Deployment", "deployments/status"}, {"apps/v1", "Deployment", "deployments"}]
"""
@spec resource_identifiers_by_group(atom(), binary(), Keyword.t() | nil) ::
{:ok, list(resource_definition_identifier_t)} | {:error, atom()}
def resource_identifiers_by_group(cluster, group, opts \\ []) do
with {:ok, identifiers} <- resource_identifiers(cluster, opts) do
{:ok, filter_resource_identifiers_by_group(identifiers, group)}
end
end

@spec filter_resource_identifiers_by_group(resource_definition_identifier_t(), binary()) ::
list(resource_definition_identifier_t())
defp filter_resource_identifiers_by_group(resource_identifiers, group) do
Enum.filter(resource_identifiers, fn {g, _, _} -> g == group end)
end

@spec get_identifiers_from_resource_definitions(list(map())) ::
list(resource_definition_identifier_t)
defp get_identifiers_from_resource_definitions(definitions) do
Expand Down
20 changes: 10 additions & 10 deletions lib/k8s/cluster/group.ex
Expand Up @@ -4,30 +4,30 @@ defmodule K8s.Cluster.Group do
"""

@doc """
Finds a resource definition by group version and kind
Finds a resource definition by group version and (name or kind).
"""
@spec find_resource(binary | atom, binary, binary | atom) ::
@spec find_resource(atom(), binary(), atom() | binary()) ::
{:ok, map}
| {:error, :unsupported_kind, binary()}
| {:error, :unsupported_resource, binary()}
| {:error, :unsupported_group_version, binary()}
def find_resource(cluster_name, group_version, kind) do
def find_resource(cluster_name, group_version, name_or_kind) do
case :ets.lookup(K8s.Cluster.Group, cluster_key(cluster_name, group_version)) do
[] ->
{:error, :unsupported_group_version, group_version}

[{_, _group_version, resources}] ->
find_resource_by_name(resources, kind)
find_resource_by_name(resources, name_or_kind)
end
end

@doc false
@spec find_resource_by_name(list(map), binary()) ::
@spec find_resource_by_name(list(map), atom() | binary()) ::
{:ok, map} | {:error, atom() | binary()}
def find_resource_by_name(resources, kind) do
resource = Enum.find(resources, &match_resource_by_name(&1, kind))
def find_resource_by_name(resources, name_or_kind) do
resource = Enum.find(resources, &match_resource_by_name(&1, name_or_kind))

case resource do
nil -> {:error, :unsupported_kind, kind}
nil -> {:error, :unsupported_resource, name_or_kind}
resource -> {:ok, resource}
end
end
Expand All @@ -48,7 +48,7 @@ defmodule K8s.Cluster.Group do
defp match_resource_by_name(resource, kind) when is_atom(kind),
do: match_resource_by_name(resource, Atom.to_string(kind))

defp match_resource_by_name(%{"kind" => kind}, kind), do: true
defp match_resource_by_name(%{"name" => name}, name), do: true
defp match_resource_by_name(%{"kind" => kind}, kind), do: true
defp match_resource_by_name(%{"kind" => kind}, name), do: String.downcase(kind) == name
end
10 changes: 5 additions & 5 deletions lib/k8s/cluster/path.ex
Expand Up @@ -174,8 +174,8 @@ defmodule K8s.Cluster.Path do

defp resource_path_suffix([name], verb), do: name_param(name, verb)

defp resource_path_suffix([name, subaction], verb),
do: name_with_subaction_param(name, subaction, verb)
defp resource_path_suffix([name, subresource], verb),
do: name_with_subresource_param(name, subresource, verb)

@spec name_param(binary, atom) :: binary
defp name_param(resource_name, :create), do: resource_name
Expand All @@ -184,9 +184,9 @@ defmodule K8s.Cluster.Path do
defp name_param(resource_name, :deletecollection), do: resource_name
defp name_param(resource_name, _), do: "#{resource_name}/{name}"

@spec name_with_subaction_param(binary, binary, atom) :: binary
defp name_with_subaction_param(resource_name, subaction, _),
do: "#{resource_name}/{name}/#{subaction}"
@spec name_with_subresource_param(binary, binary, atom) :: binary
defp name_with_subresource_param(resource_name, subresource, _),
do: "#{resource_name}/{name}/#{subresource}"

@spec build_path(binary, binary, boolean, atom) :: binary
defp build_path(prefix, suffix, true, :list_all_namespaces), do: "#{prefix}/#{suffix}"
Expand Down

0 comments on commit 58f944a

Please sign in to comment.