Skip to content

Commit

Permalink
Merge pull request #35 from mumoshu/run-as-k8s-controller
Browse files Browse the repository at this point in the history
feat: Run Variant command as Kubernetes controller
  • Loading branch information
mumoshu authored Oct 29, 2020
2 parents 17226a2 + 378df01 commit e700bc8
Show file tree
Hide file tree
Showing 44 changed files with 1,278 additions and 128 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ jobs:
strategy:
matrix:
go:
- 1.13.4
- 1.15.x
name: Go ${{ matrix.go }} build
steps:
- uses: actions/checkout@master
- name: Setup Go
uses: actions/setup-go@v1
with:
version: ${{ matrix.go }}
go-version: ${{ matrix.go }}
- name: Run go mod download
run: go mod download
- name: Install SSH key
Expand All @@ -32,14 +32,14 @@ jobs:
strategy:
matrix:
go:
- 1.13.4
- 1.15.x
name: Go ${{ matrix.go }} build
steps:
- uses: actions/checkout@master
- name: Setup Go
uses: actions/setup-go@v1
with:
version: ${{ matrix.go }}
go-version: ${{ matrix.go }}
- name: Run go mod download
run: go mod download
- name: Run golangci-lint
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.13.x
go-version: 1.15.x
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v1
Expand Down
14 changes: 10 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@ test: build
PATH=$(PWD):$(PATH) go test -race -v ./...

bin/golangci-lint:
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s v1.23.1
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s v1.32.0

.PHONY: lint
lint: bin/golangci-lint
bin/golangci-lint run --tests \
bin/golangci-lint run --tests ./... \
--timeout 5m \
--enable-all \
--disable gochecknoglobals \
--disable gochecknoinits \
--disable gomnd,funlen,prealloc,gocritic,lll,gocognit
--disable gomnd,funlen,prealloc,gocritic,lll,gocognit \
--disable testpackage,goerr113,exhaustivestruct,wrapcheck

.PHONY: smoke
smoke: export GOBIN=$(shell pwd)/tools
Expand All @@ -44,7 +46,11 @@ smoke: build
grep "Namespace to interact with" smoke2.log

rm -rf build/import-multi
VARIANT_BUILD_VER=v0.0.0 VARIANT_BUILD_REPLACE=$(shell pwd) PATH=${PATH}:$(GOBIN) ./variant export binary examples/advanced/import-multi build/import-multi
VARIANT_BUILD_VER=v0.0.0 \
VARIANT_BUILD_VARIANT_REPLACE=$(shell pwd) \
VARIANT_BUILD_MOD_REPLACE="github.com/summerwind/[email protected]=github.com/mumoshu/[email protected]" \
PATH=${PATH}:$(GOBIN) \
./variant export binary examples/advanced/import-multi build/import-multi
build/import-multi foo baz HELLO > build/import-multi.log
bash -c 'diff <(echo HELLO) <(cat build/import-multi.log)'

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- **Embeddable**: Easy embedding in any Golang application
- **Easy distribution**: Build a single-executable of your command with Golang
- **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)
- **Run as a Kubernetes controller**: You can easily turn your Variant command into a Kubernetes controller. See [examples/controller](examples/controller)
- **Integrations**: Integrates nicely with Slack, GitHub. You can run Variant command in response to Slack message, GitHub issue comment, commit push, etc.

# Getting Started
Expand Down
3 changes: 2 additions & 1 deletion examples/advanced/import-multi/export.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ PROJECT_ROOT=../../..
VARIANT=${PROJECT_ROOT}/variant

export VARIANT_BUILD_VER=v0.33.3
export VARIANT_BUILD_REPLACE=$(pwd)/${PROJECT_ROOT}
export VARIANT_BUILD_VARIANT_REPLACE=$(pwd)/${PROJECT_ROOT}
export VARIANT_BUILD_MOD_REPLACE="github.com/summerwind/[email protected]=github.com/mumoshu/[email protected]"

rm -rf ../exported
rm -rf ../compiled
Expand Down
213 changes: 213 additions & 0 deletions examples/controller/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
# Running your Variant command as a Kubernetes controller

Running `variant` with a few environment variables allows you to turn your command into a Kubernetes controller.

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:

- `variant run apply --env prod --ref abcd1234`
- `variant run destroy --env prod --ref abcd1234`

```console
$ cat main.variant
option "env" {
type = string
}

option "ref" {
type = string
}

job "apply" {
exec {
command = "echo"
args = ["Deploying ${opt.ref} to ${opt.env}"]
}
}

job "destroy" {
exec {
command = "echo"
args = ["Destroying ${opt.env}"]
}
}
```

You can turn this into a Kubernetes controller by setting `<PREFIX>_JOB_ON_APPLY` to the job ran on resource creation or update,
and `<PREFIX>_JOB_ON_DESTROY` to the job ran on resource deletion. The only remaining required envvar is `VARIANT_CONTROLLER_NAME`,
which must be set to whatever name that the controller uses as the name of itself.

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
should look like:

```console
$ VARIANT_CONTROLLER_JOB_ON_APPLY=apply \
VARIANT_CONTROLLER_JOB_ON_DESTROY=destroy \
VARIANT_CONTROLLER_NAME=resource \
variant
```

`variant` uses `core.variant.run/v1beta1` `Resource` as the resource to be reconciled by the controller.

That being said, you can let the controller reconcile your resource by creating a `Resource` object with correct arguments -
`env` and `ref` in this example - under the object's `spec` field:

```console
$ kubectl apply -f <(cat EOS
apiVersion: core.variant.run/v1beta1
kind: Resource
metadata:
name: myresource
spec:
env: preview
ref: abc1234
EOS
)
```

