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

Rework HelmRelease reconciliation logic #738

Merged
merged 76 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
e82d389
helm/storage: add observator and implementation
hiddeco May 2, 2022
fe661df
Move HelmChart handling to separate reconciler
hiddeco May 2, 2022
0140eee
Factor various bits out of reconciler
hiddeco May 5, 2022
c99b00d
Move predicates into package and add tests
hiddeco May 6, 2022
730ccec
Move post renderers into separate package
hiddeco May 6, 2022
14e08f7
api: introduce v2beta2 API
hiddeco Jul 1, 2022
89a6f49
Run individual Helm actions using HelmRelease
hiddeco Jul 1, 2022
dfebba2
Add `ObservedRelease` and other release utils
hiddeco Jul 1, 2022
d9055f8
Add reconcile logic for individual Helm actions
hiddeco Jul 1, 2022
220e789
Allow detection of next reconcile action
hiddeco Jul 1, 2022
5843cc2
action: allow passing of config options
hiddeco Jul 6, 2022
6db62ed
Adding test filters
jtyr Jul 28, 2022
88a21fe
Moving stuff from runner; removing changes in v2beta1
jtyr Jul 28, 2022
e139354
Fixing typo
jtyr Jul 28, 2022
8cefed1
Adding tests
jtyr Jul 28, 2022
9e1eedc
api: various changes to support new logic
hiddeco Sep 15, 2022
0b8692f
api: add service account name validation rule
hiddeco Sep 20, 2022
9812286
action: add `Len` method to `LogBuffer`
hiddeco Sep 15, 2022
026fd45
action: add name param to rollback and uninstall
hiddeco Sep 15, 2022
4793414
action: allow composed release name >=53 char
hiddeco Sep 20, 2022
b975b3f
reconcile: add atomic release reconciler
hiddeco Sep 16, 2022
ea81c8e
action: include TS in LogBuffer
hiddeco Oct 5, 2022
64cc09c
reconcile: test emitted events
hiddeco Sep 27, 2022
410ce3a
reconcile: include "token" in event metadata
hiddeco Jun 14, 2023
64b2d54
Address review comments
hiddeco Jul 10, 2023
76f62ff
api: backport uninstall del propagation to v2beta2
hiddeco Jul 11, 2023
deb0b14
api: make v2beta2 storage version
hiddeco Jun 16, 2023
bb4e9b7
Update YAMLs to `helm.toolkit.fluxcd.io/v2beta2`
hiddeco Jun 16, 2023
eee91b0
Introduce new `yaml` package with `Encode` func
hiddeco Jul 17, 2023
d802ba6
controllers: roughly rewire HelmRelease reconciler
hiddeco Jul 24, 2023
bbefbc4
reconcile: use failure count in Stalled condition
hiddeco Jul 24, 2023
866f076
reconcile: share PatchHelper with controller
hiddeco Jul 24, 2023
68c273b
controller: handle delete before adding finalizer
hiddeco Jul 24, 2023
e32c1a0
reconcile: trim space from Helm error messages
hiddeco Jul 24, 2023
dab2578
acl: introduce package to enable global config
hiddeco Jul 27, 2023
5e3ad5d
reconcile: add `HelmChartTemplate` sub-reconciler
hiddeco Jul 27, 2023
1dac82a
reconcile: handle manually uninstalled release
hiddeco Jul 27, 2023
fbd73ac
controller: start w/ adding tests for HelmRelease
hiddeco Jul 28, 2023
b2ba3d9
controller: improve deletion logic and add tests
hiddeco Aug 21, 2023
9df9b17
api: various naming improvements
hiddeco Aug 21, 2023
7dfce0c
api: introduce `APIVersion` in `Snapshot`
hiddeco Aug 21, 2023
882da27
api: move `Current` and `Previous` into `History`
hiddeco Aug 25, 2023
94064da
controller: add reconcile release tests
hiddeco Sep 15, 2023
5510175
reconcile: tweak event messages
hiddeco Sep 22, 2023
bc036c0
reconcile: improve insights of progress in logs
hiddeco Oct 31, 2023
a6ae4c3
reconcile: improve log levels of actions
hiddeco Oct 31, 2023
272329d
action: add `:` separator between ts and msg logs
hiddeco Oct 31, 2023
19be1b2
api: change format of `Snapshot#FullReleaseName`
hiddeco Oct 31, 2023
ac9c2c3
reconcile: ensure object patch on context cancel
hiddeco Oct 31, 2023
f156c35
reconcile: allow cfg of manager in atomic action
hiddeco Oct 31, 2023
191bebf
reconcile: simplify `NextAction` logic
hiddeco Oct 31, 2023
d0c4c14
reconcile: improve uninstall w/o purging history
hiddeco Nov 1, 2023
096956f
controller: properly record object metrics
hiddeco Nov 3, 2023
7c52fd2
action: simplify chart diff logic
hiddeco Nov 3, 2023
2df90eb
reconcile: improve observability between actions
hiddeco Nov 3, 2023
80d0878
controller: ignore `NotFound` API error on delete
hiddeco Nov 3, 2023
10277c7
api: add `LastAttemptedReleaseAction` to status
hiddeco Nov 7, 2023
2e0e225
reconcile: improve state determination
hiddeco Nov 7, 2023
16da3ec
reconcile: allow unlock without current
hiddeco Nov 8, 2023
517d42f
build: incorporate condition changes in e2e
hiddeco Oct 31, 2023
c5a017c
api: record observed releases in `Status.History`
hiddeco Nov 17, 2023
533589c
api: change `MaxHistory` default to `5`
hiddeco Nov 17, 2023
eacd975
reconcile: remove reconciler type from messages
hiddeco Nov 17, 2023
7048501
controller: requeue on fixed interval on chart 404
hiddeco Nov 17, 2023
28a7800
reconcile: mark `Ready=Unknown` when awaiting test
hiddeco Nov 17, 2023
6f05228
reconcile: remove logs from test failure event
hiddeco Nov 17, 2023
9bb8f02
api: continue to record `LastAppliedRevision`
hiddeco Nov 21, 2023
eab8a50
api: prepare `v2beta1` status for `v2beta2`
hiddeco Nov 21, 2023
580c72c
controller: adopt release based on v2beta1 state
hiddeco Nov 21, 2023
20c00fd
action: provide a reason on release target changes
hiddeco Nov 22, 2023
5d1f34a
controller: patch after setting `Reconciling=True`
hiddeco Nov 22, 2023
7aad010
controller: immediate requeue unfinished release
hiddeco Nov 22, 2023
6ffdadd
action: omit logging on CRD apply no-op
hiddeco Nov 22, 2023
0535ae1
predicates: notice source changing to `Ready=True`
hiddeco Nov 22, 2023
3ce6e8d
reconcile: improve wording `Stalled` condition
hiddeco Nov 22, 2023
4a8d2ff
action: provide reason for failures count reset
hiddeco Nov 22, 2023
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
21 changes: 11 additions & 10 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ jobs:
kubectl -n helm-system apply -f config/testdata/$test_name
echo -n ">>> Waiting for expected conditions"
count=0
until [ 'true' == "$( kubectl -n helm-system get helmrelease/$test_name -o json | jq '.status.conditions | map( { (.type): .status } ) | add | .Released=="False" and .TestSuccess=="False" and .Ready=="False"' )" ]; do
until [ 'true' == "$( kubectl -n helm-system get helmrelease/$test_name -o json | jq '.status.conditions | map( { (.type): .status } ) | add | .Released=="True" and .TestSuccess=="False" and .Ready=="False"' )" ]; do
echo -n '.'
sleep 5
count=$((count + 1))
Expand Down Expand Up @@ -213,7 +213,7 @@ jobs:
fi

