Skip to content

Commit

Permalink
Basic E2E test for machine controller
Browse files Browse the repository at this point in the history
  • Loading branch information
Richard Kovacs committed Jan 30, 2024
1 parent f0b7c2b commit 0234fb6
Show file tree
Hide file tree
Showing 20 changed files with 222 additions and 17 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/build_test_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ jobs:
disable-sudo: true
egress-policy: block
allowed-endpoints: >
api.linode.com:443
api.github.com:443
github.com:443
gcr.io:443
Expand Down Expand Up @@ -132,6 +133,8 @@ jobs:

- name: E2E test
run: make e2etest
env:
LINODE_TOKEN: ${{ secrets.LINODE_TOKEN }}

- uses: actions/upload-artifact@v4
if: ${{ always() }}
Expand Down
10 changes: 9 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ test: manifests generate fmt vet envtest ## Run tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test -race -timeout 60s ./... -coverprofile cover.out

.PHONY: e2etest
e2etest: kind kuttl kustomize clusterctl manifests generate docker-build
e2etest: kind kuttl kustomize clusterctl envsubst manifests generate docker-build
@echo -n "LINODE_TOKEN=$(LINODE_TOKEN)" > config/default/.env.linode
@$(CONTAINER_TOOL) tag ${IMG} capli-controller:e2e
IMG=capli-controller:e2e $(KUTTL) test --config e2e/kuttl-config.yaml
Expand Down Expand Up @@ -182,6 +182,7 @@ TILT ?= $(LOCALBIN)/tilt
KIND ?= $(LOCALBIN)/kind
KUTTL ?= $(LOCALBIN)/kuttl
ENVTEST ?= $(LOCALBIN)/setup-envtest
ENVSUBST ?= $(LOCALBIN)/envsubst
HUSKY ?= $(LOCALBIN)/husky
NILAWAY ?= $(LOCALBIN)/nilaway
GOVULNC ?= $(LOCALBIN)/govulncheck
Expand All @@ -194,6 +195,7 @@ CONTROLLER_TOOLS_VERSION ?= v0.13.0
TILT_VERSION ?= 0.33.6
KIND_VERSION ?= 0.20.0
KUTTL_VERSION ?= 0.15.0
ENVSUBST_VERSION ?= v1.4.2
HUSKY_VERSION ?= v0.2.16
NILAWAY_VERSION ?= latest
GOVULNC_VERSION ?= v1.0.1
Expand Down Expand Up @@ -253,6 +255,12 @@ envtest: $(ENVTEST) ## Download envtest-setup locally if necessary.
$(ENVTEST): $(LOCALBIN)
test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest

