-
-
Notifications
You must be signed in to change notification settings - Fork 27
/
Copy pathbonny.gen.controller.ex
117 lines (87 loc) · 3.25 KB
/
bonny.gen.controller.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
defmodule Mix.Tasks.Bonny.Gen.Controller do
@moduledoc """
Generates a new CRD controller
An operator can have multiple controllers. Each controller handles the lifecycle of a custom resource.
By default controllers are generated in the `V1` version scope.
```shell
mix bonny.gen.controller Widget widget
```
You can specify the version flag to create a new version of a controller. Bonny will dispatch the controller for the given version. So old versions of resources can live alongside new versions.
```shell
mix bonny.gen.controller Widget widget --version v2alpha1
```
*Note:* The one restriction with versions is that they will be camelized into a module name.
Open up your controller and add functionality for your resoures lifecycle:
* Add
* Modify
* Delete
Each controller can create multiple resources.
For example, a *todo app* controller could deploy a `Deployment` and a `Service`.
"""
use Mix.Task
@switches [version: :string, out: :string]
@default_opts [version: "v1"]
@aliases [o: :out, v: :version]
@shortdoc "Generate a new CRD Controller for this operator"
@spec run([binary()]) :: nil | :ok
def run(args) do
Mix.Bonny.no_umbrella!()
{mod_name, singular, plural, version, opts} = build(args)
binding = [
mod_name: mod_name,
singular: singular,
version: version,
plural: plural,
app_name: Mix.Bonny.app_name()
]
controller_out = opts[:out] || controller_path(opts[:version], singular)
test_out = opts[:out] || test_path(opts[:version], singular)
"controller.ex"
|> Mix.Bonny.template()
|> EEx.eval_file(binding)
|> Mix.Bonny.render(controller_out)
"controller_test.ex"
|> Mix.Bonny.template()
|> EEx.eval_file(binding)
|> Mix.Bonny.render(test_out)
end
defp controller_path(version, singular) do
Path.join(["lib", Mix.Bonny.app_dir_name(), "controllers", version, "#{singular}.ex"])
end
defp test_path(version, singular) do
Path.join(["test", Mix.Bonny.app_dir_name(), "controllers", version, "#{singular}_test.exs"])
end
defp build(args) do
{opts, parsed, _} =
Mix.Bonny.parse_args(args, @default_opts, switches: @switches, aliases: @aliases)
[mod_name, plural | _] = validate_args!(parsed)
version = Macro.camelize(opts[:version])
singular = Macro.underscore(mod_name)
{mod_name, singular, plural, version, opts}
end
defp validate_args!(args = [mod_name, _plural | _]) do
if mod_name =~ ~r/^[A-Z]\w*(\.[A-Z]\w*)*$/ do
args
else
raise_with_help("Expected the controller #{inspect(mod_name)} to be a valid module name")
end
end
defp validate_args!([_mod_name]) do
raise_with_help("Expected a controller module name followed by the plural form")
end
defp validate_args!(_) do
raise_with_help("Invalid arguments.")
end
@doc false
@spec raise_with_help(String.t()) :: no_return()
def raise_with_help(msg) do
Mix.raise("""
#{msg}
mix bonny.gen.controller expects a module name followed by the plural name of the resource.
For example:
mix bonny.gen.controller Webhook webhooks
mix bonny.gen.controller Memcached memcached
mix bonny.gen.controller Memcached memcached --version v1alpha1
""")
end
end