Skip to content

Commit

Permalink
Merge pull request #19 from coryodaniel/config-free
Browse files Browse the repository at this point in the history
Abstracted config module
  • Loading branch information
coryodaniel committed Dec 30, 2018
2 parents d4ab1c7 + 680b720 commit 6cdf484
Show file tree
Hide file tree
Showing 21 changed files with 327 additions and 172 deletions.
35 changes: 19 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,45 +25,48 @@ def deps do
end
```

Add to `config.exs`:
All configuration is optional. Configuration is introspected from the mix project.

To change configuration add to `config.exs`:

```elixir
config :bonny,
config :bonny,
# Add each CRD Controller module for this operator to load here
# Defaults to none. This *must* be set.
controllers: [
MyApp.Controllers.V1.WebServer,
MyApp.Controllers.V1.Database,
MyApp.Controllers.V1.Memcached
],

# Set the Kubernetes API group for this operator.
# This can be overwritten using the @group attribute of a controller
group: "your-operator.your-domain.tld",
group: "your-operator.example.com",

# Name must only consist of only lowercase letters and hyphens.
# Defaults to "bonny"
# Defaults to hyphenated mix app name
operator_name: "your-operator",

# Name must only consist of only lowercase letters and hyphens.
# Defaults to operator name
# Defaults to hyphenated mix app name
service_account_name: "your-operator",

# Labels to apply to the operator's resources.
# labels: %{
# "kewl": "true"
# }
labels: %{
"kewl": "true"
},

# Operator deployment resources. These are the defaults.
# resources: %{
# limits: %{cpu: "200m", memory: "200Mi"},
# requests: %{cpu: "200m", memory: "200Mi"}
# }
resources: %{
limits: %{cpu: "200m", memory: "200Mi"},
requests: %{cpu: "200m", memory: "200Mi"}
},

# Kubernetes YAML config, defaults to the service account of the pod
# kubeconf_file: "",
kubeconf_file: "",