Within a few seconds, the controller will reconcile your `Resource` by running `variant run apply --env preview --ref abc1234`.

You can verify that by tailing controller logs by `kubectl logs`, or browsing the `Reconcilation` object that is created by
the controller to record the reconciliation details:

```console
$ kubectl get reconciliation
NAME AGE
myresource-2 12m
```

```console
$ kubectl get -o yaml reconciliation myresource-2
apiVersion: core.variant.run/v1beta1
kind: Reconciliation
metadata:
creationTimestamp: "2020-10-28T12:05:55Z"
generation: 1
labels:
core.variant.run/controller: resource
core.variant.run/event: apply
core.variant.run/pod: YOUR_HOST_OR_POD_NAME
name: myresource-2
namespace: default
spec:
combinedLogs:
data: |
Deploying abc2345 to preview
job: apply
resource:
env: preview
ref: abc2345
```

Updating the `Resource` object will result in `variant` running `variant run apply` with the updated arguments:

```console
$ kubectl apply -f <(cat <<EOS
apiVersion: core.variant.run/v1beta1
kind: Resource
metadata:
name: myresource
spec:
env: preview
ref: abc2345
EOS
)
```

```console
$ kubectl get reconciliation
NAME AGE
myresource-2 12m
myresource-3 12m
```

```cnosole
apiVersion: core.variant.run/v1beta1urce-3
kind: Reconciliation
metadata:
creationTimestamp: "2020-10-28T12:06:10Z"
generation: 1
labels:
core.variant.run/controller: resource
core.variant.run/event: apply
core.variant.run/pod: YOUR_HOST_OR_POD_NAME
name: myresource-3
namespace: default
spec:
combinedLogs:
data: |
Deploying abc1234 to preview
job: apply
resource:
env: preview
ref: abc1234
```

Finally, deleting the `Resource` will let `variant` destroy the underlying resources by running `variant run destroy`
as you've configured:

```console
$ kubectl get reconciliation
NAME AGE
myresource-2 19m
myresource-3 19m
myresource-4 9s
```

```console
$ kubectl get reconciliation -o yaml myresource-4
apiVersion: core.variant.run/v1beta1
kind: Reconciliation
metadata:
creationTimestamp: "2020-10-28T12:25:32Z"
generation: 1
labels:
core.variant.run/controller: resource
core.variant.run/event: apply
core.variant.run/pod: YOUR_POD_OR_HOST_NAME
name: myresource-4
namespace: default
spec:
combinedLogs:
data: |
Destroying preview
job: destroy
resource:
env: preview
ref: abc1234
```

Now, go build a container image of your Variant command and deploy it as a Kubernetes deployment, and you've finished
deploying your first Variant-powered Kubernetes controller :smile:

We've used simple `echo` as the implementations of `apply` and `destroy` jobs.
But obviously you can run any combinations of tools within your jobs to easily manage whatever "stack".

The list of tools may include:

- Terraform
- AWS CDK
- Kubectl
- Helm
- Helmfile
- Waypoint

## Advanced configuration

- Using non-default CRD

### Using non-default CRD

You can use different apiVersion than the default `core.variant.run/v1beta1` by setting `<PREFIX>_FOR_API_VERSION`,
and different kind than the default `Resource` by setting `<PREFIX>_FOR_KIND`.

For example, to let the controller watch and reconcile `whatever.example.com/v1alpha1` `MyCustomStack` objects, run `variant`
like:

```console
$ VARIANT_CONTROLLER_JOB_ON_APPLY=apply \
VARIANT_CONTROLLER_JOB_ON_DESTROY=destroy \
VARIANT_CONTROLLER_NAME=my-custom-stack \
VARIANT_CONTROLLER_FOR_API_VERSION=whatever.example.com/v1alpha1 \
VARIANT_CONTROLLER_FOR_KIND=MyCustomStack \
variant
```
21 changes: 21 additions & 0 deletions examples/controller/main.variant
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
option "env" {
type = string
}

option "ref" {
type = string
}

job "apply" {
exec {
command = "echo"
args = ["Deploying ${opt.ref} to ${opt.env}"]
}
}

job "destroy" {
exec {
command = "echo"
args = ["Destroying ${opt.env}"]
}
}
9 changes: 9 additions & 0 deletions examples/controller/reconcilation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: core.variant.run/v1beta1
kind: Reconciliation
metadata:
name: myresource-abc
spec:
job: "apply"
resource:
env: preview
ref: abc2345
7 changes: 7 additions & 0 deletions examples/controller/resource.1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: core.variant.run/v1beta1
kind: Resource
metadata:
name: myresource
spec:
env: preview
ref: abc1234
7 changes: 7 additions & 0 deletions examples/controller/resource.2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: core.variant.run/v1beta1
kind: Resource
metadata:
name: myresource
spec:
env: preview
ref: abc2345
31 changes: 31 additions & 0 deletions examples/controller/variant.crds.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: resources.core.variant.run
spec:
group: core.variant.run
versions:
- name: v1beta1
served: true
storage: true
names:
kind: Resource
plural: resources
singular: resource
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: reconciliations.core.variant.run
spec:
group: core.variant.run
versions:
- name: v1beta1
served: true
storage: true
names:
kind: Reconciliation
plural: reconciliations
singular: reconciliation
scope: Namespaced
2 changes: 2 additions & 0 deletions export_binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ $ build/simple app deploy -n default
if err != nil {
c.SilenceUsage = true
}

//nolint:wrapcheck
return err
},
}
Expand Down
Loading

0 comments on commit e700bc8

Please sign in to comment.