kubectl -n helm-system delete -f config/testdata/$test_name
- name: Run install fail with remedition test
- name: Run install fail with remediation test
run: |
test_name=install-fail-remediate
kubectl -n helm-system apply -f config/testdata/$test_name
Expand All @@ -230,21 +230,22 @@ jobs:
done
echo ' done'

# Ensure release does not exist (was uninstalled).
HISTORY=$(helm -n helm-system history $test_name 2>&1; exit 0)
if [ "$HISTORY" != 'Error: release: not found' ]; then
echo -e "Unexpected release history: $HISTORY"
# Ensure release was uninstalled.
RELEASE_STATUS=$(helm -n helm-system history $test_name -o json | jq -r 'if length == 1 then .[0].status else empty end')
if [ "$RELEASE_STATUS" != "uninstalled" ]; then
echo -e "Unexpected release status: $RELEASE_STATUS"
exit 1
fi

kubectl -n helm-system delete -f config/testdata/$test_name
helm -n helm-system delete $test_name
- name: Run install fail with retry test
run: |
test_name=install-fail-retry
kubectl -n helm-system apply -f config/testdata/$test_name
echo -n ">>> Waiting for expected conditions"
count=0
until [ 'true' == "$( kubectl -n helm-system get helmrelease/$test_name -o json | jq '.status.installFailures == 2 and ( .status.conditions | map( { (.type): .status } ) | add | .Released=="False" and .Ready=="False" )' )" ]; do
until [ 'true' == "$( kubectl -n helm-system get helmrelease/$test_name -o json | jq '.status.installFailures == 2 and ( .status.conditions | map( { (.type): .status } ) | add | .Released=="False" and .Ready=="False" and .Stalled=="True" )' )" ]; do
echo -n '.'
sleep 5
count=$((count + 1))
Expand Down Expand Up @@ -290,7 +291,7 @@ jobs:
kubectl -n helm-system apply -f config/testdata/$test_name/upgrade.yaml
echo -n ">>> Waiting for expected conditions"
count=0
until [ 'true' == "$( kubectl -n helm-system get helmrelease/$test_name -o json | jq '.status.conditions | map( { (.type): .status } ) | add | .Released=="False" and .Ready=="False"' )" ]; do
until [ 'true' == "$( kubectl -n helm-system get helmrelease/$test_name -o json | jq '.status.conditions | map( { (.type): .status } ) | add | .Released=="False" and .Ready=="False" and .Stalled=="True"' )" ]; do
echo -n '.'
sleep 5
count=$((count + 1))
Expand Down Expand Up @@ -336,7 +337,7 @@ jobs:
kubectl -n helm-system apply -f config/testdata/$test_name/upgrade.yaml
echo -n ">>> Waiting for expected conditions"
count=0
until [ 'true' == "$( kubectl -n helm-system get helmrelease/$test_name -o json | jq '.status.conditions | map( { (.type): .status } ) | add | .Released=="False" and .TestSuccess=="False" and .Ready=="False"' )" ]; do
until [ 'true' == "$( kubectl -n helm-system get helmrelease/$test_name -o json | jq '.status.conditions | map( { (.type): .status } ) | add | .Released=="True" and .TestSuccess=="False" and .Ready=="False" and .Stalled=="True"' )" ]; do
echo -n '.'
sleep 5
count=$((count + 1))
Expand Down Expand Up @@ -558,7 +559,7 @@ jobs:
exit 1
fi
kubectl -n helm-system delete -f config/testdata/post-renderer-kustomize
- name: Boostrap CRDs Upgrade Tests
- name: Bootstrap CRDs Upgrade Tests
if: ${{ startsWith(github.ref, 'refs/tags/') || startsWith(github.ref, 'refs/heads/') }}
run: |
REF=${{ github.ref }}
Expand Down
33 changes: 31 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ BUILD_PLATFORMS ?= linux/amd64
# Architecture to use envtest with
ENVTEST_ARCH ?= amd64

