Current section
Files
Jump to
Current section
Files
lib/mix/tasks/kubegen.ex
defmodule Mix.Tasks.Kubegen do
@moduledoc ~S"""
(Re-)Generates clients for Kubernetes resources according to the config.
## Configuration
Prior to running this Mix task, you need to create a configuration in your
`config.exs` under `config :kubegen, :default` or `config :kubegen, :mycluster`.
where a custom `:mycluster` identifier is passed as argument
(`mix kubegen -c mycluster`)
* `:module_prefix` - The prefix of the generated modules (e.g. `MyApp.K8sClient`)
* `:kubeconfig_pipeline` - The `Pluggable` pipeline responsible for loading the Kubernetes config. (e.g. `Kubereq.Kubeconfig.Default`)
* `:resources` - List of resources for which clients are generated.
The entries of `:resources` can be in the form of
* Group-Version-Kind in the case of Kubernetes core resources.
* Path to a local CRD YAML (multiple CRDs in one file are supported)
* URL to a public remote CRD Yaml (multiple CRDs in one file are supported)
### Example
```
config :kubegen, :default,
module_prefix: MyApp.K8sClient,
kubeconfig_pipeline: Kubereq.Kubeconfig.Default,
resources: [
"v1/ConfigMap",
"rbac.authorization.k8s.io/v1/ClusterRole",
"test/support/foos.example.com.yaml", # local CRD
"https://raw.githubusercontent.com/mruoss/kompost/main/priv/manifest/postgresdatabase.crd.yaml" # public remote CRD
]
```
### How to find the correct Group-Version-Kind identifier
Use `mix kubegen.search` to search for GVKs (e.g. `mix.kubegen.search Pod`)
"""
@shortdoc "(Re-)generates Kubernetes Clients"
alias Kubegen.Resource
use Mix.Task
@cli_opts [strict: [cluster: :string], aliases: [c: :cluster]]
@impl Mix.Task
def run(args) do
{parsed, argv, _errors} = OptionParser.parse(args, @cli_opts)
if length(argv) != 0, do: usage_and_exit()
cluster = String.to_atom(parsed[:cluster] || "default")
config = Application.get_env(:kubegen, cluster)
if is_nil(config[:module_prefix]) do
Owl.IO.puts([
IO.ANSI.red(),
":module_prefix (Module prefix) not set. Please set the module prefix in config.exs under :kubegen, #{inspect(cluster)}, :prefix.",
IO.ANSI.reset(),
~s'''
Example:
config :kubegen, #{inspect(cluster)},
module_prefix: #{app_module()}.K8sClient
'''
])
exit({:shutdown, 65})
end
if is_nil(config[:kubeconfig_pipeline]) do
Owl.IO.puts([
IO.ANSI.red(),
":kubeconfig_pipeline (Kubeconfig Loader Pipeline) is not set. Please define the module defining a Pluggable pipeline for loading the Kubernetes configuration.",
IO.ANSI.reset(),
~s'''
Example:
config :kubegen, #{inspect(cluster)},
kubeconfig_pipeline: Kubereq.Kubeconfig.Default
'''
])
exit({:shutdown, 65})
end
if is_nil(config[:resources]) do
Owl.IO.puts([
IO.ANSI.red(),
":resources (List of resoures) is not set. Please define the list of resources to be generated in config.exs under :kubegen, #{inspect(cluster)}, :resources.",
IO.ANSI.reset(),
~s'''
Example:
config :kubegen, #{inspect(cluster)},
resources: [
"v1/Pod",
"apps/v1/Deployment"
]
'''
])
exit({:shutdown, 65})
end
module_prefix = config[:module_prefix]
kubeconfig_pipeline = config[:kubeconfig_pipeline]
resources = config[:resources] |> Enum.uniq()
"lib/#{Macro.underscore(module_prefix)}/*"
|> Path.wildcard()
|> Enum.each(&File.rm_rf!/1)
for {module_name, ast} <- Resource.generate(resources, module_prefix, kubeconfig_pipeline) do
rendered =
ast
|> Code.quoted_to_algebra(escape: false, locals_without_parens: [step: 1, step: 2])
|> Inspect.Algebra.format(98)
file_path = Path.join("lib", "#{Macro.underscore(module_name)}.ex")
file_path |> Path.dirname() |> File.mkdir_p!()
File.write(file_path, rendered)
Owl.IO.puts([
"Generated module ",
IO.ANSI.green(),
"#{module_name}",
IO.ANSI.reset(),
" in file ",
IO.ANSI.yellow(),
file_path,
IO.ANSI.reset()
])
end
end
defp usage_and_exit() do
IO.puts("""
mix kubegen_resource [-c cluster]
cluster Cluster identifier as used in config.exs (defaults to :default)
""")
exit({:shutdown, 65})
end
defp app_module() do
Macro.camelize("#{Mix.Project.config()[:app]}")
end
end