# Defaults to "current-context" if a config file is provided, override user, cluster. or context here
# kubeconf_opts: []
kubeconf_opts: []
```

## Example Operators built with Bonny
Expand Down
5 changes: 2 additions & 3 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ if Mix.env() == :test do

config :bonny,
controllers: [Widget, Cog],
group: "bonny.test",
group: "example.com",
kubeconf_file: "./test/support/kubeconfig.yaml"
end

Expand All @@ -14,7 +14,6 @@ if Mix.env() == :dev do
tasks: [
"test --cover",
"format",
"credo",
"docs"
"credo"
]
end
89 changes: 1 addition & 88 deletions lib/bonny.ex
Original file line number Diff line number Diff line change
@@ -1,92 +1,5 @@
defmodule Bonny do
@moduledoc """
Documentation for Bonny.
Extend the Kubernetes API and implement CustomResourceDefinitions lifecycles in Elixir.
"""

@doc """
The namespace to watch for `Namespaced` CRDs.
"""
@spec namespace() :: binary
def namespace() do
Application.get_env(:bonny, :override_namespace) || System.get_env("BONNY_POD_NAMESPACE") ||
"default"
end

@doc """
`K8s.Conf` configuration. This is used to sign HTTP requests to the Kubernetes API.
Bonny defaults to the service account of the pod if a cluster configuration is not provided.
"""
def kubeconfig() do
case config_path() do
conf_path when is_binary(conf_path) ->
conf_opts = Application.get_env(:bonny, :kubeconf_opts, [])
K8s.Conf.from_file(conf_path, conf_opts)

_ ->
K8s.Conf.from_service_account()
end
end

@doc """
Kubernetes API Group of this operator
"""
@spec group() :: binary
def group do
Application.get_env(:bonny, :group)
end

@doc """
Kubernetes service account name to run operator as.
*Note:* if a kube config file is provided, this service account will still be created
and assigned to pods, but the *config file auth will be used* when making requests to the Kube API.
Name must consist of only lowercase letters and hyphens.
Defaults to operator name.
"""
@spec service_account() :: binary
def service_account() do
service_account_name = Application.get_env(:bonny, :service_account_name, Bonny.name())

service_account_name
|> String.downcase()
|> String.replace(~r/[^a-z-]/, "-\\1\\g{1}")
end

@doc """
Labels to apply to all operator resources.
*Note:* These are only applied to the resoures that compose the operator itself,
not the resources created by the operator.
"""
@spec labels() :: map()
def labels() do
Application.get_env(:bonny, :labels, %{})
end

@doc """
The name of the operator.
Name must consist of only lowercase letters and hyphens.
Defaults to "bonny"
"""
@spec name() :: binary
def name() do
operator_name = Application.get_env(:bonny, :operator_name, "bonny")

operator_name
|> String.downcase()
|> String.replace(~r/[^a-z-]/, "-\\1\\g{1}")
end

@doc "List of all enabled controller modules"
@spec controllers() :: list(atom)
def controllers(), do: Application.get_env(:bonny, :controllers, [])

defp config_path do
System.get_env("BONNY_CONFIG_FILE") || Application.get_env(:bonny, :kubeconf_file)
end
end
2 changes: 1 addition & 1 deletion lib/bonny/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule Bonny.Application do

def start(_type, _args) do
children =
Bonny.controllers()
Bonny.Config.controllers()
|> Enum.map(fn controller ->
Supervisor.child_spec({Bonny.Watcher, controller}, id: controller)
end)
Expand Down
126 changes: 126 additions & 0 deletions lib/bonny/config.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
defmodule Bonny.Config do
@moduledoc """
Operator configuration interface
"""

@doc """
Kubernetes API Group of this operator
"""
@spec group() :: binary
def group do
default = "#{project_name()}.example.com"
Application.get_env(:bonny, :group, default)
end

@doc """
The name of the operator.
Name must consist of only lowercase letters and hyphens.
Defaults to hyphenated mix project app name. E.g.: `:hello_operator` becomes `hello-operator`
"""
@spec name() :: binary
def name() do
operator_name = Application.get_env(:bonny, :operator_name, project_name())

operator_name
|> String.downcase()
|> String.replace(~r/[^a-z-]/, "-\\1\\g{1}")
end

@doc """
Kubernetes service account name to run operator as.
*Note:* if a kube config file is provided, this service account will still be created
and assigned to pods, but the *config file auth will be used* when making requests to the Kube API.
Name must consist of only lowercase letters and hyphens.
Defaults to hyphenated mix project app name. E.g.: `:hello_operator` becomes `hello-operator`
"""
@spec service_account() :: binary
def service_account() do
service_account_name = Application.get_env(:bonny, :service_account_name, project_name())

service_account_name
|> String.downcase()
|> String.replace(~r/[^a-z-]/, "-\\1\\g{1}")
end

defp project_name() do
Mix.Project.config()
|> Keyword.fetch!(:app)
|> Atom.to_string()
|> String.replace("_", "-")
end

@doc """
Labels to apply to all operator resources.
*Note:* These are only applied to the resoures that compose the operator itself,
not the resources created by the operator.
This can be set in config.exs:
```
config :bonny, labels: %{foo: "bar", quz: "baz"}
```
"""
@spec labels() :: map()
def labels() do
Application.get_env(:bonny, :labels, %{})
end

@doc """
List of all controller modules to watch.
This *must* be set in config.exs:
```
config :bonny, controllers: [MyController1, MyController2]
```
"""
@spec controllers() :: list(atom)
def controllers() do
Application.get_env(:bonny, :controllers, [])
end

@doc """
The namespace to watch for `Namespaced` CRDs.
Defaults to `default`
This can be set via environment variable:
```shell
BONNY_POD_NAMESPACE=prod
iex -S mix
```
Bonny sets `BONNY_POD_NAMESPACE` on all Kubernetes deployments to the namespace the operator is deployed in.
"""
@spec namespace() :: binary
def namespace() do
System.get_env("BONNY_POD_NAMESPACE") || "default"
end

@doc """
`K8s.Conf` configuration. This is used to sign HTTP requests to the Kubernetes API.
Bonny defaults to the service account of the pod if a cluster configuration is not provided.
"""
def kubeconfig() do
config_path =
System.get_env("BONNY_CONFIG_FILE") || Application.get_env(:bonny, :kubeconf_file)

case config_path do
conf_path when is_binary(conf_path) ->
conf_opts = Application.get_env(:bonny, :kubeconf_opts, [])
K8s.Conf.from_file(conf_path, conf_opts)

_ ->
K8s.Conf.from_service_account()
end
end
end
2 changes: 1 addition & 1 deletion lib/bonny/controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ defmodule Bonny.Controller do
version = module_components |> Enum.at(1, "v1") |> String.downcase()

%Bonny.CRD{
group: @group || Application.get_env(:bonny, :group, "bonny.test"),
group: @group || Bonny.Config.group(),
scope: @scope || :namespaced,
version: @version || version,
names: @names || crd_spec_names(name)
Expand Down
12 changes: 6 additions & 6 deletions lib/bonny/crd.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ defmodule Bonny.CRD do
/apis/bonny.example.om/v1/namespaces/default/widgets
*Cluster Resource URL Path & `--all-namespaces` path*
/apis/bonny.test/v1/widgets
/apis/example.com/v1/widgets
"""
@spec list_path(Bonny.CRD.t()) :: binary
def list_path(crd = %CRD{}), do: base_path(crd)
Expand All @@ -56,10 +56,10 @@ defmodule Bonny.CRD do
URL path to read the specified CustomResourceDefinition
*Namespaced CRD Resource URL Path*
/apis/bonny.test/v1/namespaces/default/widgets/test-widget
/apis/example.com/v1/namespaces/default/widgets/test-widget
*Cluster CRD Resource URL Path & `--all-namespaces` path*
/apis/bonny.test/v1/widgets/test-widget
/apis/example.com/v1/widgets/test-widget
"""
@spec read_path(Bonny.CRD.t(), String.t()) :: binary
def read_path(crd = %CRD{}, name) do
Expand All @@ -75,9 +75,9 @@ defmodule Bonny.CRD do
kind: CustomResourceDefinition
metadata:
creationTimestamp: null
name: widgets.bonny.test
name: widgets.example.com
spec:
group: bonny.test
group: example.com
names:
kind: Widget
plural: widgets
Expand Down Expand Up @@ -106,7 +106,7 @@ defmodule Bonny.CRD do
group: group,
names: %{plural: plural}
}) do
"/apis/#{group}/#{version}/namespaces/#{Bonny.namespace()}/#{plural}"
"/apis/#{group}/#{version}/namespaces/#{Bonny.Config.namespace()}/#{plural}"
end

defp base_path(%CRD{scope: :cluster, version: version, group: group, names: %{plural: plural}}) do
Expand Down

0 comments on commit 6cdf484

Please sign in to comment.