# Paths to download the CRD dependency to.
CRD_DEP_ROOT ?= $(BUILD_DIR)/config/crd/bases

# Keep a record of the version of the downloaded source CRDs. It is used to
# detect and download new CRDs when the SOURCE_VER changes.
SOURCE_VER ?= $(shell go list -m all | grep github.com/fluxcd/source-controller/api | awk '{print $$2}')
SOURCE_CRD_VER = $(CRD_DEP_ROOT)/.src-crd-$(SOURCE_VER)

# HelmChart source CRD.
HELMCHART_SOURCE_CRD ?= $(CRD_DEP_ROOT)/source.toolkit.fluxcd.io_helmcharts.yaml

# API (doc) generation utilities
CONTROLLER_GEN_VERSION ?= v0.12.0
GEN_API_REF_DOCS_VERSION ?= e327d0730470cbd61b06300f81c5fcf91c23c113
Expand All @@ -35,7 +46,7 @@ all: manager

# Run tests
KUBEBUILDER_ASSETS?="$(shell $(ENVTEST) --arch=$(ENVTEST_ARCH) use -i $(ENVTEST_KUBERNETES_VERSION) --bin-dir=$(ENVTEST_ASSETS_DIR) -p path)"
test: tidy generate fmt vet manifests api-docs install-envtest
test: tidy generate fmt vet manifests api-docs install-envtest download-crd-deps
KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) go test ./... -coverprofile cover.out
cd api; go test ./... -coverprofile cover.out