.PHONY: envsubst
envsubst: $(ENVSUBST) ## Download envsubst locally if necessary. If wrong version is installed, it will be overwritten.
$(ENVSUBST): $(LOCALBIN)
test -s $(ENVSUBST) || \
(cd $(LOCALBIN); curl -Lso ./envsubst https://github.com/a8m/envsubst/releases/download/$(ENVSUBST_VERSION)/envsubst-$(shell uname -s)-$(ARCH) && chmod +x envsubst)

.PHONY: husky
husky: $(HUSKY) ## Download husky locally if necessary.
@echo Execute install command to enable git hooks: ./bin/husky install
Expand Down
2 changes: 2 additions & 0 deletions config/manager/manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ spec:
secretKeyRef:
name: token
key: LINODE_TOKEN
- name: LINODE_API_VERSION
value: v4beta
name: manager
securityContext:
allowPrivilegeEscalation: false
Expand Down
60 changes: 46 additions & 14 deletions controller/linodemachine_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"time"

"github.com/go-logr/logr"
"github.com/google/uuid"
"github.com/linode/cluster-api-provider-linode/cloud/scope"
"github.com/linode/cluster-api-provider-linode/util"
"github.com/linode/cluster-api-provider-linode/util/reconciler"
Expand Down Expand Up @@ -93,6 +94,8 @@ type LinodeMachineReconciler struct {
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile
//
//nolint:gocyclo,cyclop // As simple as possible.
func (r *LinodeMachineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
ctx, cancel := context.WithTimeout(ctx, reconciler.DefaultedLoopTimeout(r.ReconcileTimeout))
defer cancel()
Expand All @@ -101,17 +104,21 @@ func (r *LinodeMachineReconciler) Reconcile(ctx context.Context, req ctrl.Reques

linodeMachine := &infrav1.LinodeMachine{}
if err := r.Client.Get(ctx, req.NamespacedName, linodeMachine); err != nil {
log.Error(err, "Failed to fetch Linode machine")
if err = client.IgnoreNotFound(err); err != nil {
log.Error(err, "Failed to fetch Linode machine")
}

return ctrl.Result{}, client.IgnoreNotFound(err)
return ctrl.Result{}, err
}

machine, err := kutil.GetOwnerMachine(ctx, r.Client, linodeMachine.ObjectMeta)
switch {
case err != nil:
log.Error(err, "Failed to fetch owner machine")
if err = client.IgnoreNotFound(err); err != nil {
log.Error(err, "Failed to fetch owner machine")
}

return ctrl.Result{}, client.IgnoreNotFound(err)
return ctrl.Result{}, err
case machine == nil:
log.Info("Machine Controller has not yet set OwnerRef, skipping reconciliation")

Expand All @@ -138,14 +145,25 @@ func (r *LinodeMachineReconciler) Reconcile(ctx context.Context, req ctrl.Reques
log = log.WithValues("Linode machine: ", machine.Name)

cluster, err := kutil.GetClusterFromMetadata(ctx, r.Client, machine.ObjectMeta)
if err != nil {
log.Info("Failed to fetch cluster by label")
switch {
case err != nil:
if err = client.IgnoreNotFound(err); err != nil {
log.Error(err, "Failed to fetch cluster by label")
}

return ctrl.Result{}, err
case cluster == nil:
err = errors.New("missing cluster")

log.Error(err, "Missing cluster")

return ctrl.Result{}, client.IgnoreNotFound(err)
} else if cluster == nil {
log.Info("Failed to find cluster by label")
return ctrl.Result{}, err
case cluster.Spec.InfrastructureRef == nil:
err = errors.New("missing infrastructure reference")

return ctrl.Result{}, client.IgnoreNotFound(err)
log.Error(err, "Missing infrastructure reference")

return ctrl.Result{}, err
}

linodeCluster := &infrav1.LinodeCluster{}
Expand All @@ -155,9 +173,11 @@ func (r *LinodeMachineReconciler) Reconcile(ctx context.Context, req ctrl.Reques
}

if err = r.Client.Get(ctx, linodeClusterKey, linodeCluster); err != nil {
log.Error(err, "Failed to fetch Linode cluster")
if err = client.IgnoreNotFound(err); err != nil {
log.Error(err, "Failed to fetch Linode cluster")
}

return ctrl.Result{}, client.IgnoreNotFound(err)
return ctrl.Result{}, err
}

machineScope, err := scope.NewMachineScope(
Expand Down Expand Up @@ -202,7 +222,7 @@ func (r *LinodeMachineReconciler) reconcile(
r.Recorder.Event(machineScope.LinodeMachine, corev1.EventTypeWarning, string(failureReason), err.Error())
}

if patchErr := machineScope.PatchHelper.Patch(ctx, machineScope.LinodeMachine); patchErr != nil && client.IgnoreNotFound(patchErr) != nil {
if patchErr := machineScope.PatchHelper.Patch(ctx, machineScope.LinodeMachine); patchErr != nil && util.IgnoreKubeNotFound(patchErr) != nil {
logger.Error(patchErr, "failed to patch LinodeMachine")

err = errors.Join(err, patchErr)
Expand Down Expand Up @@ -274,12 +294,24 @@ func (r *LinodeMachineReconciler) reconcileCreate(ctx context.Context, machineSc

return nil, err
}
createConfig.Tags = tags

if createConfig.Tags == nil {
createConfig.Tags = []string{}
}
createConfig.Tags = append(createConfig.Tags, tags...)

if createConfig.Label == "" {
createConfig.Label = util.RenderObjectLabel(machineScope.LinodeMachine.UID)
}

if createConfig.Image == "" {
createConfig.Image = reconciler.DefaultMachineControllerLinodeImage
}

if createConfig.RootPass == "" {
createConfig.RootPass = uuid.NewString()
}

if machineScope.LinodeCluster.Spec.VPCRef != nil {
iface, err := r.getVPCInterfaceConfig(ctx, machineScope, createConfig.Interfaces, logger)
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions e2e/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*-generated.yaml
18 changes: 18 additions & 0 deletions e2e/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
MAKEFLAGS += -s

runTestCase:
@T="$$(kuttl test --skip-delete --namespace $(NAMESPACE) $$(make _renderTestCase))" ;\
echo "$$T" | grep -s '0-step' ;\
echo "$$T" | grep -e '^PASS'

_renderTestCase:
@D=$$(mktemp -d) ;\
mkdir -p $$D/step ;\
envsubst -i $(TPL) -o $$D/step/00-step.yaml ;\
echo -n $$D

getKubeUid:
@kubectl get -o jsonpath='{.metadata.uid}' -n $(NAMESPACE) $R

callLinodeApiGet:
@curl -s -H "Authorization: Bearer $(LINODE_TOKEN)" -H "X-Filter: $(F)" "https://api.linode.com/v4beta/$(U)"
15 changes: 15 additions & 0 deletions e2e/basic/cluster/00-assert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: capi-controller-manager
namespace: capi-system
status:
availableReplicas: 1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: cluster-api-provider-linode-controller-manager
namespace: cluster-api-provider-linode-system
status:
availableReplicas: 1
15 changes: 15 additions & 0 deletions e2e/basic/machine/00-assert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: capi-controller-manager
namespace: capi-system
status:
availableReplicas: 1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: cluster-api-provider-linode-controller-manager
namespace: cluster-api-provider-linode-system
status:
availableReplicas: 1
17 changes: 17 additions & 0 deletions e2e/basic/machine/01-assert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
annotations:
cluster.x-k8s.io/paused: "true"
name: cluster-sample
spec:
paused: true
---
apiVersion: cluster.x-k8s.io/v1beta1
kind: Machine
metadata:
annotations:
cluster.x-k8s.io/paused: "true"
name: machine-sample
spec:
clusterName: cluster-sample
31 changes: 31 additions & 0 deletions e2e/basic/machine/01-create-cluster.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: LinodeCluster
metadata:
annotations:
cluster.x-k8s.io/paused: "true"
name: linodecluster-sample
---
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
annotations:
cluster.x-k8s.io/paused: "true"
name: cluster-sample
spec:
paused: true
infrastructureRef:
name: linodecluster-sample
---
apiVersion: cluster.x-k8s.io/v1beta1
kind: Machine
metadata:
annotations:
cluster.x-k8s.io/paused: "true"
name: machine-sample
spec:
clusterName: cluster-sample
bootstrap:
configRef:
apiVersion: v1
kind: "ConfigMap"
name: "boostrap-sample"
10 changes: 10 additions & 0 deletions e2e/basic/machine/02-assert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: LinodeMachine
metadata:
name: linodemachine-sample
spec:
region: us-southeast
type: g5-nanode-1
status:
ready: true
instanceState: running
13 changes: 13 additions & 0 deletions e2e/basic/machine/02-create-linodemachine.tpl.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: LinodeMachine
metadata:
ownerReferences:
- apiVersion: cluster.x-k8s.io/v1beta1
kind: Machine
name: machine-sample
uid: ${MACHINE_UID}
name: linodemachine-sample
namespace: ${NAMESPACE}
spec:
region: us-southeast
type: g5-nanode-1
8 changes: 8 additions & 0 deletions e2e/basic/machine/02-create-linodemachine.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
commands:
- script: |-
NAMESPACE=$NAMESPACE \
MACHINE_UID=$(R=machines/machine-sample make -C ../.. getKubeUid) \
TPL=$PWD/02-create-linodemachine.tpl.yml \
make -C ../.. runTestCase
6 changes: 6 additions & 0 deletions e2e/basic/machine/03-delete-linodemachine.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
delete:
- apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: LinodeMachine
name: linodemachine-sample
4 changes: 4 additions & 0 deletions e2e/basic/machine/03-error.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: LinodeMachine
metadata:
name: linodemachine-sample
5 changes: 5 additions & 0 deletions e2e/basic/machine/04-verify-linode-instance.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
commands:
- script: |-
U="linode/instances" F="{\\\"tags\\\":\\\"$(R=linodemachines/linodemachine-sample make -C ../.. getKubeUid)\\\"}" make -C ../.. callLinodeApiGet | grep 'results": 0'
15 changes: 15 additions & 0 deletions e2e/basic/vpc/00-assert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: capi-controller-manager
namespace: capi-system
status:
availableReplicas: 1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: cluster-api-provider-linode-controller-manager
namespace: cluster-api-provider-linode-system
status:
availableReplicas: 1
2 changes: 1 addition & 1 deletion e2e/kuttl-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ kindNodeCache: true
kindContainers:
- capli-controller:e2e
artifactsDir: .kind
timeout: 120
timeout: 300
commands:
- command: clusterctl init
- command: make deploy
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ toolchain go1.21.5

require (
github.com/go-logr/logr v1.4.1
github.com/google/uuid v1.3.1
github.com/linode/linodego v1.27.0
github.com/onsi/ginkgo/v2 v2.13.2
github.com/onsi/gomega v1.30.0
Expand Down Expand Up @@ -41,7 +42,6 @@ require (
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
Expand Down
2 changes: 2 additions & 0 deletions util/reconciler/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ const (
DefaultMachineControllerWaitForRunningDelay = 5 * time.Second
// DefaultMachineControllerWaitForRunningTimeout is the default timeout if instance is not running.
DefaultMachineControllerWaitForRunningTimeout = 20 * time.Minute
// DefaultMachineControllerLinodeImage default image.
DefaultMachineControllerLinodeImage = "linode/ubuntu22.04"

// DefaultVPCControllerWaitForHasNodesDelay is the default requeue delay if VPC has nodes.
DefaultVPCControllerWaitForHasNodesDelay = 5 * time.Second
Expand Down

0 comments on commit 0234fb6

Please sign in to comment.