Skip to content

Commit 922c445

Browse files
committed
feat: Run Variant command as Kubernetes controller
See examples/controller for more information.
1 parent 17226a2 commit 922c445

File tree

11 files changed

+825
-1
lines changed

11 files changed

+825
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
- **Embeddable**: Easy embedding in any Golang application
1818
- **Easy distribution**: Build a single-executable of your command with Golang
1919
- **Dependency Management**: Dependent files, executable binaries and docker-run shims can be automatically installed and updated with the [variantdev/mod](https://github.com/variantdev/mod) integration. Example: [module](https://github.com/mumoshu/variant2/tree/master/examples/module)
20+
- **Run as a Kubernetes controller**: You can easily turn your Variant command into a Kubernetes controller. See [examples/controller](examples/controller)
2021
- **Integrations**: Integrates nicely with Slack, GitHub. You can run Variant command in response to Slack message, GitHub issue comment, commit push, etc.
2122

2223
# Getting Started

examples/controller/README.md

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
# Running your Variant command as a Kubernetes controller
2+
3+
Running `variant` with a few environment variables allows you to turn your command into a Kubernetes controller.
4+
5+
Let's say your command had a pair of jobs to upsert and destroy the your stack by using e.g. Terraform and/or Helmfile:
6+
7+
- `variant run apply --env prod --ref abcd1234`
8+
- `variant run destroy --env prod --ref abcd1234`
9+
10+
```console
11+
$ cat main.variant
12+
option "env" {
13+
type = string
14+
}
15+
16+
option "ref" {
17+
type = string
18+
}
19+
20+
job "apply" {
21+
exec {
22+
command = "echo"
23+
args = ["Deploying ${opt.ref} to ${opt.env}"]
24+
}
25+
}
26+
27+
job "destroy" {
28+
exec {
29+
command = "echo"
30+
args = ["Destroying ${opt.env}"]
31+
}
32+
}
33+
```
34+
35+
You can turn this into a Kubernetes controller by setting `<PREFIX>_JOB_ON_APPLY` to the job ran on resource creation or update,
36+
and `<PREFIX>_JOB_ON_DESTROY` to the job ran on resource deletion. The only remaining required envvar is `VARIANT_CONTROLLER_NAME`,
37+
which must be set to whatever name that the controller uses as the name of itself.
38+
39+
As we've seen in the example in the beginning of this section, the on-apply job is `apply` and the on-destroy job is `destroy` so that `variant` invocation
40+
should look like:
41+
42+
```console
43+
$ VARIANT_CONTROLLER_JOB_ON_APPLY=apply \
44+
VARIANT_CONTROLLER_JOB_ON_DESTROY=destroy \
45+
VARIANT_CONTROLLER_NAME=resource \
46+
variant
47+
```
48+
49+
`variant` uses `core.variant.run/v1beta1` `Resource` as the resource to be reconciled by the controller.
50+
51+
That being said, you can let the controller reconcile your resource by creating a `Resource` object with correct arguments -
52+
`env` and `ref` in this example - under the object's `spec` field:
53+
54+
```console
55+
$ kubectl apply -f <(cat EOS
56+
apiVersion: core.variant.run/v1beta1
57+
kind: Resource
58+
metadata:
59+
name: myresource
60+
spec:
61+
env: preview
62+
ref: abc1234
63+
EOS
64+
)
65+
```
66+
67+
Within a few seconds, the controller will reconcile your `Resource` by running `variant run apply --env preview --ref abc1234`.
68+
69+
You can verify that by tailing controller logs by `kubectl logs`, or browsing the `Reconcilation` object that is created by
70+
the controller to record the reconcilation details:
71+
72+
```console
73+
$ kubectl get reconcilation
74+
NAME AGE
75+
myresource-2 12m
76+
```
77+
78+
```console
79+
$ kubectl get -o yaml reconcilation myresource-2
80+
apiVersion: core.variant.run/v1beta1
81+
kind: Reconcilation
82+
metadata:
83+
creationTimestamp: "2020-10-28T12:05:55Z"
84+
generation: 1
85+
labels:
86+
core.variant.run/controller: resource
87+
core.variant.run/event: apply
88+
core.variant.run/pod: YOUR_HOST_OR_POD_NAME
89+
name: myresource-2
90+
namespace: default
91+
spec:
92+
combinedLogs:
93+
data: |
94+
Deploying abc2345 to preview
95+
job: apply
96+
resource:
97+
env: preview
98+
ref: abc2345
99+
```
100+
101+
Updating the `Resource` object will result in `variant` running `variant run apply` with the updated arguments:
102+
103+
```console
104+
$ kubectl apply -f <(cat <<EOS
105+
apiVersion: core.variant.run/v1beta1
106+
kind: Resource
107+
metadata:
108+
name: myresource
109+
spec:
110+
env: preview
111+
ref: abc2345
112+
EOS
113+
)
114+
```
115+
116+
```console
117+
$ kubectl get reconcilation
118+
NAME AGE
119+
myresource-2 12m
120+
myresource-3 12m
121+
```
122+
123+
```cnosole
124+
apiVersion: core.variant.run/v1beta1urce-3
125+
kind: Reconcilation
126+
metadata:
127+
creationTimestamp: "2020-10-28T12:06:10Z"
128+
generation: 1
129+
labels:
130+
core.variant.run/controller: resource
131+
core.variant.run/event: apply
132+
core.variant.run/pod: YOUR_HOST_OR_POD_NAME
133+
name: myresource-3
134+
namespace: default
135+
spec:
136+
combinedLogs:
137+
data: |
138+
Deploying abc1234 to preview
139+
job: apply
140+
resource:
141+
env: preview
142+
ref: abc1234
143+
```
144+
145+
Finally, deleting the `Resource` will let `variant` destroy the underlying resources by running `variant run destroy`
146+
as you've configured:
147+
148+
```console
149+
$ kubectl get reconcilation
150+
NAME AGE
151+
myresource-2 19m
152+
myresource-3 19m
153+
myresource-4 9s
154+
```
155+
156+
```console
157+
$ kubectl get reconcilation -o yaml myresource-4
158+
apiVersion: core.variant.run/v1beta1
159+
kind: Reconcilation
160+
metadata:
161+
creationTimestamp: "2020-10-28T12:25:32Z"
162+
generation: 1
163+
labels:
164+
core.variant.run/controller: resource
165+
core.variant.run/event: apply
166+
core.variant.run/pod: YOUR_POD_OR_HOST_NAME
167+
name: myresource-4
168+
namespace: default
169+
spec:
170+
combinedLogs:
171+
data: |
172+
Destroying preview
173+
job: destroy
174+
resource:
175+
env: preview
176+
ref: abc1234
177+
```
178+
179+
Now, go build a container image of your Variant command and deploy it as a Kubernetes deployment, and you've finished
180+
deploying your first Variant-powered Kubernetes controller :smile:
181+
182+
We've used simple `echo` as the implementations of `apply` and `destroy` jobs.
183+
But obviously you can run any combinations of tools within your jobs to easily manage whatever "stack".
184+
185+
The list of tools may include:
186+
187+
- Terraform
188+
- AWS CDK
189+
- Kubectl
190+
- Helm
191+
- Helmfile
192+
- Waypoint

examples/controller/main.variant

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
option "env" {
2+
type = string
3+
}
4+
5+
option "ref" {
6+
type = string
7+
}
8+
9+
job "apply" {
10+
exec {
11+
command = "echo"
12+
args = ["Deploying ${opt.ref} to ${opt.env}"]
13+
}
14+
}
15+
16+
job "destroy" {
17+
exec {
18+
command = "echo"
19+
args = ["Destroying ${opt.env}"]
20+
}
21+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
apiVersion: core.variant.run/v1beta1
2+
kind: Reconcilation
3+
metadata:
4+
name: myresource-abc
5+
spec:
6+
job: "apply"
7+
resource:
8+
env: preview
9+
ref: abc2345
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
apiVersion: core.variant.run/v1beta1
2+
kind: Resource
3+
metadata:
4+
name: myresource
5+
spec:
6+
env: preview
7+
ref: abc1234
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
apiVersion: core.variant.run/v1beta1
2+
kind: Resource
3+
metadata:
4+
name: myresource
5+
spec:
6+
env: preview
7+
ref: abc2345
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
apiVersion: apiextensions.k8s.io/v1beta1
2+
kind: CustomResourceDefinition
3+
metadata:
4+
name: resources.core.variant.run
5+
spec:
6+
group: core.variant.run
7+
versions:
8+
- name: v1beta1
9+
served: true
10+
storage: true
11+
names:
12+
kind: Resource
13+
plural: resources
14+
singular: resource
15+
scope: Namespaced
16+
---
17+
apiVersion: apiextensions.k8s.io/v1beta1
18+
kind: CustomResourceDefinition
19+
metadata:
20+
name: reconcilations.core.variant.run
21+
spec:
22+
group: core.variant.run
23+
versions:
24+
- name: v1beta1
25+
served: true
26+
storage: true
27+
names:
28+
kind: Reconcilation
29+
plural: reconcilations
30+
singular: reconcilation
31+
scope: Namespaced

go.mod

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ require (
2626
github.com/rs/xid v1.2.1
2727
github.com/spf13/cobra v0.0.5
2828
github.com/spf13/pflag v1.0.5 // indirect
29+
github.com/summerwind/whitebox-controller v0.7.1
2930
github.com/tidwall/gjson v1.3.5
3031
github.com/twpayne/go-vfs v1.3.6 // indirect
3132
github.com/ulikunitz/xz v0.5.6 // indirect
@@ -40,7 +41,20 @@ require (
4041
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 // indirect
4142
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
4243
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4 // indirect
44+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
4345
gopkg.in/go-playground/validator.v9 v9.31.0 // indirect
4446
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
47+
k8s.io/api v0.0.0-20190918155943-95b840bb6a1f // indirect
48+
k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783 // indirect
49+
k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655
50+
k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible
4551
k8s.io/klog v1.0.0 // indirect
52+
sigs.k8s.io/controller-runtime v0.4.0
53+
)
54+
55+
replace (
56+
k8s.io/api v0.0.0-20190918155943-95b840bb6a1f => k8s.io/api v0.0.0-20190918155943-95b840bb6a1f
57+
k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783 => k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783
58+
k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655 => k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655
59+
k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible => k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90
4660
)

0 commit comments

Comments
 (0)