Expand Down Expand Up @@ -81,7 +92,7 @@ manifests: controller-gen

# Generate API reference documentation
api-docs: gen-crd-api-reference-docs
$(GEN_CRD_API_REFERENCE_DOCS) -api-dir=./api/v2beta1 -config=./hack/api-docs/config.json -template-dir=./hack/api-docs/template -out-file=./docs/api/v2beta1/helm.md
$(GEN_CRD_API_REFERENCE_DOCS) -api-dir=./api/v2beta2 -config=./hack/api-docs/config.json -template-dir=./hack/api-docs/template -out-file=./docs/api/v2beta2/helm.md

# Run go mod tidy
tidy:
Expand Down Expand Up @@ -113,6 +124,24 @@ docker-build:
docker-push:
docker push ${IMG}

# Delete previously downloaded CRDs and record the new version of the source
# CRDs.
$(SOURCE_CRD_VER):
rm -f $(CRD_DEP_ROOT)/.src-crd*
mkdir -p $(CRD_DEP_ROOT)
$(MAKE) cleanup-crd-deps
touch $(SOURCE_CRD_VER)

$(HELMCHART_SOURCE_CRD):
curl -s https://raw.githubusercontent.com/fluxcd/source-controller/${SOURCE_VER}/config/crd/bases/source.toolkit.fluxcd.io_helmcharts.yaml > $(HELMCHART_SOURCE_CRD)

# Download the CRDs the controller depends on
download-crd-deps: $(SOURCE_CRD_VER) $(HELMCHART_SOURCE_CRD)

# Delete the downloaded CRD dependencies.
cleanup-crd-deps:
rm -f $(HELMCHART_SOURCE_CRD)

# Find or download controller-gen
CONTROLLER_GEN = $(GOBIN)/controller-gen
.PHONY: controller-gen
Expand Down
4 changes: 4 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@ resources:
- group: helm
kind: HelmRelease
version: v2beta1
- group: helm
kind: HelmRelease
version: v2beta2
storageVersion: v2beta2
version: "2"
1 change: 1 addition & 0 deletions api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
k8s.io/apiextensions-apiserver v0.27.4
k8s.io/apimachinery v0.27.4
sigs.k8s.io/controller-runtime v0.15.1
sigs.k8s.io/yaml v1.3.0
)

