Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Run Variant command as Kubernetes controller #35

Merged
merged 14 commits into from
Oct 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/whitebox-controller@v0.7.1=github.com/mumoshu/whitebox-controller@v0.5.1-0.20201028130131-ac7a0743254b" \
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/whitebox-controller@v0.7.1=github.com/mumoshu/whitebox-controller@v0.5.1-0.20201028130131-ac7a0743254b"

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