Skip to content

Rework reconciliations not to use steps based on status #153

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

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8ccde42
Rework reconciliations not to use steps based on status
vincentmrg Nov 8, 2024
bb3d98b
Use sync/errgroup to fix firewall rules reconciliation
vincentmrg Nov 27, 2024
1d6759c
Export metrics from aws-sdk (#156)
vincentmrg Dec 3, 2024
78eab0b
Change the level of log for some log and add log (#160)
klibr007 Jan 17, 2025
270170f
Improve FirewallRule status and fix SecurityGroup not found error (#161)
klibr007 Feb 4, 2025
9036600
Update golangci lint configuration (#166)
antonincms Feb 12, 2025
ce31565
Unitary FirewallRule reconciliation (#165)
klibr007 Feb 13, 2025
be8f693
Add FirewallRule webhook (#170)
klibr007 Feb 26, 2025
4bb2fff
Integrate nodename missing or error when getting the node, in the Fir…
klibr007 Mar 3, 2025
b51b65d
Better management of status and conditions for externalIP (#169)
klibr007 Mar 5, 2025
1501b73
Add cache mechanism in the provider (#172)
klibr007 Mar 5, 2025
21232c4
Allow building chart on tag or manual dispatch (#174)
antonincms Mar 6, 2025
eb258dd
Fix the deletion of the firewallrule when the node is not found (#175)
klibr007 Mar 20, 2025
7470ebf
Fix bug caused by controller-runtime caching (#176)
klibr007 Mar 26, 2025
4db6cac
Bump chart to 0.13.0-rc.4 (#177)
klibr007 Mar 27, 2025
caf1151
Merge branch 'main' into release/0.13.0
antonincms Mar 31, 2025
885a67d
Upgrade go and golangci lint (#184)
antonincms Apr 3, 2025
8741768
Bump docker go version to 1.23 (#185)
antonincms Apr 3, 2025
fe524d3
Fix PR remarks (#183)
klibr007 Apr 3, 2025
918646d
Clone the firewallrule conditions before computing any condition
klibr007 Apr 3, 2025
a6d16b6
Bump chart to 0.13.0-rc.5 (#187)
klibr007 Apr 15, 2025
ac3169a
Removes useless imds and add support for kubestatic-id tag (#188)
klibr007 Apr 17, 2025
c5be364
Add a wrapper for ModifyNetworkInterfaceAttribute method (#190)
klibr007 Apr 24, 2025
5a4de3b
Bump chart to 0.13.0-rc.6 (#191)
klibr007 Apr 24, 2025
908b60c
Update the chart to use the cluster-id value (#192)
klibr007 Apr 25, 2025
679594a
Add imds to retrieve vpcid when needed (#193)
klibr007 Apr 28, 2025
8937440
Test AWS provider reconciliation functions and small fixes (#186)
klibr007 Apr 30, 2025
8063ddf
Add migration scripts from previous version to 0.13.0 and migration p…
klibr007 May 7, 2025
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
35 changes: 31 additions & 4 deletions .github/workflows/checks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,41 @@ jobs:
with:
go-version-file: go.mod

# Instead of using our own CI, we uses https://golangci-lint.run/
# It combines 48 linters and execute them in parallel
- name: Lint with golangci-lint
uses: golangci/golangci-lint-action@v6
uses: golangci/golangci-lint-action@v7
with:
version: v1.59.1
version: v2.0.2
args: --timeout 5m

check-uncommitted-changes:
name: Check uncommitted changes
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Generate code
run: |
go generate ./...
make all

# Fails if any generated files is different from what is commited
- name: Checks uncommitted changes to generated files
run: |
changes=$(git status --porcelain)
if [[ -z $changes ]];
then
echo "No uncommitted changes to generated files"
else
echo "Error : there are uncommitted changes to generated files :"
echo "$changes"
exit 1
fi

test:
name: Test
runs-on: ubuntu-latest
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/helm-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ on:
push:
branches:
- main
tags:
- "*"
workflow_dispatch: {}

jobs:
release:
Expand Down
105 changes: 60 additions & 45 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,65 +1,80 @@
version: "2"

run:
timeout: 5m
allow-parallel-runners: true

linters-settings:
dupl:
# Increased from 150 because this operator contain a lot of boilerplate.
threshold: 200
goimports:
local-prefixes: github.com/quortex/kubestatic
gocyclo:
# Increased from 30 because existing reconcile functions are complex.
min-complexity: 40
lll:
line-length: 150

linters:
disable-all: true
default: none
enable:
- copyloopvar
- dupl
- errcheck
- exportloopref
- ginkgolinter
- goconst
- gocyclo
- gofmt
- goimports
- gosimple
- govet
- ineffassign
- lll
- misspell
- nakedret
- prealloc
- revive
- staticcheck
- typecheck
- unconvert
- unparam
- unused
settings:
dupl:
# Increased from 150 because this operator contain a lot of boilerplate.
threshold: 200
gocyclo:
# Increased from 30 because existing reconcile functions are complex.
min-complexity: 40
lll:
line-length: 150
revive:
rules:
- name: comment-spacings
exclusions:
generated: lax
rules:
# Exclude `lll` issues for long comments starting with `go:generate` or `+kubebuilder`.
- source: ^\/\/\s(go:generate|\+kubebuilder)
linters:
- lll
# Exclude dupl since controllers have a lot of boilerplate code, and exclude
# dynamic and unwrapped errors issues in controllers since they will only be logged.
- path: internal/controller/
linters:
- dupl
- err113
- wrapcheck
# Exclude some linters from running on tests files.
- path: _test\.go
linters:
- dupl
- funlen
- goconst
- gocyclo
- lll
- mnd
- varnamelen
paths:
- third_party$
- builtin$
- examples$

issues:
# Don't exclude the default set of lint
exclude-use-default: false
# Excluding configuration per-path, per-linter, per-text and per-source
exclude-rules:
# Exclude `lll` issues for long comments starting with `go:generate` or `+kubebuilder`.
- source: "^//(go:generate|\\+kubebuilder)"
linters:
- lll
# Exclude dupl since controllers have a lot of boilerplate code, and exclude
# dynamic and unwrapped errors issues in controllers since they will only be logged.
- path: controllers/
linters:
- dupl
- goerr113
- wrapcheck
# Exclude some linters from running on tests files.
- path: _test\.go
linters:
- dupl
- goconst
- gocyclo
- gomnd
- funlen
- lll
- varnamelen
formatters:
enable:
- gofmt
- goimports
settings:
goimports:
local-prefixes:
- github.com/quortex/kubestatic
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Build the manager binary
FROM golang:1.22 as builder
FROM golang:1.23 as builder
ARG TARGETOS
ARG TARGETARCH

Expand Down
29 changes: 22 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec

.PHONY: all
all: build doc
all: build mocks doc charts

##@ General

Expand Down Expand Up @@ -59,8 +59,13 @@ fmt: ## Run go fmt against code.
vet: ## Run go vet against code.
go vet ./...

.PHONY: mocks
mocks: mockgen ## Generate all mocks used for testing
@mkdir -p internal/provider/aws/mocks
@$(MOCKGEN) -source=internal/provider/aws/ec2_client.go -package=mocks -destination=internal/provider/aws/mocks/zz_generated.ec2_client.go

.PHONY: test
test: manifests generate fmt vet envtest ## Run tests.
test: mocks manifests generate fmt vet envtest ## Run tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out

# Utilize Kind or modify the e2e tests to load the image locally, enabling compatibility with other vendors.
Expand Down Expand Up @@ -90,9 +95,12 @@ charts: yq helm-docs kustomize ## Generate helm charts and their documentations
@$(YQ) '.metadata.name = ("PREFIX-" + .metadata.name)' config/rbac/role.yaml | \
sed "s/PREFIX/{{ include \"kubestatic.fullname\" . }}/" > helm/kubestatic/templates/manager_role.yaml
@$(KUSTOMIZE) build config/default/ > $(TMP)/result.yaml
${YQ} 'select(.kind=="CustomResourceDefinition")' $${TMPFILE} > helm/kubestatic/crds/crds.yaml && \
rm -rf $${TMPFILE}
@$(YQ) 'select(.kind=="ValidatingWebhookConfiguration" or .kind=="MutatingWebhookConfiguration")' $(TMP)/result.yaml | \
sed -e 's|name: kubestatic|name: {{ include \"kubestatic.fullname\" . }}|' \
-e 's|cert-manager.io/inject-ca-from: .*|cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ include \"kubestatic.fullname\" . }}-serving-cert|' \
-e 's|namespace: .*|namespace: {{ .Release.Namespace }}|' > helm/kubestatic/templates/webhooks.yaml
@$(YQ) 'select(.kind=="CustomResourceDefinition")' $(TMP)/result.yaml > helm/kubestatic/crds/crds.yaml
@rm -rf $(TMP)
@$(HELM_DOCS) -s file


Expand Down Expand Up @@ -123,7 +131,7 @@ docker-push: ## Push docker image with the manager.
# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/
# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=<myregistry/image:<tag>> then the export will fail)
# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option.
PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le
PLATFORMS ?= linux/arm64,linux/amd64
.PHONY: docker-buildx
docker-buildx: ## Build and push docker image for the manager for cross-platform support
# copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile
Expand Down Expand Up @@ -179,15 +187,17 @@ CRD_REF_DOCS ?= $(LOCALBIN)/crd-ref-docs
GOLANGCI_LINT?= $(LOCALBIN)/golangci-lint
YQ ?= $(LOCALBIN)/yq
HELM_DOCS ?= $(LOCALBIN)/helm-docs
MOCKGEN ?= $(LOCALBIN)/mockgen

## Tool Versions
KUSTOMIZE_VERSION ?= v5.4.3
CONTROLLER_TOOLS_VERSION ?= v0.16.1
ENVTEST_VERSION ?= release-0.19
GOLANGCI_LINT_VERSION ?= v1.59.1
GOLANGCI_LINT_VERSION ?= v2.0.2
CRD_REF_DOCS_VERSION ?= v0.1.0
YQ_VERSION ?= v4.44.3
HELM_DOCS_VERSION ?= v1.14.2
MOCKGEN_VERSION ?= v0.5.1

.PHONY: kustomize
kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary.
Expand All @@ -207,7 +217,7 @@ $(ENVTEST): $(LOCALBIN)
.PHONY: golangci-lint
golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
$(GOLANGCI_LINT): $(LOCALBIN)
$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))
$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))

.PHONY: crd-ref-docs
crd-ref-docs: $(CRD_REF_DOCS) ## Download crd-ref-docs locally if necessary.
Expand All @@ -224,6 +234,11 @@ helm-docs: $(HELM_DOCS) ## Download helm-docs locally if necessary.
$(HELM_DOCS): $(LOCALBIN)
$(call go-install-tool,$(HELM_DOCS),github.com/norwoodj/helm-docs/cmd/helm-docs,${HELM_DOCS_VERSION})

.PHONY: mockgen
mockgen: $(MOCKGEN) ## Download mockgen if necessary.
$(MOCKGEN): $(LOCALBIN)
$(call go-install-tool,$(MOCKGEN),go.uber.org/mock/mockgen,$(MOCKGEN_VERSION))

# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
# $1 - target path with name of binary
# $2 - package url which can be installed
Expand Down
7 changes: 7 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Code generated by tool. DO NOT EDIT.
# This file is used to track the info used to scaffold your project
# and allow the plugins properly work.
# More info: https://book.kubebuilder.io/reference/project-config.html
domain: kubestatic.quortex.io
layout:
- go.kubebuilder.io/v4
Expand All @@ -24,4 +28,7 @@ resources:
kind: FirewallRule
path: github.com/quortex/kubestatic/api/v1alpha1
version: v1alpha1
webhooks:
validation: true
webhookVersion: v1
version: "3"
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,14 @@ The kubestatic container takes as argument the parameters below.
| Key | Description | Default |
| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | ------- |
| cloud-provider | Which cloud provider to deploy to. Available values: aws | "" |
| cluster-id | A required unique identifier used to track ownership of cloud resources. | `unset` |
| metrics-bind-address | The address the metric endpoint binds to. | :8080 |
| health-probe-bind-address | The address the probe endpoint binds to. | :8081 |
| leader-elect | Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager. | `false` |
| prevent-eip-deallocation | Prevent EIP deallocation on nodes auto-assigned ExternalIPs. | `false` |
| node-min-reconciliation-interval | The minimum duration to wait between two reconciliations for the same node. | 10s |
| node-reconciliation-requeue-interval | The duration for which nodes are automatically reconciled. | 1m |
| vpc-id | The VPC ID to use. If not set, the VPC ID will be retrieved from the instance metadata. | "" |

## License

Expand All @@ -152,3 +154,33 @@ Got a question?
File a GitHub [issue](https://github.com/quortex/kubestatic/issues).

[logo]: https://storage.googleapis.com/quortex-assets/logo.webp

## ⚠️ Breaking Change in Tagging Behavior (v0.13.0)

Starting from version **v0.13.0**, `kubestatic` introduces a **breaking change** in the way cloud resources (External IPs and Security Groups) are tagged.

To ensure proper tracking and resource ownership, the following tags are now applied to AWS resources:

- `kubestatic.quortex.io/managed`
- `kubestatic.quortex.io/instance-id`
- `kubestatic.quortex.io/node-name` (for Security Groups)
- `kubestatic.quortex.io/external-ip-name` (for External IPs)
- `kubestatic.quortex.io/cluster-id` *(new requirement)*

This change may affect existing resources created by earlier versions of `kubestatic`, as they may be missing the required tags. Without proper tagging, the controller will **not** manage these resources, and even try to recreate them.

### 🛠 Migration Guide

To help you transition smoothly, two migration scripts are provided in the [`migration-scripts/`](./migration-scripts) directory. These scripts need to be run before upgrading:

- [`tag_externalips_from_k8s.sh`](./migration-scripts/tag_externalips_from_k8s.sh):
Tags existing **Elastic IPs (EIPs)** by resolving the node names and their corresponding EC2 instance IDs.

- [`tag_firewallrules_from_k8s.sh`](./migration-scripts/tag_firewallrules_from_k8s.sh):
Tags existing **Security Groups (SGs)** based on the status fields of `FirewallRule` CRDs.

Both scripts require a `cluster-id` as a parameter, which must match the cluster-id specified in your Helm values:

```bash
./migration-scripts/tag_externalips_from_k8s.sh <cluster-id>
./migration-scripts/tag_firewallrules_from_k8s.sh <cluster-id>
24 changes: 18 additions & 6 deletions api/v1alpha1/externalip_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,24 @@ type ExternalIPState string

// All defined ExternalIPStates
const (
ExternalIPStateNone ExternalIPState = ""
ExternalIPStateReserved ExternalIPState = "Reserved"
ExternalIPStatePending ExternalIPState = "Pending"
ExternalIPStateAssociated ExternalIPState = "Associated"
)

// The list of condition types.
const (
ExternalIPConditionTypeIPCreated = "IPCreated"
ExternalIPConditionTypeNetworkInterfaceAssociated = "NetworkInterfaceAssociated"
)

// The list of condition reasons.
const (
ExternalIPConditionReasonProviderError = "ProviderError"
ExternalIPConditionReasonIPCreated = "IPCreated"
ExternalIPConditionReasonNetworkInterfaceAssociated = "NetworkInterfaceAssociated"
)

// ExternalIPStatus defines the observed state of ExternalIP
type ExternalIPStatus struct {
// The current state of the ExternalIP
Expand All @@ -60,6 +73,10 @@ type ExternalIPStatus struct {

// The instance identifier
InstanceID *string `json:"instanceID,omitempty"`

// Current conditions of the ExternalIP.
// +kubebuilder:validation:Optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
}

// +kubebuilder:object:root=true
Expand All @@ -78,11 +95,6 @@ type ExternalIP struct {
Status ExternalIPStatus `json:"status,omitempty"`
}

// IsReserved returns if externalIP is reserved
func (e *ExternalIP) IsReserved() bool {
return e.Status.State == ExternalIPStateReserved
}

// IsAssociated returns if externalIP is associated
func (e *ExternalIP) IsAssociated() bool {
return e.Status.State == ExternalIPStateAssociated
Expand Down
Loading
Loading