require (
Expand Down
1 change: 1 addition & 0 deletions api/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,4 @@ sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h6
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
42 changes: 42 additions & 0 deletions api/v2beta1/helmrelease_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (

"github.com/fluxcd/pkg/apis/kustomize"
"github.com/fluxcd/pkg/apis/meta"

"github.com/fluxcd/helm-controller/api/v2beta2"
)

const HelmReleaseKind = "HelmRelease"
Expand Down Expand Up @@ -905,6 +907,46 @@ type HelmReleaseStatus struct {
// state. It is reset after a successful reconciliation.
// +optional
UpgradeFailures int64 `json:"upgradeFailures,omitempty"`

// StorageNamespace is the namespace of the Helm release storage for the
// current release.
//
// Note: this field is provisional to the v2beta2 API, and not actively used
// by v2beta1 HelmReleases.
// +optional
StorageNamespace string `json:"storageNamespace,omitempty"`

// History holds the history of Helm releases performed for this HelmRelease
// up to the last successfully completed release.
//
// Note: this field is provisional to the v2beta2 API, and not actively used
// by v2beta1 HelmReleases.
// +optional
History v2beta2.Snapshots `json:"history,omitempty"`

// LastAttemptedGeneration is the last generation the controller attempted
// to reconcile.
//
// Note: this field is provisional to the v2beta2 API, and not actively used
// by v2beta1 HelmReleases.
// +optional
LastAttemptedGeneration int64 `json:"lastAttemptedGeneration,omitempty"`

// LastAttemptedConfigDigest is the digest for the config (better known as
// "values") of the last reconciliation attempt.
//
// Note: this field is provisional to the v2beta2 API, and not actively used
// by v2beta1 HelmReleases.
// +optional
LastAttemptedConfigDigest string `json:"lastAttemptedConfigDigest,omitempty"`

// LastAttemptedReleaseAction is the last release action performed for this
// HelmRelease. It is used to determine the active remediation strategy.
//
// Note: this field is provisional to the v2beta2 API, and not actively used
// by v2beta1 HelmReleases.
// +optional
LastAttemptedReleaseAction string `json:"lastAttemptedReleaseAction,omitempty"`
}

// GetHelmChart returns the namespace and name of the HelmChart.
Expand Down
14 changes: 13 additions & 1 deletion api/v2beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

98 changes: 98 additions & 0 deletions api/v2beta2/condition_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
Copyright 2022 The Flux authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v2beta2

const (
// ReleasedCondition represents the status of the last release attempt
// (install/upgrade/test) against the latest desired state.
ReleasedCondition string = "Released"

// TestSuccessCondition represents the status of the last test attempt against
// the latest desired state.
TestSuccessCondition string = "TestSuccess"

// RemediatedCondition represents the status of the last remediation attempt
// (uninstall/rollback) due to a failure of the last release attempt against the
// latest desired state.
RemediatedCondition string = "Remediated"
)

const (
// InstallSucceededReason represents the fact that the Helm install for the
// HelmRelease succeeded.
InstallSucceededReason string = "InstallSucceeded"

// InstallFailedReason represents the fact that the Helm install for the
// HelmRelease failed.
InstallFailedReason string = "InstallFailed"

// UpgradeSucceededReason represents the fact that the Helm upgrade for the
// HelmRelease succeeded.
UpgradeSucceededReason string = "UpgradeSucceeded"

// UpgradeFailedReason represents the fact that the Helm upgrade for the
// HelmRelease failed.
UpgradeFailedReason string = "UpgradeFailed"

// TestSucceededReason represents the fact that the Helm tests for the
// HelmRelease succeeded.
TestSucceededReason string = "TestSucceeded"

// TestFailedReason represents the fact that the Helm tests for the HelmRelease
// failed.
TestFailedReason string = "TestFailed"

// RollbackSucceededReason represents the fact that the Helm rollback for the
// HelmRelease succeeded.
RollbackSucceededReason string = "RollbackSucceeded"

// RollbackFailedReason represents the fact that the Helm test for the
// HelmRelease failed.
RollbackFailedReason string = "RollbackFailed"

// UninstallSucceededReason represents the fact that the Helm uninstall for the
// HelmRelease succeeded.
UninstallSucceededReason string = "UninstallSucceeded"

// UninstallFailedReason represents the fact that the Helm uninstall for the
// HelmRelease failed.
UninstallFailedReason string = "UninstallFailed"

// ArtifactFailedReason represents the fact that the artifact download for the
// HelmRelease failed.
ArtifactFailedReason string = "ArtifactFailed"

// InitFailedReason represents the fact that the initialization of the Helm
// configuration failed.
InitFailedReason string = "InitFailed"

// GetLastReleaseFailedReason represents the fact that observing the last
// release failed.
GetLastReleaseFailedReason string = "GetLastReleaseFailed"

// DependencyNotReadyReason represents the fact that
// one of the dependencies is not ready.
DependencyNotReadyReason string = "DependencyNotReady"

// ReconciliationSucceededReason represents the fact that
// the reconciliation succeeded.
ReconciliationSucceededReason string = "ReconciliationSucceeded"

// ReconciliationFailedReason represents the fact that
// the reconciliation failed.
ReconciliationFailedReason string = "ReconciliationFailed"
)
20 changes: 20 additions & 0 deletions api/v2beta2/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
Copyright 2022 The Flux authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package v2beta2 contains API Schema definitions for the helm v2beta2 API group
// +kubebuilder:object:generate=true
// +groupName=helm.toolkit.fluxcd.io
package v2beta2
Loading