Compare commits
No commits in common. "main" and "api/v0.28.1" have entirely different histories.
main
...
api/v0.28.
|
@ -1,39 +0,0 @@
|
|||
version: 2
|
||||
|
||||
updates:
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/"
|
||||
labels: ["dependencies"]
|
||||
schedule:
|
||||
interval: "daily"
|
||||
groups:
|
||||
go-deps:
|
||||
patterns:
|
||||
- "*"
|
||||
allow:
|
||||
- dependency-type: "direct"
|
||||
ignore:
|
||||
# Kubernetes deps are updated by fluxcd/pkg/runtime
|
||||
- dependency-name: "k8s.io/*"
|
||||
- dependency-name: "sigs.k8s.io/*"
|
||||
- dependency-name: "github.com/go-logr/*"
|
||||
# jsondiff is updated by fluxcd/pkg/ssa
|
||||
- dependency-name: "github.com/wI2L/jsondiff"
|
||||
# OCI deps are updated by fluxcd/pkg/oci
|
||||
- dependency-name: "github.com/google/go-containerregistry*"
|
||||
- dependency-name: "github.com/opencontainers/*"
|
||||
# Helm deps are updated by fluxcd/pkg/helmtestserver
|
||||
- dependency-name: "helm.sh/helm/*"
|
||||
- dependency-name: "github.com/Masterminds/semver/*"
|
||||
# Flux APIs are updated at release time
|
||||
- dependency-name: "github.com/fluxcd/helm-controller/api"
|
||||
- dependency-name: "github.com/fluxcd/source-controller/api"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
labels: ["area/ci", "dependencies"]
|
||||
groups:
|
||||
ci:
|
||||
patterns:
|
||||
- "*"
|
||||
schedule:
|
||||
interval: "monthly"
|
|
@ -1,27 +0,0 @@
|
|||
# Configuration file to declaratively configure labels
|
||||
# Ref: https://github.com/EndBug/label-sync#Config-files
|
||||
|
||||
- name: area/drift
|
||||
description: Drift detection/correction related issues and pull requests
|
||||
color: '#ff5c00'
|
||||
- name: area/helm
|
||||
description: Helm related issues and pull requests
|
||||
color: '#1673b6'
|
||||
- name: area/kustomize
|
||||
description: Kustomize (post-rendering) related issues and pull requests
|
||||
color: '#00e54d'
|
||||
- name: area/oci
|
||||
description: OCI related issues and pull requests
|
||||
color: '#c739ff'
|
||||
- name: backport:release/v1.0.x
|
||||
description: To be backported to release/v1.0.x
|
||||
color: '#ffd700'
|
||||
- name: backport:release/v1.1.x
|
||||
description: To be backported to release/v1.1.x
|
||||
color: '#ffd700'
|
||||
- name: backport:release/v1.2.x
|
||||
description: To be backported to release/v1.2.x
|
||||
color: '#ffd700'
|
||||
- name: backport:release/v1.3.x
|
||||
description: To be backported to release/v1.3.x
|
||||
color: '#ffd700'
|
|
@ -1,34 +0,0 @@
|
|||
name: backport
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [closed, labeled]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
pull-request:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
if: github.event.pull_request.state == 'closed' && github.event.pull_request.merged && (github.event_name != 'labeled' || startsWith('backport:', github.event.label.name))
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Create backport PRs
|
||||
uses: korthout/backport-action@0193454f0c5947491d348f33a275c119f30eb736 # v3.2.1
|
||||
# xref: https://github.com/korthout/backport-action#inputs
|
||||
with:
|
||||
# Use token to allow workflows to be triggered for the created PR
|
||||
github_token: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||
# Match labels with a pattern `backport:<target-branch>`
|
||||
label_pattern: '^backport:([^ ]+)$'
|
||||
# A bit shorter pull-request title than the default
|
||||
pull_title: '[${target_branch}] ${pull_title}'
|
||||
# Simpler PR description than default
|
||||
pull_description: |-
|
||||
Automated backport to `${target_branch}`, triggered by a label in #${pull_number}.
|
|
@ -2,8 +2,7 @@ name: fuzz
|
|||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/**"
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read # for actions/checkout to fetch code
|
||||
|
@ -13,13 +12,17 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.24.x
|
||||
cache-dependency-path: |
|
||||
**/go.sum
|
||||
**/go.mod
|
||||
go-version: 1.19.x
|
||||
- name: Restore Go cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go
|
||||
- name: Smoke test Fuzzers
|
||||
run: make fuzz-smoketest
|
||||
|
|
|
@ -4,8 +4,8 @@ on:
|
|||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/**"
|
||||
- main
|
||||
- "feature/**"
|
||||
|
||||
permissions:
|
||||
contents: read # for actions/checkout to fetch code
|
||||
|
@ -15,16 +15,25 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup QEMU
|
||||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||
uses: docker/setup-qemu-action@v1
|
||||
with:
|
||||
platforms: all
|
||||
- name: Setup Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
buildkitd-flags: "--debug"
|
||||
- name: Restore Go cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
uses: actions/cache@v3
|
||||
id: cache
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
|
@ -32,24 +41,26 @@ jobs:
|
|||
restore-keys: |
|
||||
${{ runner.os }}-buildx-ghcache-
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.24.x
|
||||
cache-dependency-path: |
|
||||
**/go.sum
|
||||
**/go.mod
|
||||
go-version: 1.19.x
|
||||
- name: Setup Kubernetes
|
||||
uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0
|
||||
uses: engineerd/setup-kind@v0.5.0
|
||||
with:
|
||||
version: v0.20.0
|
||||
cluster_name: kind
|
||||
node_image: kindest/node:v1.27.3@sha256:3966ac761ae0136263ffdb6cfd4db23ef8a83cba8a463690e98317add2c9ba72
|
||||
version: v0.11.1
|
||||
image: kindest/node:v1.23.13
|
||||
- name: Setup Helm
|
||||
uses: fluxcd/pkg/actions/helm@main
|
||||
- name: Setup Kustomize
|
||||
uses: fluxcd/pkg/actions/kustomize@main
|
||||
- name: Setup Kubebuilder
|
||||
uses: fluxcd/pkg/actions/kubebuilder@main
|
||||
- name: Setup Kubectl
|
||||
uses: fluxcd/pkg/actions/kubectl@main
|
||||
- name: Run tests
|
||||
run: make test
|
||||
env:
|
||||
KUBEBUILDER_ASSETS: ${{ github.workspace }}/kubebuilder/bin
|
||||
- name: Check if working tree is dirty
|
||||
run: |
|
||||
if [[ $(git diff --stat) != '' ]]; then
|
||||
|
@ -91,14 +102,6 @@ jobs:
|
|||
kubectl -n helm-system rollout status deploy/helm-controller --timeout=1m
|
||||
env:
|
||||
KUBEBUILDER_ASSETS: ${{ github.workspace }}/kubebuilder/bin
|
||||
- name: Test samples
|
||||
run: |
|
||||
kubectl create ns samples
|
||||
kubectl -n samples apply -f config/samples
|
||||
kubectl -n samples wait hr/podinfo-ocirepository --for=condition=ready --timeout=4m
|
||||
kubectl -n samples wait hr/podinfo-gitrepository --for=condition=ready --timeout=4m
|
||||
kubectl -n samples wait hr/podinfo-helmrepository --for=condition=ready --timeout=4m
|
||||
kubectl delete ns samples
|
||||
- name: Install sources
|
||||
run: |
|
||||
kubectl -n helm-system apply -f config/testdata/sources
|
||||
|
@ -146,16 +149,6 @@ jobs:
|
|||
kubectl -n install-create-target-ns get deployment install-create-target-ns-install-create-target-ns-podinfo
|
||||
|
||||
kubectl -n helm-system delete -f config/testdata/install-create-target-ns
|
||||
- name: Run install from helmChart test
|
||||
run: |
|
||||
kubectl -n helm-system apply -f config/testdata/install-from-hc-source
|
||||
kubectl -n helm-system wait helmreleases/podinfo-from-hc --for=condition=ready --timeout=4m
|
||||
kubectl -n helm-system delete -f config/testdata/install-from-hc-source
|
||||
- name: Run install from ocirepo test
|
||||
run: |
|
||||
kubectl -n helm-system apply -f config/testdata/install-from-ocirepo-source
|
||||
kubectl -n helm-system wait helmreleases/podinfo-from-ocirepo --for=condition=ready --timeout=4m
|
||||
kubectl -n helm-system delete -f config/testdata/install-from-ocirepo-source
|
||||
- name: Run install fail test
|
||||
run: |
|
||||
test_name=install-fail
|
||||
|
@ -187,7 +180,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=="True" 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=="False" and .TestSuccess=="False" and .Ready=="False"' )" ]; do
|
||||
echo -n '.'
|
||||
sleep 5
|
||||
count=$((count + 1))
|
||||
|
@ -231,7 +224,7 @@ jobs:
|
|||
fi
|
||||
|
||||
kubectl -n helm-system delete -f config/testdata/$test_name
|
||||
- name: Run install fail with remediation test
|
||||
- name: Run install fail with remedition test
|
||||
run: |
|
||||
test_name=install-fail-remediate
|
||||
kubectl -n helm-system apply -f config/testdata/$test_name
|
||||
|
@ -248,22 +241,21 @@ jobs:
|
|||
done
|
||||
echo ' done'
|
||||
|
||||
# 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"
|
||||
# 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"
|
||||
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" and .Stalled=="True" )' )" ]; 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" )' )" ]; do
|
||||
echo -n '.'
|
||||
sleep 5
|
||||
count=$((count + 1))
|
||||
|
@ -309,7 +301,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" and .Stalled=="True"' )" ]; 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"' )" ]; do
|
||||
echo -n '.'
|
||||
sleep 5
|
||||
count=$((count + 1))
|
||||
|
@ -355,7 +347,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=="True" and .TestSuccess=="False" and .Ready=="False" and .Stalled=="True"' )" ]; do
|
||||
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
|
||||
echo -n '.'
|
||||
sleep 5
|
||||
count=$((count + 1))
|
||||
|
@ -476,45 +468,6 @@ jobs:
|
|||
fi
|
||||
|
||||
kubectl delete -n helm-system -f config/testdata/$test_name/install.yaml
|
||||
- name: Run upgrade from ocirepo source
|
||||
run: |
|
||||
test_name=upgrade-from-ocirepo-source
|
||||
kubectl -n helm-system apply -f config/testdata/$test_name/install.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=="True" and .Ready=="True"' )" ]; do
|
||||
echo -n '.'
|
||||
sleep 5
|
||||
count=$((count + 1))
|
||||
if [[ ${count} -eq 24 ]]; then
|
||||
echo ' No more retries left!'
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo ' done'
|
||||
|
||||
# Validate release was installed.
|
||||
REVISION_COUNT=$(helm -n helm-system history -o json $test_name | jq 'length')
|
||||
if [ "$REVISION_COUNT" != 1 ]; then
|
||||
echo -e "Unexpected revision count: $REVISION_COUNT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
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=="True" and .Ready=="True"' )" ]; do
|
||||
echo -n '.'
|
||||
sleep 5
|
||||
count=$((count + 1))
|
||||
if [[ ${count} -eq 24 ]]; then
|
||||
echo ' No more retries left!'
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo ' done'
|
||||
|
||||
kubectl delete -n helm-system -f config/testdata/$test_name/install.yaml
|
||||
- name: Run upgrade fail with uninstall remediation strategy test
|
||||
run: |
|
||||
test_name=upgrade-fail-remediate-uninstall
|
||||
|
@ -589,19 +542,6 @@ jobs:
|
|||
fi
|
||||
done
|
||||
echo ' done'
|
||||
- name: Run delete-ns tests
|
||||
run: |
|
||||
kubectl apply -f config/testdata/delete-ns
|
||||
kubectl -n delete-ns wait helmreleases/podinfo --for=condition=ready --timeout=2m
|
||||
kubectl delete ns delete-ns 1>/dev/null 2>&1 &
|
||||
echo -n ">>> Waiting for namespace to be deleted"
|
||||
if kubectl wait --for=delete namespace delete-ns --timeout=5m; then
|
||||
echo ' Namespace deleted successfully'
|
||||
else
|
||||
echo ' Timed out waiting for namespace to be deleted'
|
||||
kubectl get all -n delete-ns
|
||||
exit 1
|
||||
fi
|
||||
- name: Run post-renderer-kustomize test
|
||||
run: |
|
||||
kubectl -n helm-system apply -f config/testdata/post-renderer-kustomize
|
||||
|
@ -617,7 +557,7 @@ jobs:
|
|||
exit 1
|
||||
fi
|
||||
kubectl -n helm-system delete -f config/testdata/post-renderer-kustomize
|
||||
- name: Bootstrap CRDs Upgrade Tests
|
||||
- name: Boostrap CRDs Upgrade Tests
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') || startsWith(github.ref, 'refs/heads/') }}
|
||||
run: |
|
||||
REF=${{ github.ref }}
|
||||
|
@ -654,6 +594,9 @@ jobs:
|
|||
- name: Debug failure
|
||||
if: failure()
|
||||
run: |
|
||||
which kubectl
|
||||
kubectl version
|
||||
helm version
|
||||
kubectl -n helm-system get helmrepositories -oyaml || true
|
||||
kubectl -n helm-system get helmcharts -oyaml || true
|
||||
kubectl -n helm-system get helmreleases -oyaml || true
|
||||
|
|
|
@ -14,17 +14,18 @@ jobs:
|
|||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup QEMU
|
||||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||
uses: docker/setup-qemu-action@v1
|
||||
with:
|
||||
platforms: all
|
||||
- name: Setup Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
buildkitd-flags: "--debug"
|
||||
- name: Build multi-arch container image
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
push: false
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
|
|
|
@ -7,29 +7,22 @@ on:
|
|||
inputs:
|
||||
tag:
|
||||
description: 'image tag prefix'
|
||||
default: 'preview'
|
||||
default: 'rc'
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
contents: write # needed to write releases
|
||||
id-token: write # needed for keyless signing
|
||||
packages: write # needed for ghcr access
|
||||
|
||||
env:
|
||||
CONTROLLER: ${{ github.event.repository.name }}
|
||||
|
||||
jobs:
|
||||
release:
|
||||
outputs:
|
||||
hashes: ${{ steps.slsa.outputs.hashes }}
|
||||
image_url: ${{ steps.slsa.outputs.image_url }}
|
||||
image_digest: ${{ steps.slsa.outputs.image_digest }}
|
||||
build-push:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write # for creating the GitHub release.
|
||||
id-token: write # for creating OIDC tokens for signing.
|
||||
packages: write # for pushing and signing container images.
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Kustomize
|
||||
uses: fluxcd/pkg/actions/kustomize@main
|
||||
- name: Prepare
|
||||
|
@ -39,27 +32,27 @@ jobs:
|
|||
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||
VERSION=${GITHUB_REF/refs\/tags\//}
|
||||
fi
|
||||
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT
|
||||
echo ::set-output name=BUILD_DATE::$(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
echo ::set-output name=VERSION::${VERSION}
|
||||
- name: Setup QEMU
|
||||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Setup Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: fluxcdbot
|
||||
password: ${{ secrets.GHCR_TOKEN }}
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: fluxcdbot
|
||||
password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }}
|
||||
- name: Generate images meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
fluxcd/${{ env.CONTROLLER }}
|
||||
|
@ -67,11 +60,8 @@ jobs:
|
|||
tags: |
|
||||
type=raw,value=${{ steps.prep.outputs.VERSION }}
|
||||
- name: Publish images
|
||||
id: build-push
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
sbom: true
|
||||
provenance: true
|
||||
push: true
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
context: .
|
||||
|
@ -79,82 +69,32 @@ jobs:
|
|||
platforms: linux/amd64,linux/arm/v7,linux/arm64
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
- uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3.9.1
|
||||
- name: Check images
|
||||
run: |
|
||||
docker buildx imagetools inspect docker.io/fluxcd/${{ env.CONTROLLER }}:${{ steps.prep.outputs.VERSION }}
|
||||
docker buildx imagetools inspect ghcr.io/fluxcd/${{ env.CONTROLLER }}:${{ steps.prep.outputs.VERSION }}
|
||||
docker pull docker.io/fluxcd/${{ env.CONTROLLER }}:${{ steps.prep.outputs.VERSION }}
|
||||
docker pull ghcr.io/fluxcd/${{ env.CONTROLLER }}:${{ steps.prep.outputs.VERSION }}
|
||||
- uses: sigstore/cosign-installer@main
|
||||
- name: Sign images
|
||||
env:
|
||||
COSIGN_EXPERIMENTAL: 1
|
||||
run: |
|
||||
cosign sign --yes fluxcd/${{ env.CONTROLLER }}@${{ steps.build-push.outputs.digest }}
|
||||
cosign sign --yes ghcr.io/fluxcd/${{ env.CONTROLLER }}@${{ steps.build-push.outputs.digest }}
|
||||
cosign sign fluxcd/${{ env.CONTROLLER }}:${{ steps.prep.outputs.VERSION }}
|
||||
cosign sign ghcr.io/fluxcd/${{ env.CONTROLLER }}:${{ steps.prep.outputs.VERSION }}
|
||||
- name: Generate release artifacts
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
run: |
|
||||
mkdir -p config/release
|
||||
kustomize build ./config/crd > ./config/release/${{ env.CONTROLLER }}.crds.yaml
|
||||
kustomize build ./config/manager > ./config/release/${{ env.CONTROLLER }}.deployment.yaml
|
||||
- uses: anchore/sbom-action/download-syft@cee1b8e05ae5b2593a75e197229729eabaa9f8ec # v0.20.2
|
||||
echo '[CHANGELOG](https://github.com/fluxcd/${{ env.CONTROLLER }}/blob/main/CHANGELOG.md)' > ./config/release/notes.md
|
||||
- uses: anchore/sbom-action/download-syft@v0
|
||||
- name: Create release and SBOM
|
||||
id: run-goreleaser
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0
|
||||
uses: goreleaser/goreleaser-action@v3
|
||||
with:
|
||||
version: latest
|
||||
args: release --clean --skip=validate
|
||||
args: release --release-notes=config/release/notes.md --rm-dist --skip-validate
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Generate SLSA metadata
|
||||
id: slsa
|
||||
env:
|
||||
ARTIFACTS: "${{ steps.run-goreleaser.outputs.artifacts }}"
|
||||
run: |
|
||||
hashes=$(echo -E $ARTIFACTS | jq --raw-output '.[] | {name, "digest": (.extra.Digest // .extra.Checksum)} | select(.digest) | {digest} + {name} | join(" ") | sub("^sha256:";"")' | base64 -w0)
|
||||
echo "hashes=$hashes" >> $GITHUB_OUTPUT
|
||||
|
||||
image_url=fluxcd/${{ env.CONTROLLER }}:${{ steps.prep.outputs.version }}
|
||||
echo "image_url=$image_url" >> $GITHUB_OUTPUT
|
||||
|
||||
image_digest=${{ steps.build-push.outputs.digest }}
|
||||
echo "image_digest=$image_digest" >> $GITHUB_OUTPUT
|
||||
|
||||
release-provenance:
|
||||
needs: [release]
|
||||
permissions:
|
||||
actions: read # for detecting the Github Actions environment.
|
||||
id-token: write # for creating OIDC tokens for signing.
|
||||
contents: write # for uploading attestations to GitHub releases.
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
|
||||
with:
|
||||
provenance-name: "provenance.intoto.jsonl"
|
||||
base64-subjects: "${{ needs.release.outputs.hashes }}"
|
||||
upload-assets: true
|
||||
|
||||
dockerhub-provenance:
|
||||
needs: [release]
|
||||
permissions:
|
||||
actions: read # for detecting the Github Actions environment.
|
||||
id-token: write # for creating OIDC tokens for signing.
|
||||
packages: write # for uploading attestations.
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
|
||||
with:
|
||||
image: ${{ needs.release.outputs.image_url }}
|
||||
digest: ${{ needs.release.outputs.image_digest }}
|
||||
registry-username: fluxcdbot
|
||||
secrets:
|
||||
registry-password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }}
|
||||
|
||||
ghcr-provenance:
|
||||
needs: [release]
|
||||
permissions:
|
||||
actions: read # for detecting the Github Actions environment.
|
||||
id-token: write # for creating OIDC tokens for signing.
|
||||
packages: write # for uploading attestations.
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
|
||||
with:
|
||||
image: ghcr.io/${{ needs.release.outputs.image_url }}
|
||||
digest: ${{ needs.release.outputs.image_digest }}
|
||||
registry-username: fluxcdbot
|
||||
secrets:
|
||||
registry-password: ${{ secrets.GHCR_TOKEN }}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
name: scan
|
||||
on:
|
||||
push:
|
||||
branches: [ "main", "release/**" ]
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ "main", "release/**" ]
|
||||
branches: [ main ]
|
||||
schedule:
|
||||
- cron: '18 10 * * 3'
|
||||
|
||||
|
@ -16,36 +16,47 @@ jobs:
|
|||
name: FOSSA
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run FOSSA scan and upload build data
|
||||
uses: fossa-contrib/fossa-action@3d2ef181b1820d6dcd1972f86a767d18167fa19b # v3.0.1
|
||||
uses: fossa-contrib/fossa-action@v1
|
||||
with:
|
||||
# FOSSA Push-Only API Token
|
||||
fossa-api-key: 5ee8bf422db1471e0bcf2bcb289185de
|
||||
github-token: ${{ github.token }}
|
||||
|
||||
snyk:
|
||||
name: Snyk
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run Snyk to check for vulnerabilities
|
||||
uses: snyk/actions/golang@master
|
||||
continue-on-error: true
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
with:
|
||||
args: --sarif-file-output=snyk.sarif
|
||||
- name: Upload result to GitHub Code Scanning
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
with:
|
||||
sarif_file: snyk.sarif
|
||||
|
||||
codeql:
|
||||
name: CodeQL
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.24.x
|
||||
cache-dependency-path: |
|
||||
**/go.sum
|
||||
**/go.mod
|
||||
go-version: 1.19.x
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: go
|
||||
# xref: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# xref: https://codeql.github.com/codeql-query-help/go/
|
||||
queries: security-and-quality
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
name: sync-labels
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- .github/labels.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
labels:
|
||||
name: Run sync
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: EndBug/label-sync@52074158190acb45f3077f9099fea818aa43f97a # v2.3.3
|
||||
with:
|
||||
# Configuration file
|
||||
config-file: |
|
||||
https://raw.githubusercontent.com/fluxcd/community/main/.github/standard-labels.yaml
|
||||
.github/labels.yaml
|
||||
# Strictly declarative
|
||||
delete-other-labels: true
|
|
@ -4,26 +4,9 @@ builds:
|
|||
- skip: true
|
||||
|
||||
release:
|
||||
prerelease: "true"
|
||||
extra_files:
|
||||
- glob: config/release/*.yaml
|
||||
prerelease: "auto"
|
||||
header: |
|
||||
## Changelog
|
||||
|
||||
[{{.Tag}} changelog](https://github.com/fluxcd/{{.ProjectName}}/blob/{{.Tag}}/CHANGELOG.md)
|
||||
footer: |
|
||||
## Container images
|
||||
|
||||
- `docker.io/fluxcd/{{.ProjectName}}:{{.Tag}}`
|
||||
- `ghcr.io/fluxcd/{{.ProjectName}}:{{.Tag}}`
|
||||
|
||||
Supported architectures: `linux/amd64`, `linux/arm64` and `linux/arm/v7`.
|
||||
|
||||
The container images are built on GitHub hosted runners and are signed with cosign and GitHub OIDC.
|
||||
To verify the images and their provenance (SLSA level 3), please see the [security documentation](https://fluxcd.io/flux/security/).
|
||||
|
||||
changelog:
|
||||
disable: true
|
||||
|
||||
checksum:
|
||||
extra_files:
|
||||
|
@ -49,7 +32,6 @@ signs:
|
|||
certificate: "${artifact}.pem"
|
||||
args:
|
||||
- sign-blob
|
||||
- "--yes"
|
||||
- "--output-certificate=${certificate}"
|
||||
- "--output-signature=${signature}"
|
||||
- "${artifact}"
|
||||
|
|
1011
CHANGELOG.md
1011
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
|
@ -24,7 +24,7 @@ If any of the above dependencies are not present on your system, the first invoc
|
|||
## How to run the test suite
|
||||
|
||||
Prerequisites:
|
||||
* Go >= 1.24
|
||||
* Go >= 1.18
|
||||
|
||||
You can run the test suite by simply doing
|
||||
|
||||
|
|
17
Dockerfile
17
Dockerfile
|
@ -1,10 +1,10 @@
|
|||
ARG GO_VERSION=1.24
|
||||
ARG XX_VERSION=1.6.1
|
||||
ARG GO_VERSION=1.19
|
||||
ARG XX_VERSION=1.1.0
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
|
||||
# Docker buildkit multi-arch build requires golang alpine
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS builder
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine as builder
|
||||
|
||||
# Copy the build utilities.
|
||||
COPY --from=xx / /
|
||||
|
@ -25,19 +25,22 @@ RUN go mod download
|
|||
|
||||
# copy source code
|
||||
COPY main.go main.go
|
||||
COPY controllers/ controllers/
|
||||
COPY internal/ internal/
|
||||
|
||||
# build without specifing the arch
|
||||
ENV CGO_ENABLED=0
|
||||
RUN xx-go build -trimpath -a -o helm-controller main.go
|
||||
|
||||
FROM alpine:3.21
|
||||
FROM alpine:3.16
|
||||
|
||||
RUN apk add --no-cache ca-certificates \
|
||||
&& update-ca-certificates
|
||||
# link repo to the GitHub Container Registry image
|
||||
LABEL org.opencontainers.image.source="https://github.com/fluxcd/helm-controller"
|
||||
|
||||
RUN apk add --no-cache ca-certificates tini
|
||||
|
||||
COPY --from=builder /workspace/helm-controller /usr/local/bin/
|
||||
|
||||
USER 65534:65534
|
||||
|
||||
ENTRYPOINT [ "helm-controller" ]
|
||||
ENTRYPOINT [ "/sbin/tini", "--", "helm-controller" ]
|
||||
|
|
47
Makefile
47
Makefile
|
@ -27,26 +27,11 @@ 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.16.1
|
||||
GEN_API_REF_DOCS_VERSION ?= e327d0730470cbd61b06300f81c5fcf91c23c113
|
||||
|
||||
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 download-crd-deps
|
||||
test: tidy generate fmt vet manifests api-docs install-envtest
|
||||
KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) go test ./... -coverprofile cover.out
|
||||
cd api; go test ./... -coverprofile cover.out
|
||||
|
||||
|
@ -92,12 +77,12 @@ manifests: controller-gen
|
|||
|
||||
# Generate API reference documentation
|
||||
api-docs: gen-crd-api-reference-docs
|
||||
$(GEN_CRD_API_REFERENCE_DOCS) -api-dir=./api/v2 -config=./hack/api-docs/config.json -template-dir=./hack/api-docs/template -out-file=./docs/api/v2/helm.md
|
||||
$(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/helmrelease.md
|
||||
|
||||
# Run go mod tidy
|
||||
tidy:
|
||||
cd api; rm -f go.sum; go mod tidy -compat=1.23
|
||||
rm -f go.sum; go mod tidy -compat=1.23
|
||||
cd api; rm -f go.sum; go mod tidy -compat=1.19
|
||||
rm -f go.sum; go mod tidy -compat=1.19
|
||||
|
||||
# Run go fmt against code
|
||||
fmt:
|
||||
|
@ -124,35 +109,17 @@ 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
|
||||
controller-gen: ## Download controller-gen locally if necessary.
|
||||
$(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_GEN_VERSION))
|
||||
$(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.8.0)
|
||||
|
||||
# Find or download gen-crd-api-reference-docs
|
||||
GEN_CRD_API_REFERENCE_DOCS = $(GOBIN)/gen-crd-api-reference-docs
|
||||
.PHONY: gen-crd-api-reference-docs
|
||||
gen-crd-api-reference-docs: ## Download gen-crd-api-reference-docs locally if necessary
|
||||
$(call go-install-tool,$(GEN_CRD_API_REFERENCE_DOCS),github.com/ahmetb/gen-crd-api-reference-docs@$(GEN_API_REF_DOCS_VERSION))
|
||||
gen-crd-api-reference-docs:
|
||||
$(call go-install-tool,$(GEN_CRD_API_REFERENCE_DOCS),github.com/ahmetb/gen-crd-api-reference-docs@v0.3.0)
|
||||
|
||||
ENVTEST_ASSETS_DIR=$(BUILD_DIR)/testbin
|
||||
ENVTEST_KUBERNETES_VERSION?=latest
|
||||
|
|
7
PROJECT
7
PROJECT
|
@ -4,11 +4,4 @@ resources:
|
|||
- group: helm
|
||||
kind: HelmRelease
|
||||
version: v2beta1
|
||||
- group: helm
|
||||
kind: HelmRelease
|
||||
version: v2beta2
|
||||
- group: helm
|
||||
kind: HelmRelease
|
||||
version: v2
|
||||
storageVersion: v2
|
||||
version: "2"
|
||||
|
|
|
@ -24,7 +24,7 @@ operator.
|
|||
* Supports `HelmChart` artifacts produced from `HelmRepository`,
|
||||
`GitRepository` and `Bucket` sources
|
||||
* Fetches artifacts produced by [source-controller][] from `HelmChart`
|
||||
and `OCIRepository` objects
|
||||
objects
|
||||
* Watches `HelmChart` objects for revision changes (including semver
|
||||
ranges for charts from `HelmRepository` sources)
|
||||
* Performs automated Helm actions, including Helm tests, rollbacks and
|
||||
|
@ -38,18 +38,16 @@ operator.
|
|||
[notification-controller][])
|
||||
* Built-in Kustomize compatible Helm post renderer, providing support
|
||||
for strategic merge, JSON 6902 and images patches
|
||||
* Supports detecting and correcting in-cluster changes compared to the desired
|
||||
state of the Helm release
|
||||
|
||||
## Guides
|
||||
|
||||
* [Get started with Flux](https://fluxcd.io/flux/get-started/)
|
||||
* [Get started with GitOps Toolkit](https://fluxcd.io/flux/get-started/)
|
||||
* [Manage Helm Releases](https://fluxcd.io/flux/guides/helmreleases/)
|
||||
* [Setup Notifications](https://fluxcd.io/flux/guides/notifications/)
|
||||
|
||||
## Specifications
|
||||
|
||||
* [API](docs/spec/v2/README.md)
|
||||
* [API](docs/spec/v2beta1/README.md)
|
||||
* [Controller](docs/spec/README.md)
|
||||
|
||||
[source-controller]: https://github.com/fluxcd/source-controller
|
||||
|
|
39
api/go.mod
39
api/go.mod
|
@ -1,33 +1,32 @@
|
|||
module github.com/fluxcd/helm-controller/api
|
||||
|
||||
go 1.24.0
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/fluxcd/pkg/apis/kustomize v1.11.0
|
||||
github.com/fluxcd/pkg/apis/meta v1.18.0
|
||||
k8s.io/apiextensions-apiserver v0.33.2
|
||||
k8s.io/apimachinery v0.33.2
|
||||
sigs.k8s.io/controller-runtime v0.21.0
|
||||
sigs.k8s.io/yaml v1.5.0
|
||||
github.com/fluxcd/pkg/apis/kustomize v0.7.0
|
||||
github.com/fluxcd/pkg/apis/meta v0.18.0
|
||||
k8s.io/apiextensions-apiserver v0.25.4
|
||||
k8s.io/apimachinery v0.25.4
|
||||
sigs.k8s.io/controller-runtime v0.13.1
|
||||
)
|
||||
|
||||
// Fix CVE-2022-32149
|
||||
replace golang.org/x/text => golang.org/x/text v0.4.0
|
||||
|
||||
require (
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
golang.org/x/net v0.2.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
k8s.io/klog/v2 v2.80.1 // indirect
|
||||
k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
)
|
||||
|
|
129
api/go.sum
129
api/go.sum
|
@ -1,91 +1,81 @@
|
|||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fluxcd/pkg/apis/kustomize v1.11.0 h1:0IzDgxZkc4v+5SDNCvgZhfwfkdkQLPXCner7TNaJFWE=
|
||||
github.com/fluxcd/pkg/apis/kustomize v1.11.0/go.mod h1:j302mJGDww8cn9qvMsRQ0LJ1HPAPs/IlX7CSsoJV7BI=
|
||||
github.com/fluxcd/pkg/apis/meta v1.18.0 h1:ACHrMIjlcioE9GKS7NGk62KX4NshqNewr8sBwMcXABs=
|
||||
github.com/fluxcd/pkg/apis/meta v1.18.0/go.mod h1:97l3hTwBpJbXBY+wetNbqrUsvES8B1jGioKcBUxmqd8=
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
|
||||
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/fluxcd/pkg/apis/kustomize v0.7.0 h1:X2htBmJ91nGYv4d93gin665MFWKNGiNwUiZ08/Zz0hY=
|
||||
github.com/fluxcd/pkg/apis/kustomize v0.7.0/go.mod h1:Mu+KdktsEKWA4l/33CZdY5lB4hz51mqfcLzBZSwAqVg=
|
||||
github.com/fluxcd/pkg/apis/meta v0.18.0 h1:s0LeulWcQ4DxVX6805vgDTxlA6bAYk+Lq1QHSnNdqLM=
|
||||
github.com/fluxcd/pkg/apis/meta v0.18.0/go.mod h1:pYvXRFi1UKNNrGR34jw3uqOnMXw9X6dTkML8j5Z7tis=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
|
||||
github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
|
||||
github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
|
||||
go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -95,27 +85,24 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
|
|||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/api v0.33.2 h1:YgwIS5jKfA+BZg//OQhkJNIfie/kmRsO0BmNaVSimvY=
|
||||
k8s.io/api v0.33.2/go.mod h1:fhrbphQJSM2cXzCWgqU29xLDuks4mu7ti9vveEnpSXs=
|
||||
k8s.io/apiextensions-apiserver v0.33.2 h1:6gnkIbngnaUflR3XwE1mCefN3YS8yTD631JXQhsU6M8=
|
||||
k8s.io/apiextensions-apiserver v0.33.2/go.mod h1:IvVanieYsEHJImTKXGP6XCOjTwv2LUMos0YWc9O+QP8=
|
||||
k8s.io/apimachinery v0.33.2 h1:IHFVhqg59mb8PJWTLi8m1mAoepkUNYmptHsV+Z1m5jY=
|
||||
k8s.io/apimachinery v0.33.2/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e h1:KqK5c/ghOm8xkHYhlodbp6i6+r+ChV2vuAuVRdFbLro=
|
||||
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8=
|
||||
sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
|
||||
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
|
||||
k8s.io/api v0.25.4 h1:3YO8J4RtmG7elEgaWMb4HgmpS2CfY1QlaOz9nwB+ZSs=
|
||||
k8s.io/apiextensions-apiserver v0.25.4 h1:7hu9pF+xikxQuQZ7/30z/qxIPZc2J1lFElPtr7f+B6U=
|
||||
k8s.io/apiextensions-apiserver v0.25.4/go.mod h1:bkSGki5YBoZWdn5pWtNIdGvDrrsRWlmnvl9a+tAw5vQ=
|
||||
k8s.io/apimachinery v0.25.4 h1:CtXsuaitMESSu339tfhVXhQrPET+EiWnIY1rcurKnAc=
|
||||
k8s.io/apimachinery v0.25.4/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo=
|
||||
k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4=
|
||||
k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 h1:GfD9OzL11kvZN5iArC6oTS7RTj7oJOIfnislxYlqTj8=
|
||||
k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/controller-runtime v0.13.1 h1:tUsRCSJVM1QQOOeViGeX3GMT3dQF1eePPw6sEE3xSlg=
|
||||
sigs.k8s.io/controller-runtime v0.13.1/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI=
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k=
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
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=
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 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 v2
|
||||
|
||||
import "github.com/fluxcd/pkg/apis/meta"
|
||||
|
||||
const (
|
||||
// ForceRequestAnnotation is the annotation used for triggering a one-off forced
|
||||
// Helm release, even when there are no new changes in the HelmRelease.
|
||||
// The value is interpreted as a token, and must equal the value of
|
||||
// meta.ReconcileRequestAnnotation in order to trigger a release.
|
||||
ForceRequestAnnotation string = meta.ForceRequestAnnotation
|
||||
|
||||
// ResetRequestAnnotation is the annotation used for resetting the failure counts
|
||||
// of a HelmRelease, so that it can be retried again.
|
||||
// The value is interpreted as a token, and must equal the value of
|
||||
// meta.ReconcileRequestAnnotation in order to reset the failure counts.
|
||||
ResetRequestAnnotation string = "reconcile.fluxcd.io/resetAt"
|
||||
)
|
||||
|
||||
// ShouldHandleResetRequest returns true if the HelmRelease has a reset request
|
||||
// annotation, and the value of the annotation matches the value of the
|
||||
// meta.ReconcileRequestAnnotation annotation.
|
||||
//
|
||||
// To ensure that the reset request is handled only once, the value of
|
||||
// HelmReleaseStatus.LastHandledResetAt is updated to match the value of the
|
||||
// reset request annotation (even if the reset request is not handled because
|
||||
// the value of the meta.ReconcileRequestAnnotation annotation does not match).
|
||||
func ShouldHandleResetRequest(obj *HelmRelease) bool {
|
||||
return meta.HandleAnnotationRequest(obj, ResetRequestAnnotation, &obj.Status.LastHandledResetAt)
|
||||
}
|
||||
|
||||
// ShouldHandleForceRequest returns true if the HelmRelease has a force request
|
||||
// annotation, and the value of the annotation matches the value of the
|
||||
// meta.ReconcileRequestAnnotation annotation.
|
||||
//
|
||||
// To ensure that the force request is handled only once, the value of
|
||||
// HelmReleaseStatus.LastHandledForceAt is updated to match the value of the
|
||||
// force request annotation (even if the force request is not handled because
|
||||
// the value of the meta.ReconcileRequestAnnotation annotation does not match).
|
||||
func ShouldHandleForceRequest(obj *HelmRelease) bool {
|
||||
return meta.ShouldHandleForceRequest(obj)
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 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 v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
)
|
||||
|
||||
func TestShouldHandleResetRequest(t *testing.T) {
|
||||
obj := &HelmRelease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
meta.ReconcileRequestAnnotation: "b",
|
||||
ResetRequestAnnotation: "b",
|
||||
},
|
||||
},
|
||||
Status: HelmReleaseStatus{
|
||||
LastHandledResetAt: "a",
|
||||
ReconcileRequestStatus: meta.ReconcileRequestStatus{
|
||||
LastHandledReconcileAt: "a",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if !ShouldHandleResetRequest(obj) {
|
||||
t.Error("ShouldHandleResetRequest() = false")
|
||||
}
|
||||
|
||||
if obj.Status.LastHandledResetAt != "b" {
|
||||
t.Error("ShouldHandleResetRequest did not update LastHandledResetAt")
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 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 v2
|
||||
|
||||
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"
|
||||
|
||||
// DependencyNotReadyReason represents the fact that
|
||||
// one of the dependencies is not ready.
|
||||
DependencyNotReadyReason string = "DependencyNotReady"
|
||||
)
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 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 v2 contains API Schema definitions for the helm v2 API group
|
||||
// +kubebuilder:object:generate=true
|
||||
// +groupName=helm.toolkit.fluxcd.io
|
||||
package v2
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 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 v2
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/scheme"
|
||||
)
|
||||
|
||||
var (
|
||||
// GroupVersion is group version used to register these objects
|
||||
GroupVersion = schema.GroupVersion{Group: "helm.toolkit.fluxcd.io", Version: "v2"}
|
||||
|
||||
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
|
||||
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
|
||||
|
||||
// AddToScheme adds the types in this group-version to the given scheme.
|
||||
AddToScheme = SchemeBuilder.AddToScheme
|
||||
)
|
File diff suppressed because it is too large
Load Diff
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 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 v2
|
||||
|
||||
// CrossNamespaceObjectReference contains enough information to let you locate
|
||||
// the typed referenced object at cluster level.
|
||||
type CrossNamespaceObjectReference struct {
|
||||
// APIVersion of the referent.
|
||||
// +optional
|
||||
APIVersion string `json:"apiVersion,omitempty"`
|
||||
|
||||
// Kind of the referent.
|
||||
// +kubebuilder:validation:Enum=HelmRepository;GitRepository;Bucket
|
||||
// +required
|
||||
Kind string `json:"kind,omitempty"`
|
||||
|
||||
// Name of the referent.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +required
|
||||
Name string `json:"name"`
|
||||
|
||||
// Namespace of the referent.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=63
|
||||
// +kubebuilder:validation:Optional
|
||||
// +optional
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
// CrossNamespaceSourceReference contains enough information to let you locate
|
||||
// the typed referenced object at cluster level.
|
||||
type CrossNamespaceSourceReference struct {
|
||||
// APIVersion of the referent.
|
||||
// +optional
|
||||
APIVersion string `json:"apiVersion,omitempty"`
|
||||
|
||||
// Kind of the referent.
|
||||
// +kubebuilder:validation:Enum=OCIRepository;HelmChart
|
||||
// +required
|
||||
Kind string `json:"kind"`
|
||||
|
||||
// Name of the referent.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +required
|
||||
Name string `json:"name"`
|
||||
|
||||
// Namespace of the referent, defaults to the namespace of the Kubernetes
|
||||
// resource object that contains the reference.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=63
|
||||
// +kubebuilder:validation:Optional
|
||||
// +optional
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
// DependencyReference defines a HelmRelease dependency on another HelmRelease resource.
|
||||
type DependencyReference struct {
|
||||
// Name of the referent.
|
||||
// +required
|
||||
Name string `json:"name"`
|
||||
|
||||
// Namespace of the referent, defaults to the namespace of the HelmRelease
|
||||
// resource object that contains the reference.
|
||||
// +optional
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
|
||||
// ReadyExpr is a CEL expression that can be used to assess the readiness
|
||||
// of a dependency. When specified, the built-in readiness check
|
||||
// is replaced by the logic defined in the CEL expression.
|
||||
// To make the CEL expression additive to the built-in readiness check,
|
||||
// the feature gate `AdditiveCELDependencyCheck` must be set to `true`.
|
||||
// +optional
|
||||
ReadyExpr string `json:"readyExpr,omitempty"`
|
||||
}
|
|
@ -1,239 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 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 v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
// snapshotStatusDeployed indicates that the release the snapshot was taken
|
||||
// from is currently deployed.
|
||||
snapshotStatusDeployed = "deployed"
|
||||
// snapshotStatusSuperseded indicates that the release the snapshot was taken
|
||||
// from has been superseded by a newer release.
|
||||
snapshotStatusSuperseded = "superseded"
|
||||
|
||||
// snapshotTestPhaseFailed indicates that the test of the release the snapshot
|
||||
// was taken from has failed.
|
||||
snapshotTestPhaseFailed = "Failed"
|
||||
)
|
||||
|
||||
// Snapshots is a list of Snapshot objects.
|
||||
type Snapshots []*Snapshot
|
||||
|
||||
// Len returns the number of Snapshots.
|
||||
func (in Snapshots) Len() int {
|
||||
return len(in)
|
||||
}
|
||||
|
||||
// SortByVersion sorts the Snapshots by version, in descending order.
|
||||
func (in Snapshots) SortByVersion() {
|
||||
sort.Slice(in, func(i, j int) bool {
|
||||
return in[i].Version > in[j].Version
|
||||
})
|
||||
}
|
||||
|
||||
// Latest returns the most recent Snapshot.
|
||||
func (in Snapshots) Latest() *Snapshot {
|
||||
if len(in) == 0 {
|
||||
return nil
|
||||
}
|
||||
in.SortByVersion()
|
||||
return in[0]
|
||||
}
|
||||
|
||||
// Previous returns the most recent Snapshot before the Latest that has a
|
||||
// status of "deployed" or "superseded", or nil if there is no such Snapshot.
|
||||
// Unless ignoreTests is true, Snapshots with a test in the "Failed" phase are
|
||||
// ignored.
|
||||
func (in Snapshots) Previous(ignoreTests bool) *Snapshot {
|
||||
if len(in) < 2 {
|
||||
return nil
|
||||
}
|
||||
in.SortByVersion()
|
||||
for i := range in[1:] {
|
||||
s := in[i+1]
|
||||
if s.Status == snapshotStatusDeployed || s.Status == snapshotStatusSuperseded {
|
||||
if ignoreTests || !s.HasTestInPhase(snapshotTestPhaseFailed) {
|
||||
return s
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Truncate removes all Snapshots up to the Previous deployed Snapshot.
|
||||
// If there is no previous-deployed Snapshot, the most recent 5 Snapshots are
|
||||
// retained.
|
||||
func (in *Snapshots) Truncate(ignoreTests bool) {
|
||||
if in.Len() < 2 {
|
||||
return
|
||||
}
|
||||
|
||||
in.SortByVersion()
|
||||
for i := range (*in)[1:] {
|
||||
s := (*in)[i+1]
|
||||
if s.Status == snapshotStatusDeployed || s.Status == snapshotStatusSuperseded {
|
||||
if ignoreTests || !s.HasTestInPhase(snapshotTestPhaseFailed) {
|
||||
*in = (*in)[:i+2]
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if in.Len() > defaultMaxHistory {
|
||||
// If none of the Snapshots are deployed or superseded, and there
|
||||
// are more than the defaultMaxHistory, truncate to the most recent
|
||||
// Snapshots.
|
||||
*in = (*in)[:defaultMaxHistory]
|
||||
}
|
||||
}
|
||||
|
||||
// Snapshot captures a point-in-time copy of the status information for a Helm release,
|
||||
// as managed by the controller.
|
||||
type Snapshot struct {
|
||||
// APIVersion is the API version of the Snapshot.
|
||||
// Provisional: when the calculation method of the Digest field is changed,
|
||||
// this field will be used to distinguish between the old and new methods.
|
||||
// +optional
|
||||
APIVersion string `json:"apiVersion,omitempty"`
|
||||
// Digest is the checksum of the release object in storage.
|
||||
// It has the format of `<algo>:<checksum>`.
|
||||
// +required
|
||||
Digest string `json:"digest"`
|
||||
// Name is the name of the release.
|
||||
// +required
|
||||
Name string `json:"name"`
|
||||
// Namespace is the namespace the release is deployed to.
|
||||
// +required
|
||||
Namespace string `json:"namespace"`
|
||||
// Version is the version of the release object in storage.
|
||||
// +required
|
||||
Version int `json:"version"`
|
||||
// Status is the current state of the release.
|
||||
// +required
|
||||
Status string `json:"status"`
|
||||
// ChartName is the chart name of the release object in storage.
|
||||
// +required
|
||||
ChartName string `json:"chartName"`
|
||||
// ChartVersion is the chart version of the release object in
|
||||
// storage.
|
||||
// +required
|
||||
ChartVersion string `json:"chartVersion"`
|
||||
// AppVersion is the chart app version of the release object in storage.
|
||||
// +optional
|
||||
AppVersion string `json:"appVersion,omitempty"`
|
||||
// ConfigDigest is the checksum of the config (better known as
|
||||
// "values") of the release object in storage.
|
||||
// It has the format of `<algo>:<checksum>`.
|
||||
// +required
|
||||
ConfigDigest string `json:"configDigest"`
|
||||
// FirstDeployed is when the release was first deployed.
|
||||
// +required
|
||||
FirstDeployed metav1.Time `json:"firstDeployed"`
|
||||
// LastDeployed is when the release was last deployed.
|
||||
// +required
|
||||
LastDeployed metav1.Time `json:"lastDeployed"`
|
||||
// Deleted is when the release was deleted.
|
||||
// +optional
|
||||
Deleted metav1.Time `json:"deleted,omitempty"`
|
||||
// TestHooks is the list of test hooks for the release as observed to be
|
||||
// run by the controller.
|
||||
// +optional
|
||||
TestHooks *map[string]*TestHookStatus `json:"testHooks,omitempty"`
|
||||
// OCIDigest is the digest of the OCI artifact associated with the release.
|
||||
// +optional
|
||||
OCIDigest string `json:"ociDigest,omitempty"`
|
||||
}
|
||||
|
||||
// FullReleaseName returns the full name of the release in the format
|
||||
// of '<namespace>/<name>.<version>
|
||||
func (in *Snapshot) FullReleaseName() string {
|
||||
if in == nil {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s/%s.v%d", in.Namespace, in.Name, in.Version)
|
||||
}
|
||||
|
||||
// VersionedChartName returns the full name of the chart in the format of
|
||||
// '<name>@<version>'.
|
||||
func (in *Snapshot) VersionedChartName() string {
|
||||
if in == nil {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s@%s", in.ChartName, in.ChartVersion)
|
||||
}
|
||||
|
||||
// HasBeenTested returns true if TestHooks is not nil. This includes an empty
|
||||
// map, which indicates the chart has no tests.
|
||||
func (in *Snapshot) HasBeenTested() bool {
|
||||
return in != nil && in.TestHooks != nil
|
||||
}
|
||||
|
||||
// GetTestHooks returns the TestHooks for the release if not nil.
|
||||
func (in *Snapshot) GetTestHooks() map[string]*TestHookStatus {
|
||||
if in == nil || in.TestHooks == nil {
|
||||
return nil
|
||||
}
|
||||
return *in.TestHooks
|
||||
}
|
||||
|
||||
// HasTestInPhase returns true if any of the TestHooks is in the given phase.
|
||||
func (in *Snapshot) HasTestInPhase(phase string) bool {
|
||||
if in != nil {
|
||||
for _, h := range in.GetTestHooks() {
|
||||
if h.Phase == phase {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetTestHooks sets the TestHooks for the release.
|
||||
func (in *Snapshot) SetTestHooks(hooks map[string]*TestHookStatus) {
|
||||
if in == nil || hooks == nil {
|
||||
return
|
||||
}
|
||||
in.TestHooks = &hooks
|
||||
}
|
||||
|
||||
// Targets returns true if the Snapshot targets the given release data.
|
||||
func (in *Snapshot) Targets(name, namespace string, version int) bool {
|
||||
if in != nil {
|
||||
return in.Name == name && in.Namespace == namespace && in.Version == version
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// TestHookStatus holds the status information for a test hook as observed
|
||||
// to be run by the controller.
|
||||
type TestHookStatus struct {
|
||||
// LastStarted is the time the test hook was last started.
|
||||
// +optional
|
||||
LastStarted metav1.Time `json:"lastStarted,omitempty"`
|
||||
// LastCompleted is the time the test hook last completed.
|
||||
// +optional
|
||||
LastCompleted metav1.Time `json:"lastCompleted,omitempty"`
|
||||
// Phase the test hook was observed to be in.
|
||||
// +optional
|
||||
Phase string `json:"phase,omitempty"`
|
||||
}
|
|
@ -1,298 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 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 v2
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSnapshots_Sort(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in Snapshots
|
||||
want Snapshots
|
||||
}{
|
||||
{
|
||||
name: "sorts by descending version",
|
||||
in: Snapshots{
|
||||
{Version: 1},
|
||||
{Version: 3},
|
||||
{Version: 2},
|
||||
},
|
||||
want: Snapshots{
|
||||
{Version: 3},
|
||||
{Version: 2},
|
||||
{Version: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "already sorted",
|
||||
in: Snapshots{
|
||||
{Version: 3},
|
||||
{Version: 2},
|
||||
{Version: 1},
|
||||
},
|
||||
want: Snapshots{
|
||||
{Version: 3},
|
||||
{Version: 2},
|
||||
{Version: 1},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.in.SortByVersion()
|
||||
|
||||
if !reflect.DeepEqual(tt.in, tt.want) {
|
||||
t.Errorf("SortByVersion() got %v, want %v", tt.in, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshots_Latest(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in Snapshots
|
||||
want *Snapshot
|
||||
}{
|
||||
{
|
||||
name: "returns most recent snapshot",
|
||||
in: Snapshots{
|
||||
{Version: 1},
|
||||
{Version: 3},
|
||||
{Version: 2},
|
||||
},
|
||||
want: &Snapshot{Version: 3},
|
||||
},
|
||||
{
|
||||
name: "returns nil if empty",
|
||||
in: Snapshots{},
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.in.Latest(); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Latest() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshots_Previous(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in Snapshots
|
||||
ignoreTests bool
|
||||
want *Snapshot
|
||||
}{
|
||||
{
|
||||
name: "returns previous snapshot",
|
||||
in: Snapshots{
|
||||
{Version: 2, Status: "deployed"},
|
||||
{Version: 3, Status: "failed"},
|
||||
{Version: 1, Status: "superseded"},
|
||||
},
|
||||
want: &Snapshot{Version: 2, Status: "deployed"},
|
||||
},
|
||||
{
|
||||
name: "includes snapshots with failed tests",
|
||||
in: Snapshots{
|
||||
{Version: 4, Status: "deployed"},
|
||||
{Version: 1, Status: "superseded"},
|
||||
{Version: 2, Status: "superseded"},
|
||||
{Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"test": {Phase: "Failed"},
|
||||
}},
|
||||
},
|
||||
ignoreTests: true,
|
||||
want: &Snapshot{Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"test": {Phase: "Failed"},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "ignores snapshots with failed tests",
|
||||
in: Snapshots{
|
||||
{Version: 4, Status: "deployed"},
|
||||
{Version: 1, Status: "superseded"},
|
||||
{Version: 2, Status: "superseded"},
|
||||
{Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"test": {Phase: "Failed"},
|
||||
}},
|
||||
},
|
||||
ignoreTests: false,
|
||||
want: &Snapshot{Version: 2, Status: "superseded"},
|
||||
},
|
||||
{
|
||||
name: "returns nil without previous snapshot",
|
||||
in: Snapshots{
|
||||
{Version: 1, Status: "deployed"},
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "returns nil without snapshot matching criteria",
|
||||
in: Snapshots{
|
||||
{Version: 4, Status: "deployed"},
|
||||
{Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"test": {Phase: "Failed"},
|
||||
}},
|
||||
},
|
||||
ignoreTests: false,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.in.Previous(tt.ignoreTests); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Previous() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshots_Truncate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in Snapshots
|
||||
ignoreTests bool
|
||||
want Snapshots
|
||||
}{
|
||||
{
|
||||
name: "keeps previous snapshot",
|
||||
in: Snapshots{
|
||||
{Version: 1, Status: "superseded"},
|
||||
{Version: 3, Status: "failed"},
|
||||
{Version: 2, Status: "superseded"},
|
||||
{Version: 4, Status: "deployed"},
|
||||
},
|
||||
want: Snapshots{
|
||||
{Version: 4, Status: "deployed"},
|
||||
{Version: 3, Status: "failed"},
|
||||
{Version: 2, Status: "superseded"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ignores snapshots with failed tests",
|
||||
in: Snapshots{
|
||||
{Version: 4, Status: "deployed"},
|
||||
{Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"upgrade-test-fail-podinfo-fault-test-tiz9x": {Phase: "Failed"},
|
||||
"upgrade-test-fail-podinfo-grpc-test-gddcw": {},
|
||||
}},
|
||||
{Version: 2, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"upgrade-test-fail-podinfo-grpc-test-h0tc2": {
|
||||
Phase: "Succeeded",
|
||||
},
|
||||
"upgrade-test-fail-podinfo-jwt-test-vzusa": {
|
||||
Phase: "Succeeded",
|
||||
},
|
||||
"upgrade-test-fail-podinfo-service-test-b647e": {
|
||||
Phase: "Succeeded",
|
||||
},
|
||||
}},
|
||||
},
|
||||
ignoreTests: false,
|
||||
want: Snapshots{
|
||||
{Version: 4, Status: "deployed"},
|
||||
{Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"upgrade-test-fail-podinfo-fault-test-tiz9x": {Phase: "Failed"},
|
||||
"upgrade-test-fail-podinfo-grpc-test-gddcw": {},
|
||||
}},
|
||||
{Version: 2, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"upgrade-test-fail-podinfo-grpc-test-h0tc2": {
|
||||
Phase: "Succeeded",
|
||||
},
|
||||
"upgrade-test-fail-podinfo-jwt-test-vzusa": {
|
||||
Phase: "Succeeded",
|
||||
},
|
||||
"upgrade-test-fail-podinfo-service-test-b647e": {
|
||||
Phase: "Succeeded",
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "keeps previous snapshot with failed tests",
|
||||
in: Snapshots{
|
||||
{Version: 4, Status: "deployed"},
|
||||
{Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"upgrade-test-fail-podinfo-fault-test-tiz9x": {Phase: "Failed"},
|
||||
"upgrade-test-fail-podinfo-grpc-test-gddcw": {},
|
||||
}},
|
||||
{Version: 2, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"upgrade-test-fail-podinfo-grpc-test-h0tc2": {
|
||||
Phase: "Succeeded",
|
||||
},
|
||||
"upgrade-test-fail-podinfo-jwt-test-vzusa": {
|
||||
Phase: "Succeeded",
|
||||
},
|
||||
"upgrade-test-fail-podinfo-service-test-b647e": {
|
||||
Phase: "Succeeded",
|
||||
},
|
||||
}},
|
||||
{Version: 1, Status: "superseded"},
|
||||
},
|
||||
ignoreTests: true,
|
||||
want: Snapshots{
|
||||
{Version: 4, Status: "deployed"},
|
||||
{Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"upgrade-test-fail-podinfo-fault-test-tiz9x": {Phase: "Failed"},
|
||||
"upgrade-test-fail-podinfo-grpc-test-gddcw": {},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "retains most recent snapshots when all have failed",
|
||||
in: Snapshots{
|
||||
{Version: 6, Status: "deployed"},
|
||||
{Version: 5, Status: "failed"},
|
||||
{Version: 4, Status: "failed"},
|
||||
{Version: 3, Status: "failed"},
|
||||
{Version: 2, Status: "failed"},
|
||||
{Version: 1, Status: "failed"},
|
||||
},
|
||||
want: Snapshots{
|
||||
{Version: 6, Status: "deployed"},
|
||||
{Version: 5, Status: "failed"},
|
||||
{Version: 4, Status: "failed"},
|
||||
{Version: 3, Status: "failed"},
|
||||
{Version: 2, Status: "failed"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "without previous snapshot",
|
||||
in: Snapshots{
|
||||
{Version: 1, Status: "deployed"},
|
||||
},
|
||||
want: Snapshots{
|
||||
{Version: 1, Status: "deployed"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.in.Truncate(tt.ignoreTests)
|
||||
|
||||
if !reflect.DeepEqual(tt.in, tt.want) {
|
||||
t.Errorf("Truncate() got %v, want %v", tt.in, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,774 +0,0 @@
|
|||
//go:build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 2024 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.
|
||||
*/
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/fluxcd/pkg/apis/kustomize"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CommonMetadata) DeepCopyInto(out *CommonMetadata) {
|
||||
*out = *in
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.Labels != nil {
|
||||
in, out := &in.Labels, &out.Labels
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonMetadata.
|
||||
func (in *CommonMetadata) DeepCopy() *CommonMetadata {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CommonMetadata)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CrossNamespaceObjectReference) DeepCopyInto(out *CrossNamespaceObjectReference) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CrossNamespaceObjectReference.
|
||||
func (in *CrossNamespaceObjectReference) DeepCopy() *CrossNamespaceObjectReference {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CrossNamespaceObjectReference)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CrossNamespaceSourceReference) DeepCopyInto(out *CrossNamespaceSourceReference) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CrossNamespaceSourceReference.
|
||||
func (in *CrossNamespaceSourceReference) DeepCopy() *CrossNamespaceSourceReference {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CrossNamespaceSourceReference)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DependencyReference) DeepCopyInto(out *DependencyReference) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DependencyReference.
|
||||
func (in *DependencyReference) DeepCopy() *DependencyReference {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DependencyReference)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DriftDetection) DeepCopyInto(out *DriftDetection) {
|
||||
*out = *in
|
||||
if in.Ignore != nil {
|
||||
in, out := &in.Ignore, &out.Ignore
|
||||
*out = make([]IgnoreRule, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DriftDetection.
|
||||
func (in *DriftDetection) DeepCopy() *DriftDetection {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DriftDetection)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Filter) DeepCopyInto(out *Filter) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Filter.
|
||||
func (in *Filter) DeepCopy() *Filter {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Filter)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmChartTemplate) DeepCopyInto(out *HelmChartTemplate) {
|
||||
*out = *in
|
||||
if in.ObjectMeta != nil {
|
||||
in, out := &in.ObjectMeta, &out.ObjectMeta
|
||||
*out = new(HelmChartTemplateObjectMeta)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartTemplate.
|
||||
func (in *HelmChartTemplate) DeepCopy() *HelmChartTemplate {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmChartTemplate)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmChartTemplateObjectMeta) DeepCopyInto(out *HelmChartTemplateObjectMeta) {
|
||||
*out = *in
|
||||
if in.Labels != nil {
|
||||
in, out := &in.Labels, &out.Labels
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartTemplateObjectMeta.
|
||||
func (in *HelmChartTemplateObjectMeta) DeepCopy() *HelmChartTemplateObjectMeta {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmChartTemplateObjectMeta)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmChartTemplateSpec) DeepCopyInto(out *HelmChartTemplateSpec) {
|
||||
*out = *in
|
||||
out.SourceRef = in.SourceRef
|
||||
if in.Interval != nil {
|
||||
in, out := &in.Interval, &out.Interval
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.ValuesFiles != nil {
|
||||
in, out := &in.ValuesFiles, &out.ValuesFiles
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Verify != nil {
|
||||
in, out := &in.Verify, &out.Verify
|
||||
*out = new(HelmChartTemplateVerification)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartTemplateSpec.
|
||||
func (in *HelmChartTemplateSpec) DeepCopy() *HelmChartTemplateSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmChartTemplateSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmChartTemplateVerification) DeepCopyInto(out *HelmChartTemplateVerification) {
|
||||
*out = *in
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(meta.LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartTemplateVerification.
|
||||
func (in *HelmChartTemplateVerification) DeepCopy() *HelmChartTemplateVerification {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmChartTemplateVerification)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmRelease) DeepCopyInto(out *HelmRelease) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmRelease.
|
||||
func (in *HelmRelease) DeepCopy() *HelmRelease {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmRelease)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *HelmRelease) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmReleaseList) DeepCopyInto(out *HelmReleaseList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]HelmRelease, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmReleaseList.
|
||||
func (in *HelmReleaseList) DeepCopy() *HelmReleaseList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmReleaseList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *HelmReleaseList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmReleaseSpec) DeepCopyInto(out *HelmReleaseSpec) {
|
||||
*out = *in
|
||||
if in.Chart != nil {
|
||||
in, out := &in.Chart, &out.Chart
|
||||
*out = new(HelmChartTemplate)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ChartRef != nil {
|
||||
in, out := &in.ChartRef, &out.ChartRef
|
||||
*out = new(CrossNamespaceSourceReference)
|
||||
**out = **in
|
||||
}
|
||||
out.Interval = in.Interval
|
||||
if in.KubeConfig != nil {
|
||||
in, out := &in.KubeConfig, &out.KubeConfig
|
||||
*out = new(meta.KubeConfigReference)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.DependsOn != nil {
|
||||
in, out := &in.DependsOn, &out.DependsOn
|
||||
*out = make([]DependencyReference, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Timeout != nil {
|
||||
in, out := &in.Timeout, &out.Timeout
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.MaxHistory != nil {
|
||||
in, out := &in.MaxHistory, &out.MaxHistory
|
||||
*out = new(int)
|
||||
**out = **in
|
||||
}
|
||||
if in.PersistentClient != nil {
|
||||
in, out := &in.PersistentClient, &out.PersistentClient
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.DriftDetection != nil {
|
||||
in, out := &in.DriftDetection, &out.DriftDetection
|
||||
*out = new(DriftDetection)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Install != nil {
|
||||
in, out := &in.Install, &out.Install
|
||||
*out = new(Install)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Upgrade != nil {
|
||||
in, out := &in.Upgrade, &out.Upgrade
|
||||
*out = new(Upgrade)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Test != nil {
|
||||
in, out := &in.Test, &out.Test
|
||||
*out = new(Test)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Rollback != nil {
|
||||
in, out := &in.Rollback, &out.Rollback
|
||||
*out = new(Rollback)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Uninstall != nil {
|
||||
in, out := &in.Uninstall, &out.Uninstall
|
||||
*out = new(Uninstall)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ValuesFrom != nil {
|
||||
in, out := &in.ValuesFrom, &out.ValuesFrom
|
||||
*out = make([]meta.ValuesReference, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Values != nil {
|
||||
in, out := &in.Values, &out.Values
|
||||
*out = new(apiextensionsv1.JSON)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.CommonMetadata != nil {
|
||||
in, out := &in.CommonMetadata, &out.CommonMetadata
|
||||
*out = new(CommonMetadata)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.PostRenderers != nil {
|
||||
in, out := &in.PostRenderers, &out.PostRenderers
|
||||
*out = make([]PostRenderer, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmReleaseSpec.
|
||||
func (in *HelmReleaseSpec) DeepCopy() *HelmReleaseSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmReleaseSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmReleaseStatus) DeepCopyInto(out *HelmReleaseStatus) {
|
||||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]v1.Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.History != nil {
|
||||
in, out := &in.History, &out.History
|
||||
*out = make(Snapshots, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Snapshot)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
if in.LastAttemptedReleaseActionDuration != nil {
|
||||
in, out := &in.LastAttemptedReleaseActionDuration, &out.LastAttemptedReleaseActionDuration
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
out.ReconcileRequestStatus = in.ReconcileRequestStatus
|
||||
out.ForceRequestStatus = in.ForceRequestStatus
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmReleaseStatus.
|
||||
func (in *HelmReleaseStatus) DeepCopy() *HelmReleaseStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmReleaseStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *IgnoreRule) DeepCopyInto(out *IgnoreRule) {
|
||||
*out = *in
|
||||
if in.Paths != nil {
|
||||
in, out := &in.Paths, &out.Paths
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Target != nil {
|
||||
in, out := &in.Target, &out.Target
|
||||
*out = new(kustomize.Selector)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IgnoreRule.
|
||||
func (in *IgnoreRule) DeepCopy() *IgnoreRule {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(IgnoreRule)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Install) DeepCopyInto(out *Install) {
|
||||
*out = *in
|
||||
if in.Timeout != nil {
|
||||
in, out := &in.Timeout, &out.Timeout
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.Remediation != nil {
|
||||
in, out := &in.Remediation, &out.Remediation
|
||||
*out = new(InstallRemediation)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Install.
|
||||
func (in *Install) DeepCopy() *Install {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Install)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *InstallRemediation) DeepCopyInto(out *InstallRemediation) {
|
||||
*out = *in
|
||||
if in.IgnoreTestFailures != nil {
|
||||
in, out := &in.IgnoreTestFailures, &out.IgnoreTestFailures
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.RemediateLastFailure != nil {
|
||||
in, out := &in.RemediateLastFailure, &out.RemediateLastFailure
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstallRemediation.
|
||||
func (in *InstallRemediation) DeepCopy() *InstallRemediation {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(InstallRemediation)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Kustomize) DeepCopyInto(out *Kustomize) {
|
||||
*out = *in
|
||||
if in.Patches != nil {
|
||||
in, out := &in.Patches, &out.Patches
|
||||
*out = make([]kustomize.Patch, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Images != nil {
|
||||
in, out := &in.Images, &out.Images
|
||||
*out = make([]kustomize.Image, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Kustomize.
|
||||
func (in *Kustomize) DeepCopy() *Kustomize {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Kustomize)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PostRenderer) DeepCopyInto(out *PostRenderer) {
|
||||
*out = *in
|
||||
if in.Kustomize != nil {
|
||||
in, out := &in.Kustomize, &out.Kustomize
|
||||
*out = new(Kustomize)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostRenderer.
|
||||
func (in *PostRenderer) DeepCopy() *PostRenderer {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PostRenderer)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Rollback) DeepCopyInto(out *Rollback) {
|
||||
*out = *in
|
||||
if in.Timeout != nil {
|
||||
in, out := &in.Timeout, &out.Timeout
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rollback.
|
||||
func (in *Rollback) DeepCopy() *Rollback {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Rollback)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Snapshot) DeepCopyInto(out *Snapshot) {
|
||||
*out = *in
|
||||
in.FirstDeployed.DeepCopyInto(&out.FirstDeployed)
|
||||
in.LastDeployed.DeepCopyInto(&out.LastDeployed)
|
||||
in.Deleted.DeepCopyInto(&out.Deleted)
|
||||
if in.TestHooks != nil {
|
||||
in, out := &in.TestHooks, &out.TestHooks
|
||||
*out = new(map[string]*TestHookStatus)
|
||||
if **in != nil {
|
||||
in, out := *in, *out
|
||||
*out = make(map[string]*TestHookStatus, len(*in))
|
||||
for key, val := range *in {
|
||||
var outVal *TestHookStatus
|
||||
if val == nil {
|
||||
(*out)[key] = nil
|
||||
} else {
|
||||
inVal := (*in)[key]
|
||||
in, out := &inVal, &outVal
|
||||
*out = new(TestHookStatus)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
(*out)[key] = outVal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Snapshot.
|
||||
func (in *Snapshot) DeepCopy() *Snapshot {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Snapshot)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in Snapshots) DeepCopyInto(out *Snapshots) {
|
||||
{
|
||||
in := &in
|
||||
*out = make(Snapshots, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Snapshot)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Snapshots.
|
||||
func (in Snapshots) DeepCopy() Snapshots {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Snapshots)
|
||||
in.DeepCopyInto(out)
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Test) DeepCopyInto(out *Test) {
|
||||
*out = *in
|
||||
if in.Timeout != nil {
|
||||
in, out := &in.Timeout, &out.Timeout
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.Filters != nil {
|
||||
in, out := &in.Filters, &out.Filters
|
||||
*out = new([]Filter)
|
||||
if **in != nil {
|
||||
in, out := *in, *out
|
||||
*out = make([]Filter, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Test.
|
||||
func (in *Test) DeepCopy() *Test {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Test)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TestHookStatus) DeepCopyInto(out *TestHookStatus) {
|
||||
*out = *in
|
||||
in.LastStarted.DeepCopyInto(&out.LastStarted)
|
||||
in.LastCompleted.DeepCopyInto(&out.LastCompleted)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TestHookStatus.
|
||||
func (in *TestHookStatus) DeepCopy() *TestHookStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TestHookStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Uninstall) DeepCopyInto(out *Uninstall) {
|
||||
*out = *in
|
||||
if in.Timeout != nil {
|
||||
in, out := &in.Timeout, &out.Timeout
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.DeletionPropagation != nil {
|
||||
in, out := &in.DeletionPropagation, &out.DeletionPropagation
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Uninstall.
|
||||
func (in *Uninstall) DeepCopy() *Uninstall {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Uninstall)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Upgrade) DeepCopyInto(out *Upgrade) {
|
||||
*out = *in
|
||||
if in.Timeout != nil {
|
||||
in, out := &in.Timeout, &out.Timeout
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.Remediation != nil {
|
||||
in, out := &in.Remediation, &out.Remediation
|
||||
*out = new(UpgradeRemediation)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Upgrade.
|
||||
func (in *Upgrade) DeepCopy() *Upgrade {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Upgrade)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *UpgradeRemediation) DeepCopyInto(out *UpgradeRemediation) {
|
||||
*out = *in
|
||||
if in.IgnoreTestFailures != nil {
|
||||
in, out := &in.IgnoreTestFailures, &out.IgnoreTestFailures
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.RemediateLastFailure != nil {
|
||||
in, out := &in.RemediateLastFailure, &out.RemediateLastFailure
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.Strategy != nil {
|
||||
in, out := &in.Strategy, &out.Strategy
|
||||
*out = new(RemediationStrategy)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpgradeRemediation.
|
||||
func (in *UpgradeRemediation) DeepCopy() *UpgradeRemediation {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(UpgradeRemediation)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
|
@ -15,9 +15,6 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
// Package v2beta1 contains API Schema definitions for the helm v2beta1 API group
|
||||
//
|
||||
// Deprecated: v2beta1 is no longer supported, use v2 instead.
|
||||
//
|
||||
// +kubebuilder:object:generate=true
|
||||
// +groupName=helm.toolkit.fluxcd.io
|
||||
package v2beta1
|
||||
|
|
|
@ -28,9 +28,6 @@ import (
|
|||
|
||||
"github.com/fluxcd/pkg/apis/kustomize"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
|
||||
v2 "github.com/fluxcd/helm-controller/api/v2"
|
||||
"github.com/fluxcd/helm-controller/api/v2beta2"
|
||||
)
|
||||
|
||||
const HelmReleaseKind = "HelmRelease"
|
||||
|
@ -70,19 +67,9 @@ type HelmReleaseSpec struct {
|
|||
// Chart defines the template of the v1beta2.HelmChart that should be created
|
||||
// for this HelmRelease.
|
||||
// +required
|
||||
Chart *HelmChartTemplate `json:"chart,omitempty"`
|
||||
|
||||
// ChartRef holds a reference to a source controller resource containing the
|
||||
// Helm chart artifact.
|
||||
//
|
||||
// Note: this field is provisional to the v2 API, and not actively used
|
||||
// by v2beta1 HelmReleases.
|
||||
// +optional
|
||||
ChartRef *v2.CrossNamespaceSourceReference `json:"chartRef,omitempty"`
|
||||
Chart HelmChartTemplate `json:"chart"`
|
||||
|
||||
// Interval at which to reconcile the Helm release.
|
||||
// This interval is approximate and may be subject to jitter to ensure
|
||||
// efficient use of resources.
|
||||
// +kubebuilder:validation:Type=string
|
||||
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$"
|
||||
// +required
|
||||
|
@ -96,7 +83,7 @@ type HelmReleaseSpec struct {
|
|||
// a controller level fallback for when HelmReleaseSpec.ServiceAccountName
|
||||
// is empty.
|
||||
// +optional
|
||||
KubeConfig *meta.KubeConfigReference `json:"kubeConfig,omitempty"`
|
||||
KubeConfig *KubeConfig `json:"kubeConfig,omitempty"`
|
||||
|
||||
// Suspend tells the controller to suspend reconciliation for this HelmRelease,
|
||||
// it does not apply to already started reconciliations. Defaults to false.
|
||||
|
@ -150,30 +137,6 @@ type HelmReleaseSpec struct {
|
|||
// +optional
|
||||
ServiceAccountName string `json:"serviceAccountName,omitempty"`
|
||||
|
||||
// PersistentClient tells the controller to use a persistent Kubernetes
|
||||
// client for this release. When enabled, the client will be reused for the
|
||||
// duration of the reconciliation, instead of being created and destroyed
|
||||
// for each (step of a) Helm action.
|
||||
//
|
||||
// This can improve performance, but may cause issues with some Helm charts
|
||||
// that for example do create Custom Resource Definitions during installation
|
||||
// outside Helm's CRD lifecycle hooks, which are then not observed to be
|
||||
// available by e.g. post-install hooks.
|
||||
//
|
||||
// If not set, it defaults to true.
|
||||
//
|
||||
// +optional
|
||||
PersistentClient *bool `json:"persistentClient,omitempty"`
|
||||
|
||||
// DriftDetection holds the configuration for detecting and handling
|
||||
// differences between the manifest in the Helm storage and the resources
|
||||
// currently existing in the cluster.
|
||||
//
|
||||
// Note: this field is provisional to the v2beta2 API, and not actively used
|
||||
// by v2beta1 HelmReleases.
|
||||
// +optional
|
||||
DriftDetection *v2beta2.DriftDetection `json:"driftDetection,omitempty"`
|
||||
|
||||
// Install holds the configuration for Helm install actions for this HelmRelease.
|
||||
// +optional
|
||||
Install *Install `json:"install,omitempty"`
|
||||
|
@ -252,36 +215,30 @@ func (in HelmReleaseSpec) GetUninstall() Uninstall {
|
|||
return *in.Uninstall
|
||||
}
|
||||
|
||||
// KubeConfig references a Kubernetes secret that contains a kubeconfig file.
|
||||
type KubeConfig struct {
|
||||
// SecretRef holds the name to a secret that contains a key with
|
||||
// the kubeconfig file as the value. If no key is specified the key will
|
||||
// default to 'value'. The secret must be in the same namespace as
|
||||
// the HelmRelease.
|
||||
// It is recommended that the kubeconfig is self-contained, and the secret
|
||||
// is regularly updated if credentials such as a cloud-access-token expire.
|
||||
// Cloud specific `cmd-path` auth helpers will not function without adding
|
||||
// binaries and credentials to the Pod that is responsible for reconciling
|
||||
// the HelmRelease.
|
||||
// +required
|
||||
SecretRef meta.SecretKeyReference `json:"secretRef,omitempty"`
|
||||
}
|
||||
|
||||
// HelmChartTemplate defines the template from which the controller will
|
||||
// generate a v1beta2.HelmChart object in the same namespace as the referenced
|
||||
// v1beta2.Source.
|
||||
type HelmChartTemplate struct {
|
||||
// ObjectMeta holds the template for metadata like labels and annotations.
|
||||
// +optional
|
||||
ObjectMeta *HelmChartTemplateObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Spec holds the template for the v1beta2.HelmChartSpec for this HelmRelease.
|
||||
// +required
|
||||
Spec HelmChartTemplateSpec `json:"spec"`
|
||||
}
|
||||
|
||||
// HelmChartTemplateObjectMeta defines the template for the ObjectMeta of a
|
||||
// v1beta2.HelmChart.
|
||||
type HelmChartTemplateObjectMeta struct {
|
||||
// Map of string keys and values that can be used to organize and categorize
|
||||
// (scope and select) objects.
|
||||
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
|
||||
// +optional
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
// Annotations is an unstructured key value map stored with a resource that may be
|
||||
// set by external tools to store and retrieve arbitrary metadata. They are not
|
||||
// queryable and should be preserved when modifying objects.
|
||||
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
|
||||
// +optional
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
// HelmChartTemplateSpec defines the template from which the controller will
|
||||
// generate a v1beta2.HelmChartSpec object.
|
||||
type HelmChartTemplateSpec struct {
|
||||
|
@ -850,13 +807,6 @@ type Uninstall struct {
|
|||
// a Helm uninstall is performed.
|
||||
// +optional
|
||||
DisableWait bool `json:"disableWait,omitempty"`
|
||||
|
||||
// DeletionPropagation specifies the deletion propagation policy when
|
||||
// a Helm uninstall is performed.
|
||||
// +kubebuilder:default=background
|
||||
// +kubebuilder:validation:Enum=background;foreground;orphan
|
||||
// +optional
|
||||
DeletionPropagation *string `json:"deletionPropagation,omitempty"`
|
||||
}
|
||||
|
||||
// GetTimeout returns the configured timeout for the Helm uninstall action, or
|
||||
|
@ -868,26 +818,12 @@ func (in Uninstall) GetTimeout(defaultTimeout metav1.Duration) metav1.Duration {
|
|||
return *in.Timeout
|
||||
}
|
||||
|
||||
// GetDeletionPropagation returns the configured deletion propagation policy
|
||||
// for the Helm uninstall action, or 'background'.
|
||||
func (in Uninstall) GetDeletionPropagation() string {
|
||||
if in.DeletionPropagation == nil {
|
||||
return "background"
|
||||
}
|
||||
return *in.DeletionPropagation
|
||||
}
|
||||
|
||||
// HelmReleaseStatus defines the observed state of a HelmRelease.
|
||||
type HelmReleaseStatus struct {
|
||||
// ObservedGeneration is the last observed generation.
|
||||
// +optional
|
||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
||||
|
||||
// ObservedPostRenderersDigest is the digest for the post-renderers of
|
||||
// the last successful reconciliation attempt.
|
||||
// +optional
|
||||
ObservedPostRenderersDigest string `json:"observedPostRenderersDigest,omitempty"`
|
||||
|
||||
meta.ReconcileRequestStatus `json:",inline"`
|
||||
|
||||
// Conditions holds the conditions for the HelmRelease.
|
||||
|
@ -930,62 +866,6 @@ 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 v2.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"`
|
||||
|
||||
// LastHandledForceAt holds the value of the most recent force request
|
||||
// value, so a change of the annotation value can be detected.
|
||||
//
|
||||
// Note: this field is provisional to the v2beta2 API, and not actively used
|
||||
// by v2beta1 HelmReleases.
|
||||
// +optional
|
||||
LastHandledForceAt string `json:"lastHandledForceAt,omitempty"`
|
||||
|
||||
// LastHandledResetAt holds the value of the most recent reset request
|
||||
// value, so a change of the annotation value can be detected.
|
||||
//
|
||||
// Note: this field is provisional to the v2beta2 API, and not actively used
|
||||
// by v2beta1 HelmReleases.
|
||||
// +optional
|
||||
LastHandledResetAt string `json:"lastHandledResetAt,omitempty"`
|
||||
}
|
||||
|
||||
// GetHelmChart returns the namespace and name of the HelmChart.
|
||||
|
@ -1044,8 +924,6 @@ func HelmReleaseReady(hr HelmRelease) HelmRelease {
|
|||
|
||||
// HelmReleaseAttempted registers an attempt of the given HelmRelease with the given state.
|
||||
// and returns the modified HelmRelease and a boolean indicating a state change.
|
||||
//
|
||||
// Deprecated: in favor of HelmReleaseChanged and HelmReleaseRecordAttempt.
|
||||
func HelmReleaseAttempted(hr HelmRelease, revision string, releaseRevision int, valuesChecksum string) (HelmRelease, bool) {
|
||||
changed := hr.Status.LastAttemptedRevision != revision ||
|
||||
hr.Status.LastReleaseRevision != releaseRevision ||
|
||||
|
@ -1057,31 +935,6 @@ func HelmReleaseAttempted(hr HelmRelease, revision string, releaseRevision int,
|
|||
return hr, changed
|
||||
}
|
||||
|
||||
// HelmReleaseChanged returns if the HelmRelease has changed compared to the
|
||||
// provided values.
|
||||
func HelmReleaseChanged(hr HelmRelease, revision string, releaseRevision int, valuesChecksums ...string) bool {
|
||||
return hr.Status.LastAttemptedRevision != revision ||
|
||||
hr.Status.LastReleaseRevision != releaseRevision ||
|
||||
!inStringSlice(hr.Status.LastAttemptedValuesChecksum, valuesChecksums)
|
||||
}
|
||||
|
||||
// HelmReleaseRecordAttempt returns an attempt of the given HelmRelease with the
|
||||
// given state in the Status of the provided object.
|
||||
func HelmReleaseRecordAttempt(hr *HelmRelease, revision string, releaseRevision int, valuesChecksum string) {
|
||||
hr.Status.LastAttemptedRevision = revision
|
||||
hr.Status.LastReleaseRevision = releaseRevision
|
||||
hr.Status.LastAttemptedValuesChecksum = valuesChecksum
|
||||
}
|
||||
|
||||
func inStringSlice(str string, s []string) bool {
|
||||
for _, v := range s {
|
||||
if str == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func resetFailureCounts(hr *HelmRelease) {
|
||||
hr.Status.Failures = 0
|
||||
hr.Status.InstallFailures = 0
|
||||
|
@ -1095,9 +948,13 @@ const (
|
|||
)
|
||||
|
||||
// +genclient
|
||||
// +genclient:Namespaced
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:resource:shortName=hr
|
||||
// +kubebuilder:skipversion
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
|
||||
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
|
||||
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""
|
||||
|
||||
// HelmRelease is the Schema for the helmreleases API
|
||||
type HelmRelease struct {
|
||||
|
@ -1176,15 +1033,6 @@ func (in HelmRelease) GetMaxHistory() int {
|
|||
return *in.Spec.MaxHistory
|
||||
}
|
||||
|
||||
// UsePersistentClient returns the configured PersistentClient, or the default
|
||||
// of true.
|
||||
func (in HelmRelease) UsePersistentClient() bool {
|
||||
if in.Spec.PersistentClient == nil {
|
||||
return true
|
||||
}
|
||||
return *in.Spec.PersistentClient
|
||||
}
|
||||
|
||||
// GetDependsOn returns the list of dependencies across-namespaces.
|
||||
func (in HelmRelease) GetDependsOn() []meta.NamespacedObjectReference {
|
||||
return in.Spec.DependsOn
|
||||
|
@ -1201,7 +1049,6 @@ func (in *HelmRelease) SetConditions(conditions []metav1.Condition) {
|
|||
}
|
||||
|
||||
// GetStatusConditions returns a pointer to the Status.Conditions slice.
|
||||
//
|
||||
// Deprecated: use GetConditions instead.
|
||||
func (in *HelmRelease) GetStatusConditions() *[]metav1.Condition {
|
||||
return &in.Status.Conditions
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 2024 The Flux authors
|
||||
Copyright 2021 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.
|
||||
|
@ -21,8 +22,6 @@ limitations under the License.
|
|||
package v2beta1
|
||||
|
||||
import (
|
||||
"github.com/fluxcd/helm-controller/api/v2"
|
||||
"github.com/fluxcd/helm-controller/api/v2beta2"
|
||||
"github.com/fluxcd/pkg/apis/kustomize"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
|
@ -48,11 +47,6 @@ func (in *CrossNamespaceObjectReference) DeepCopy() *CrossNamespaceObjectReferen
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmChartTemplate) DeepCopyInto(out *HelmChartTemplate) {
|
||||
*out = *in
|
||||
if in.ObjectMeta != nil {
|
||||
in, out := &in.ObjectMeta, &out.ObjectMeta
|
||||
*out = new(HelmChartTemplateObjectMeta)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
}
|
||||
|
||||
|
@ -66,35 +60,6 @@ func (in *HelmChartTemplate) DeepCopy() *HelmChartTemplate {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmChartTemplateObjectMeta) DeepCopyInto(out *HelmChartTemplateObjectMeta) {
|
||||
*out = *in
|
||||
if in.Labels != nil {
|
||||
in, out := &in.Labels, &out.Labels
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartTemplateObjectMeta.
|
||||
func (in *HelmChartTemplateObjectMeta) DeepCopy() *HelmChartTemplateObjectMeta {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmChartTemplateObjectMeta)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmChartTemplateSpec) DeepCopyInto(out *HelmChartTemplateSpec) {
|
||||
*out = *in
|
||||
|
@ -208,21 +173,12 @@ func (in *HelmReleaseList) DeepCopyObject() runtime.Object {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmReleaseSpec) DeepCopyInto(out *HelmReleaseSpec) {
|
||||
*out = *in
|
||||
if in.Chart != nil {
|
||||
in, out := &in.Chart, &out.Chart
|
||||
*out = new(HelmChartTemplate)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ChartRef != nil {
|
||||
in, out := &in.ChartRef, &out.ChartRef
|
||||
*out = new(v2.CrossNamespaceSourceReference)
|
||||
**out = **in
|
||||
}
|
||||
in.Chart.DeepCopyInto(&out.Chart)
|
||||
out.Interval = in.Interval
|
||||
if in.KubeConfig != nil {
|
||||
in, out := &in.KubeConfig, &out.KubeConfig
|
||||
*out = new(meta.KubeConfigReference)
|
||||
(*in).DeepCopyInto(*out)
|
||||
*out = new(KubeConfig)
|
||||
**out = **in
|
||||
}
|
||||
if in.DependsOn != nil {
|
||||
in, out := &in.DependsOn, &out.DependsOn
|
||||
|
@ -239,16 +195,6 @@ func (in *HelmReleaseSpec) DeepCopyInto(out *HelmReleaseSpec) {
|
|||
*out = new(int)
|
||||
**out = **in
|
||||
}
|
||||
if in.PersistentClient != nil {
|
||||
in, out := &in.PersistentClient, &out.PersistentClient
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.DriftDetection != nil {
|
||||
in, out := &in.DriftDetection, &out.DriftDetection
|
||||
*out = new(v2beta2.DriftDetection)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Install != nil {
|
||||
in, out := &in.Install, &out.Install
|
||||
*out = new(Install)
|
||||
|
@ -314,17 +260,6 @@ func (in *HelmReleaseStatus) DeepCopyInto(out *HelmReleaseStatus) {
|
|||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.History != nil {
|
||||
in, out := &in.History, &out.History
|
||||
*out = make(v2.Snapshots, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(v2.Snapshot)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmReleaseStatus.
|
||||
|
@ -387,15 +322,29 @@ func (in *InstallRemediation) DeepCopy() *InstallRemediation {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *KubeConfig) DeepCopyInto(out *KubeConfig) {
|
||||
*out = *in
|
||||
out.SecretRef = in.SecretRef
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeConfig.
|
||||
func (in *KubeConfig) DeepCopy() *KubeConfig {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(KubeConfig)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Kustomize) DeepCopyInto(out *Kustomize) {
|
||||
*out = *in
|
||||
if in.Patches != nil {
|
||||
in, out := &in.Patches, &out.Patches
|
||||
*out = make([]kustomize.Patch, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.PatchesStrategicMerge != nil {
|
||||
in, out := &in.PatchesStrategicMerge, &out.PatchesStrategicMerge
|
||||
|
@ -496,11 +445,6 @@ func (in *Uninstall) DeepCopyInto(out *Uninstall) {
|
|||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.DeletionPropagation != nil {
|
||||
in, out := &in.DeletionPropagation, &out.DeletionPropagation
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Uninstall.
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
Copyright 2023 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
|
||||
|
||||
import "github.com/fluxcd/pkg/apis/meta"
|
||||
|
||||
const (
|
||||
// ForceRequestAnnotation is the annotation used for triggering a one-off forced
|
||||
// Helm release, even when there are no new changes in the HelmRelease.
|
||||
// The value is interpreted as a token, and must equal the value of
|
||||
// meta.ReconcileRequestAnnotation in order to trigger a release.
|
||||
ForceRequestAnnotation string = "reconcile.fluxcd.io/forceAt"
|
||||
|
||||
// ResetRequestAnnotation is the annotation used for resetting the failure counts
|
||||
// of a HelmRelease, so that it can be retried again.
|
||||
// The value is interpreted as a token, and must equal the value of
|
||||
// meta.ReconcileRequestAnnotation in order to reset the failure counts.
|
||||
ResetRequestAnnotation string = "reconcile.fluxcd.io/resetAt"
|
||||
)
|
||||
|
||||
// ShouldHandleResetRequest returns true if the HelmRelease has a reset request
|
||||
// annotation, and the value of the annotation matches the value of the
|
||||
// meta.ReconcileRequestAnnotation annotation.
|
||||
//
|
||||
// To ensure that the reset request is handled only once, the value of
|
||||
// HelmReleaseStatus.LastHandledResetAt is updated to match the value of the
|
||||
// reset request annotation (even if the reset request is not handled because
|
||||
// the value of the meta.ReconcileRequestAnnotation annotation does not match).
|
||||
func ShouldHandleResetRequest(obj *HelmRelease) bool {
|
||||
return handleRequest(obj, ResetRequestAnnotation, &obj.Status.LastHandledResetAt)
|
||||
}
|
||||
|
||||
// ShouldHandleForceRequest returns true if the HelmRelease has a force request
|
||||
// annotation, and the value of the annotation matches the value of the
|
||||
// meta.ReconcileRequestAnnotation annotation.
|
||||
//
|
||||
// To ensure that the force request is handled only once, the value of
|
||||
// HelmReleaseStatus.LastHandledForceAt is updated to match the value of the
|
||||
// force request annotation (even if the force request is not handled because
|
||||
// the value of the meta.ReconcileRequestAnnotation annotation does not match).
|
||||
func ShouldHandleForceRequest(obj *HelmRelease) bool {
|
||||
return handleRequest(obj, ForceRequestAnnotation, &obj.Status.LastHandledForceAt)
|
||||
}
|
||||
|
||||
// handleRequest returns true if the HelmRelease has a request annotation, and
|
||||
// the value of the annotation matches the value of the meta.ReconcileRequestAnnotation
|
||||
// annotation.
|
||||
//
|
||||
// The lastHandled argument is used to ensure that the request is handled only
|
||||
// once, and is updated to match the value of the request annotation (even if
|
||||
// the request is not handled because the value of the meta.ReconcileRequestAnnotation
|
||||
// annotation does not match).
|
||||
func handleRequest(obj *HelmRelease, annotation string, lastHandled *string) bool {
|
||||
requestAt, requestOk := obj.GetAnnotations()[annotation]
|
||||
reconcileAt, reconcileOk := meta.ReconcileAnnotationValue(obj.GetAnnotations())
|
||||
|
||||
var lastHandledRequest string
|
||||
if requestOk {
|
||||
lastHandledRequest = *lastHandled
|
||||
*lastHandled = requestAt
|
||||
}
|
||||
|
||||
if requestOk && reconcileOk && requestAt == reconcileAt {
|
||||
lastHandledReconcile := obj.Status.GetLastHandledReconcileRequest()
|
||||
if lastHandledReconcile != reconcileAt && lastHandledRequest != requestAt {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,165 +0,0 @@
|
|||
/*
|
||||
Copyright 2023 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
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
)
|
||||
|
||||
func TestShouldHandleResetRequest(t *testing.T) {
|
||||
t.Run("should handle reset request", func(t *testing.T) {
|
||||
obj := &HelmRelease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
meta.ReconcileRequestAnnotation: "b",
|
||||
ResetRequestAnnotation: "b",
|
||||
},
|
||||
},
|
||||
Status: HelmReleaseStatus{
|
||||
LastHandledResetAt: "a",
|
||||
ReconcileRequestStatus: meta.ReconcileRequestStatus{
|
||||
LastHandledReconcileAt: "a",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if !ShouldHandleResetRequest(obj) {
|
||||
t.Error("ShouldHandleResetRequest() = false")
|
||||
}
|
||||
|
||||
if obj.Status.LastHandledResetAt != "b" {
|
||||
t.Error("ShouldHandleResetRequest did not update LastHandledResetAt")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestShouldHandleForceRequest(t *testing.T) {
|
||||
t.Run("should handle force request", func(t *testing.T) {
|
||||
obj := &HelmRelease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
meta.ReconcileRequestAnnotation: "b",
|
||||
ForceRequestAnnotation: "b",
|
||||
},
|
||||
},
|
||||
Status: HelmReleaseStatus{
|
||||
LastHandledForceAt: "a",
|
||||
ReconcileRequestStatus: meta.ReconcileRequestStatus{
|
||||
LastHandledReconcileAt: "a",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if !ShouldHandleForceRequest(obj) {
|
||||
t.Error("ShouldHandleForceRequest() = false")
|
||||
}
|
||||
|
||||
if obj.Status.LastHandledForceAt != "b" {
|
||||
t.Error("ShouldHandleForceRequest did not update LastHandledForceAt")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_handleRequest(t *testing.T) {
|
||||
const requestAnnotation = "requestAnnotation"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
annotations map[string]string
|
||||
lastHandledReconcile string
|
||||
lastHandledRequest string
|
||||
want bool
|
||||
expectLastHandledRequest string
|
||||
}{
|
||||
{
|
||||
name: "valid request and reconcile annotations",
|
||||
annotations: map[string]string{
|
||||
meta.ReconcileRequestAnnotation: "b",
|
||||
requestAnnotation: "b",
|
||||
},
|
||||
want: true,
|
||||
expectLastHandledRequest: "b",
|
||||
},
|
||||
{
|
||||
name: "mismatched annotations",
|
||||
annotations: map[string]string{
|
||||
meta.ReconcileRequestAnnotation: "b",
|
||||
requestAnnotation: "c",
|
||||
},
|
||||
want: false,
|
||||
expectLastHandledRequest: "c",
|
||||
},
|
||||
{
|
||||
name: "reconcile matches previous request",
|
||||
annotations: map[string]string{
|
||||
meta.ReconcileRequestAnnotation: "b",
|
||||
requestAnnotation: "b",
|
||||
},
|
||||
lastHandledReconcile: "a",
|
||||
lastHandledRequest: "b",
|
||||
want: false,
|
||||
expectLastHandledRequest: "b",
|
||||
},
|
||||
{
|
||||
name: "request matches previous reconcile",
|
||||
annotations: map[string]string{
|
||||
meta.ReconcileRequestAnnotation: "b",
|
||||
requestAnnotation: "b",
|
||||
},
|
||||
lastHandledReconcile: "b",
|
||||
lastHandledRequest: "a",
|
||||
want: false,
|
||||
expectLastHandledRequest: "b",
|
||||
},
|
||||
{
|
||||
name: "missing annotations",
|
||||
annotations: map[string]string{},
|
||||
lastHandledRequest: "a",
|
||||
want: false,
|
||||
expectLastHandledRequest: "a",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
obj := &HelmRelease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: tt.annotations,
|
||||
},
|
||||
Status: HelmReleaseStatus{
|
||||
ReconcileRequestStatus: meta.ReconcileRequestStatus{
|
||||
LastHandledReconcileAt: tt.lastHandledReconcile,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
lastHandled := tt.lastHandledRequest
|
||||
result := handleRequest(obj, requestAnnotation, &lastHandled)
|
||||
|
||||
if result != tt.want {
|
||||
t.Errorf("handleRequest() = %v, want %v", result, tt.want)
|
||||
}
|
||||
if lastHandled != tt.expectLastHandledRequest {
|
||||
t.Errorf("lastHandledRequest = %v, want %v", lastHandled, tt.expectLastHandledRequest)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
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"
|
||||
)
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
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
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
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
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/scheme"
|
||||
)
|
||||
|
||||
var (
|
||||
// GroupVersion is group version used to register these objects
|
||||
GroupVersion = schema.GroupVersion{Group: "helm.toolkit.fluxcd.io", Version: "v2beta2"}
|
||||
|
||||
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
|
||||
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
|
||||
|
||||
// AddToScheme adds the types in this group-version to the given scheme.
|
||||
AddToScheme = SchemeBuilder.AddToScheme
|
||||
)
|
File diff suppressed because it is too large
Load Diff
|
@ -1,115 +0,0 @@
|
|||
/*
|
||||
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
|
||||
|
||||
// CrossNamespaceObjectReference contains enough information to let you locate
|
||||
// the typed referenced object at cluster level.
|
||||
type CrossNamespaceObjectReference struct {
|
||||
// APIVersion of the referent.
|
||||
// +optional
|
||||
APIVersion string `json:"apiVersion,omitempty"`
|
||||
|
||||
// Kind of the referent.
|
||||
// +kubebuilder:validation:Enum=HelmRepository;GitRepository;Bucket
|
||||
// +required
|
||||
Kind string `json:"kind,omitempty"`
|
||||
|
||||
// Name of the referent.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +required
|
||||
Name string `json:"name"`
|
||||
|
||||
// Namespace of the referent.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=63
|
||||
// +kubebuilder:validation:Optional
|
||||
// +optional
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
// CrossNamespaceSourceReference contains enough information to let you locate
|
||||
// the typed referenced object at cluster level.
|
||||
type CrossNamespaceSourceReference struct {
|
||||
// APIVersion of the referent.
|
||||
// +optional
|
||||
APIVersion string `json:"apiVersion,omitempty"`
|
||||
|
||||
// Kind of the referent.
|
||||
// +kubebuilder:validation:Enum=OCIRepository;HelmChart
|
||||
// +required
|
||||
Kind string `json:"kind"`
|
||||
|
||||
// Name of the referent.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +required
|
||||
Name string `json:"name"`
|
||||
|
||||
// Namespace of the referent, defaults to the namespace of the Kubernetes
|
||||
// resource object that contains the reference.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=63
|
||||
// +kubebuilder:validation:Optional
|
||||
// +optional
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
// ValuesReference contains a reference to a resource containing Helm values,
|
||||
// and optionally the key they can be found at.
|
||||
type ValuesReference struct {
|
||||
// Kind of the values referent, valid values are ('Secret', 'ConfigMap').
|
||||
// +kubebuilder:validation:Enum=Secret;ConfigMap
|
||||
// +required
|
||||
Kind string `json:"kind"`
|
||||
|
||||
// Name of the values referent. Should reside in the same namespace as the
|
||||
// referring resource.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +required
|
||||
Name string `json:"name"`
|
||||
|
||||
// ValuesKey is the data key where the values.yaml or a specific value can be
|
||||
// found at. Defaults to 'values.yaml'.
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +kubebuilder:validation:Pattern=`^[\-._a-zA-Z0-9]+$`
|
||||
// +optional
|
||||
ValuesKey string `json:"valuesKey,omitempty"`
|
||||
|
||||
// TargetPath is the YAML dot notation path the value should be merged at. When
|
||||
// set, the ValuesKey is expected to be a single flat value. Defaults to 'None',
|
||||
// which results in the values getting merged at the root.
|
||||
// +kubebuilder:validation:MaxLength=250
|
||||
// +kubebuilder:validation:Pattern=`^([a-zA-Z0-9_\-.\\\/]|\[[0-9]{1,5}\])+$`
|
||||
// +optional
|
||||
TargetPath string `json:"targetPath,omitempty"`
|
||||
|
||||
// Optional marks this ValuesReference as optional. When set, a not found error
|
||||
// for the values reference is ignored, but any ValuesKey, TargetPath or
|
||||
// transient error will still result in a reconciliation failure.
|
||||
// +optional
|
||||
Optional bool `json:"optional,omitempty"`
|
||||
}
|
||||
|
||||
// GetValuesKey returns the defined ValuesKey, or the default ('values.yaml').
|
||||
func (in ValuesReference) GetValuesKey() string {
|
||||
if in.ValuesKey == "" {
|
||||
return "values.yaml"
|
||||
}
|
||||
return in.ValuesKey
|
||||
}
|
|
@ -1,236 +0,0 @@
|
|||
/*
|
||||
Copyright 2023 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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
// snapshotStatusDeployed indicates that the release the snapshot was taken
|
||||
// from is currently deployed.
|
||||
snapshotStatusDeployed = "deployed"
|
||||
// snapshotStatusSuperseded indicates that the release the snapshot was taken
|
||||
// from has been superseded by a newer release.
|
||||
snapshotStatusSuperseded = "superseded"
|
||||
|
||||
// snapshotTestPhaseFailed indicates that the test of the release the snapshot
|
||||
// was taken from has failed.
|
||||
snapshotTestPhaseFailed = "Failed"
|
||||
)
|
||||
|
||||
// Snapshots is a list of Snapshot objects.
|
||||
type Snapshots []*Snapshot
|
||||
|
||||
// Len returns the number of Snapshots.
|
||||
func (in Snapshots) Len() int {
|
||||
return len(in)
|
||||
}
|
||||
|
||||
// SortByVersion sorts the Snapshots by version, in descending order.
|
||||
func (in Snapshots) SortByVersion() {
|
||||
sort.Slice(in, func(i, j int) bool {
|
||||
return in[i].Version > in[j].Version
|
||||
})
|
||||
}
|
||||
|
||||
// Latest returns the most recent Snapshot.
|
||||
func (in Snapshots) Latest() *Snapshot {
|
||||
if len(in) == 0 {
|
||||
return nil
|
||||
}
|
||||
in.SortByVersion()
|
||||
return in[0]
|
||||
}
|
||||
|
||||
// Previous returns the most recent Snapshot before the Latest that has a
|
||||
// status of "deployed" or "superseded", or nil if there is no such Snapshot.
|
||||
// Unless ignoreTests is true, Snapshots with a test in the "Failed" phase are
|
||||
// ignored.
|
||||
func (in Snapshots) Previous(ignoreTests bool) *Snapshot {
|
||||
if len(in) < 2 {
|
||||
return nil
|
||||
}
|
||||
in.SortByVersion()
|
||||
for i := range in[1:] {
|
||||
s := in[i+1]
|
||||
if s.Status == snapshotStatusDeployed || s.Status == snapshotStatusSuperseded {
|
||||
if ignoreTests || !s.HasTestInPhase(snapshotTestPhaseFailed) {
|
||||
return s
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Truncate removes all Snapshots up to the Previous deployed Snapshot.
|
||||
// If there is no previous-deployed Snapshot, the most recent 5 Snapshots are
|
||||
// retained.
|
||||
func (in *Snapshots) Truncate(ignoreTests bool) {
|
||||
if in.Len() < 2 {
|
||||
return
|
||||
}
|
||||
|
||||
in.SortByVersion()
|
||||
for i := range (*in)[1:] {
|
||||
s := (*in)[i+1]
|
||||
if s.Status == snapshotStatusDeployed || s.Status == snapshotStatusSuperseded {
|
||||
if ignoreTests || !s.HasTestInPhase(snapshotTestPhaseFailed) {
|
||||
*in = (*in)[:i+2]
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if in.Len() > defaultMaxHistory {
|
||||
// If none of the Snapshots are deployed or superseded, and there
|
||||
// are more than the defaultMaxHistory, truncate to the most recent
|
||||
// Snapshots.
|
||||
*in = (*in)[:defaultMaxHistory]
|
||||
}
|
||||
}
|
||||
|
||||
// Snapshot captures a point-in-time copy of the status information for a Helm release,
|
||||
// as managed by the controller.
|
||||
type Snapshot struct {
|
||||
// APIVersion is the API version of the Snapshot.
|
||||
// Provisional: when the calculation method of the Digest field is changed,
|
||||
// this field will be used to distinguish between the old and new methods.
|
||||
// +optional
|
||||
APIVersion string `json:"apiVersion,omitempty"`
|
||||
// Digest is the checksum of the release object in storage.
|
||||
// It has the format of `<algo>:<checksum>`.
|
||||
// +required
|
||||
Digest string `json:"digest"`
|
||||
// Name is the name of the release.
|
||||
// +required
|
||||
Name string `json:"name"`
|
||||
// Namespace is the namespace the release is deployed to.
|
||||
// +required
|
||||
Namespace string `json:"namespace"`
|
||||
// Version is the version of the release object in storage.
|
||||
// +required
|
||||
Version int `json:"version"`
|
||||
// Status is the current state of the release.
|
||||
// +required
|
||||
Status string `json:"status"`
|
||||
// ChartName is the chart name of the release object in storage.
|
||||
// +required
|
||||
ChartName string `json:"chartName"`
|
||||
// ChartVersion is the chart version of the release object in
|
||||
// storage.
|
||||
// +required
|
||||
ChartVersion string `json:"chartVersion"`
|
||||
// ConfigDigest is the checksum of the config (better known as
|
||||
// "values") of the release object in storage.
|
||||
// It has the format of `<algo>:<checksum>`.
|
||||
// +required
|
||||
ConfigDigest string `json:"configDigest"`
|
||||
// FirstDeployed is when the release was first deployed.
|
||||
// +required
|
||||
FirstDeployed metav1.Time `json:"firstDeployed"`
|
||||
// LastDeployed is when the release was last deployed.
|
||||
// +required
|
||||
LastDeployed metav1.Time `json:"lastDeployed"`
|
||||
// Deleted is when the release was deleted.
|
||||
// +optional
|
||||
Deleted metav1.Time `json:"deleted,omitempty"`
|
||||
// TestHooks is the list of test hooks for the release as observed to be
|
||||
// run by the controller.
|
||||
// +optional
|
||||
TestHooks *map[string]*TestHookStatus `json:"testHooks,omitempty"`
|
||||
// OCIDigest is the digest of the OCI artifact associated with the release.
|
||||
// +optional
|
||||
OCIDigest string `json:"ociDigest,omitempty"`
|
||||
}
|
||||
|
||||
// FullReleaseName returns the full name of the release in the format
|
||||
// of '<namespace>/<name>.<version>
|
||||
func (in *Snapshot) FullReleaseName() string {
|
||||
if in == nil {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s/%s.v%d", in.Namespace, in.Name, in.Version)
|
||||
}
|
||||
|
||||
// VersionedChartName returns the full name of the chart in the format of
|
||||
// '<name>@<version>'.
|
||||
func (in *Snapshot) VersionedChartName() string {
|
||||
if in == nil {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s@%s", in.ChartName, in.ChartVersion)
|
||||
}
|
||||
|
||||
// HasBeenTested returns true if TestHooks is not nil. This includes an empty
|
||||
// map, which indicates the chart has no tests.
|
||||
func (in *Snapshot) HasBeenTested() bool {
|
||||
return in != nil && in.TestHooks != nil
|
||||
}
|
||||
|
||||
// GetTestHooks returns the TestHooks for the release if not nil.
|
||||
func (in *Snapshot) GetTestHooks() map[string]*TestHookStatus {
|
||||
if in == nil || in.TestHooks == nil {
|
||||
return nil
|
||||
}
|
||||
return *in.TestHooks
|
||||
}
|
||||
|
||||
// HasTestInPhase returns true if any of the TestHooks is in the given phase.
|
||||
func (in *Snapshot) HasTestInPhase(phase string) bool {
|
||||
if in != nil {
|
||||
for _, h := range in.GetTestHooks() {
|
||||
if h.Phase == phase {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetTestHooks sets the TestHooks for the release.
|
||||
func (in *Snapshot) SetTestHooks(hooks map[string]*TestHookStatus) {
|
||||
if in == nil || hooks == nil {
|
||||
return
|
||||
}
|
||||
in.TestHooks = &hooks
|
||||
}
|
||||
|
||||
// Targets returns true if the Snapshot targets the given release data.
|
||||
func (in *Snapshot) Targets(name, namespace string, version int) bool {
|
||||
if in != nil {
|
||||
return in.Name == name && in.Namespace == namespace && in.Version == version
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// TestHookStatus holds the status information for a test hook as observed
|
||||
// to be run by the controller.
|
||||
type TestHookStatus struct {
|
||||
// LastStarted is the time the test hook was last started.
|
||||
// +optional
|
||||
LastStarted metav1.Time `json:"lastStarted,omitempty"`
|
||||
// LastCompleted is the time the test hook last completed.
|
||||
// +optional
|
||||
LastCompleted metav1.Time `json:"lastCompleted,omitempty"`
|
||||
// Phase the test hook was observed to be in.
|
||||
// +optional
|
||||
Phase string `json:"phase,omitempty"`
|
||||
}
|
|
@ -1,298 +0,0 @@
|
|||
/*
|
||||
Copyright 2023 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
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSnapshots_Sort(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in Snapshots
|
||||
want Snapshots
|
||||
}{
|
||||
{
|
||||
name: "sorts by descending version",
|
||||
in: Snapshots{
|
||||
{Version: 1},
|
||||
{Version: 3},
|
||||
{Version: 2},
|
||||
},
|
||||
want: Snapshots{
|
||||
{Version: 3},
|
||||
{Version: 2},
|
||||
{Version: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "already sorted",
|
||||
in: Snapshots{
|
||||
{Version: 3},
|
||||
{Version: 2},
|
||||
{Version: 1},
|
||||
},
|
||||
want: Snapshots{
|
||||
{Version: 3},
|
||||
{Version: 2},
|
||||
{Version: 1},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.in.SortByVersion()
|
||||
|
||||
if !reflect.DeepEqual(tt.in, tt.want) {
|
||||
t.Errorf("SortByVersion() got %v, want %v", tt.in, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshots_Latest(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in Snapshots
|
||||
want *Snapshot
|
||||
}{
|
||||
{
|
||||
name: "returns most recent snapshot",
|
||||
in: Snapshots{
|
||||
{Version: 1},
|
||||
{Version: 3},
|
||||
{Version: 2},
|
||||
},
|
||||
want: &Snapshot{Version: 3},
|
||||
},
|
||||
{
|
||||
name: "returns nil if empty",
|
||||
in: Snapshots{},
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.in.Latest(); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Latest() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshots_Previous(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in Snapshots
|
||||
ignoreTests bool
|
||||
want *Snapshot
|
||||
}{
|
||||
{
|
||||
name: "returns previous snapshot",
|
||||
in: Snapshots{
|
||||
{Version: 2, Status: "deployed"},
|
||||
{Version: 3, Status: "failed"},
|
||||
{Version: 1, Status: "superseded"},
|
||||
},
|
||||
want: &Snapshot{Version: 2, Status: "deployed"},
|
||||
},
|
||||
{
|
||||
name: "includes snapshots with failed tests",
|
||||
in: Snapshots{
|
||||
{Version: 4, Status: "deployed"},
|
||||
{Version: 1, Status: "superseded"},
|
||||
{Version: 2, Status: "superseded"},
|
||||
{Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"test": {Phase: "Failed"},
|
||||
}},
|
||||
},
|
||||
ignoreTests: true,
|
||||
want: &Snapshot{Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"test": {Phase: "Failed"},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "ignores snapshots with failed tests",
|
||||
in: Snapshots{
|
||||
{Version: 4, Status: "deployed"},
|
||||
{Version: 1, Status: "superseded"},
|
||||
{Version: 2, Status: "superseded"},
|
||||
{Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"test": {Phase: "Failed"},
|
||||
}},
|
||||
},
|
||||
ignoreTests: false,
|
||||
want: &Snapshot{Version: 2, Status: "superseded"},
|
||||
},
|
||||
{
|
||||
name: "returns nil without previous snapshot",
|
||||
in: Snapshots{
|
||||
{Version: 1, Status: "deployed"},
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "returns nil without snapshot matching criteria",
|
||||
in: Snapshots{
|
||||
{Version: 4, Status: "deployed"},
|
||||
{Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"test": {Phase: "Failed"},
|
||||
}},
|
||||
},
|
||||
ignoreTests: false,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.in.Previous(tt.ignoreTests); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Previous() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshots_Truncate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in Snapshots
|
||||
ignoreTests bool
|
||||
want Snapshots
|
||||
}{
|
||||
{
|
||||
name: "keeps previous snapshot",
|
||||
in: Snapshots{
|
||||
{Version: 1, Status: "superseded"},
|
||||
{Version: 3, Status: "failed"},
|
||||
{Version: 2, Status: "superseded"},
|
||||
{Version: 4, Status: "deployed"},
|
||||
},
|
||||
want: Snapshots{
|
||||
{Version: 4, Status: "deployed"},
|
||||
{Version: 3, Status: "failed"},
|
||||
{Version: 2, Status: "superseded"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ignores snapshots with failed tests",
|
||||
in: Snapshots{
|
||||
{Version: 4, Status: "deployed"},
|
||||
{Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"upgrade-test-fail-podinfo-fault-test-tiz9x": {Phase: "Failed"},
|
||||
"upgrade-test-fail-podinfo-grpc-test-gddcw": {},
|
||||
}},
|
||||
{Version: 2, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"upgrade-test-fail-podinfo-grpc-test-h0tc2": {
|
||||
Phase: "Succeeded",
|
||||
},
|
||||
"upgrade-test-fail-podinfo-jwt-test-vzusa": {
|
||||
Phase: "Succeeded",
|
||||
},
|
||||
"upgrade-test-fail-podinfo-service-test-b647e": {
|
||||
Phase: "Succeeded",
|
||||
},
|
||||
}},
|
||||
},
|
||||
ignoreTests: false,
|
||||
want: Snapshots{
|
||||
{Version: 4, Status: "deployed"},
|
||||
{Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"upgrade-test-fail-podinfo-fault-test-tiz9x": {Phase: "Failed"},
|
||||
"upgrade-test-fail-podinfo-grpc-test-gddcw": {},
|
||||
}},
|
||||
{Version: 2, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"upgrade-test-fail-podinfo-grpc-test-h0tc2": {
|
||||
Phase: "Succeeded",
|
||||
},
|
||||
"upgrade-test-fail-podinfo-jwt-test-vzusa": {
|
||||
Phase: "Succeeded",
|
||||
},
|
||||
"upgrade-test-fail-podinfo-service-test-b647e": {
|
||||
Phase: "Succeeded",
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "keeps previous snapshot with failed tests",
|
||||
in: Snapshots{
|
||||
{Version: 4, Status: "deployed"},
|
||||
{Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"upgrade-test-fail-podinfo-fault-test-tiz9x": {Phase: "Failed"},
|
||||
"upgrade-test-fail-podinfo-grpc-test-gddcw": {},
|
||||
}},
|
||||
{Version: 2, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"upgrade-test-fail-podinfo-grpc-test-h0tc2": {
|
||||
Phase: "Succeeded",
|
||||
},
|
||||
"upgrade-test-fail-podinfo-jwt-test-vzusa": {
|
||||
Phase: "Succeeded",
|
||||
},
|
||||
"upgrade-test-fail-podinfo-service-test-b647e": {
|
||||
Phase: "Succeeded",
|
||||
},
|
||||
}},
|
||||
{Version: 1, Status: "superseded"},
|
||||
},
|
||||
ignoreTests: true,
|
||||
want: Snapshots{
|
||||
{Version: 4, Status: "deployed"},
|
||||
{Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{
|
||||
"upgrade-test-fail-podinfo-fault-test-tiz9x": {Phase: "Failed"},
|
||||
"upgrade-test-fail-podinfo-grpc-test-gddcw": {},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "retains most recent snapshots when all have failed",
|
||||
in: Snapshots{
|
||||
{Version: 6, Status: "deployed"},
|
||||
{Version: 5, Status: "failed"},
|
||||
{Version: 4, Status: "failed"},
|
||||
{Version: 3, Status: "failed"},
|
||||
{Version: 2, Status: "failed"},
|
||||
{Version: 1, Status: "failed"},
|
||||
},
|
||||
want: Snapshots{
|
||||
{Version: 6, Status: "deployed"},
|
||||
{Version: 5, Status: "failed"},
|
||||
{Version: 4, Status: "failed"},
|
||||
{Version: 3, Status: "failed"},
|
||||
{Version: 2, Status: "failed"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "without previous snapshot",
|
||||
in: Snapshots{
|
||||
{Version: 1, Status: "deployed"},
|
||||
},
|
||||
want: Snapshots{
|
||||
{Version: 1, Status: "deployed"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.in.Truncate(tt.ignoreTests)
|
||||
|
||||
if !reflect.DeepEqual(tt.in, tt.want) {
|
||||
t.Errorf("Truncate() got %v, want %v", tt.in, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,749 +0,0 @@
|
|||
//go:build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 2024 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.
|
||||
*/
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
package v2beta2
|
||||
|
||||
import (
|
||||
"github.com/fluxcd/helm-controller/api/v2"
|
||||
"github.com/fluxcd/pkg/apis/kustomize"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CrossNamespaceObjectReference) DeepCopyInto(out *CrossNamespaceObjectReference) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CrossNamespaceObjectReference.
|
||||
func (in *CrossNamespaceObjectReference) DeepCopy() *CrossNamespaceObjectReference {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CrossNamespaceObjectReference)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CrossNamespaceSourceReference) DeepCopyInto(out *CrossNamespaceSourceReference) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CrossNamespaceSourceReference.
|
||||
func (in *CrossNamespaceSourceReference) DeepCopy() *CrossNamespaceSourceReference {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CrossNamespaceSourceReference)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DriftDetection) DeepCopyInto(out *DriftDetection) {
|
||||
*out = *in
|
||||
if in.Ignore != nil {
|
||||
in, out := &in.Ignore, &out.Ignore
|
||||
*out = make([]IgnoreRule, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DriftDetection.
|
||||
func (in *DriftDetection) DeepCopy() *DriftDetection {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DriftDetection)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Filter) DeepCopyInto(out *Filter) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Filter.
|
||||
func (in *Filter) DeepCopy() *Filter {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Filter)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmChartTemplate) DeepCopyInto(out *HelmChartTemplate) {
|
||||
*out = *in
|
||||
if in.ObjectMeta != nil {
|
||||
in, out := &in.ObjectMeta, &out.ObjectMeta
|
||||
*out = new(HelmChartTemplateObjectMeta)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartTemplate.
|
||||
func (in *HelmChartTemplate) DeepCopy() *HelmChartTemplate {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmChartTemplate)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmChartTemplateObjectMeta) DeepCopyInto(out *HelmChartTemplateObjectMeta) {
|
||||
*out = *in
|
||||
if in.Labels != nil {
|
||||
in, out := &in.Labels, &out.Labels
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartTemplateObjectMeta.
|
||||
func (in *HelmChartTemplateObjectMeta) DeepCopy() *HelmChartTemplateObjectMeta {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmChartTemplateObjectMeta)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmChartTemplateSpec) DeepCopyInto(out *HelmChartTemplateSpec) {
|
||||
*out = *in
|
||||
out.SourceRef = in.SourceRef
|
||||
if in.Interval != nil {
|
||||
in, out := &in.Interval, &out.Interval
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.ValuesFiles != nil {
|
||||
in, out := &in.ValuesFiles, &out.ValuesFiles
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Verify != nil {
|
||||
in, out := &in.Verify, &out.Verify
|
||||
*out = new(HelmChartTemplateVerification)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartTemplateSpec.
|
||||
func (in *HelmChartTemplateSpec) DeepCopy() *HelmChartTemplateSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmChartTemplateSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmChartTemplateVerification) DeepCopyInto(out *HelmChartTemplateVerification) {
|
||||
*out = *in
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(meta.LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartTemplateVerification.
|
||||
func (in *HelmChartTemplateVerification) DeepCopy() *HelmChartTemplateVerification {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmChartTemplateVerification)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmRelease) DeepCopyInto(out *HelmRelease) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmRelease.
|
||||
func (in *HelmRelease) DeepCopy() *HelmRelease {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmRelease)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *HelmRelease) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmReleaseList) DeepCopyInto(out *HelmReleaseList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]HelmRelease, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmReleaseList.
|
||||
func (in *HelmReleaseList) DeepCopy() *HelmReleaseList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmReleaseList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *HelmReleaseList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmReleaseSpec) DeepCopyInto(out *HelmReleaseSpec) {
|
||||
*out = *in
|
||||
if in.Chart != nil {
|
||||
in, out := &in.Chart, &out.Chart
|
||||
*out = new(HelmChartTemplate)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ChartRef != nil {
|
||||
in, out := &in.ChartRef, &out.ChartRef
|
||||
*out = new(CrossNamespaceSourceReference)
|
||||
**out = **in
|
||||
}
|
||||
out.Interval = in.Interval
|
||||
if in.KubeConfig != nil {
|
||||
in, out := &in.KubeConfig, &out.KubeConfig
|
||||
*out = new(meta.KubeConfigReference)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.DependsOn != nil {
|
||||
in, out := &in.DependsOn, &out.DependsOn
|
||||
*out = make([]meta.NamespacedObjectReference, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Timeout != nil {
|
||||
in, out := &in.Timeout, &out.Timeout
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.MaxHistory != nil {
|
||||
in, out := &in.MaxHistory, &out.MaxHistory
|
||||
*out = new(int)
|
||||
**out = **in
|
||||
}
|
||||
if in.PersistentClient != nil {
|
||||
in, out := &in.PersistentClient, &out.PersistentClient
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.DriftDetection != nil {
|
||||
in, out := &in.DriftDetection, &out.DriftDetection
|
||||
*out = new(DriftDetection)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Install != nil {
|
||||
in, out := &in.Install, &out.Install
|
||||
*out = new(Install)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Upgrade != nil {
|
||||
in, out := &in.Upgrade, &out.Upgrade
|
||||
*out = new(Upgrade)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Test != nil {
|
||||
in, out := &in.Test, &out.Test
|
||||
*out = new(Test)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Rollback != nil {
|
||||
in, out := &in.Rollback, &out.Rollback
|
||||
*out = new(Rollback)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Uninstall != nil {
|
||||
in, out := &in.Uninstall, &out.Uninstall
|
||||
*out = new(Uninstall)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ValuesFrom != nil {
|
||||
in, out := &in.ValuesFrom, &out.ValuesFrom
|
||||
*out = make([]ValuesReference, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Values != nil {
|
||||
in, out := &in.Values, &out.Values
|
||||
*out = new(v1.JSON)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.PostRenderers != nil {
|
||||
in, out := &in.PostRenderers, &out.PostRenderers
|
||||
*out = make([]PostRenderer, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmReleaseSpec.
|
||||
func (in *HelmReleaseSpec) DeepCopy() *HelmReleaseSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmReleaseSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmReleaseStatus) DeepCopyInto(out *HelmReleaseStatus) {
|
||||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]metav1.Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.History != nil {
|
||||
in, out := &in.History, &out.History
|
||||
*out = make(v2.Snapshots, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(v2.Snapshot)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
out.ReconcileRequestStatus = in.ReconcileRequestStatus
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmReleaseStatus.
|
||||
func (in *HelmReleaseStatus) DeepCopy() *HelmReleaseStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmReleaseStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *IgnoreRule) DeepCopyInto(out *IgnoreRule) {
|
||||
*out = *in
|
||||
if in.Paths != nil {
|
||||
in, out := &in.Paths, &out.Paths
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Target != nil {
|
||||
in, out := &in.Target, &out.Target
|
||||
*out = new(kustomize.Selector)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IgnoreRule.
|
||||
func (in *IgnoreRule) DeepCopy() *IgnoreRule {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(IgnoreRule)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Install) DeepCopyInto(out *Install) {
|
||||
*out = *in
|
||||
if in.Timeout != nil {
|
||||
in, out := &in.Timeout, &out.Timeout
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.Remediation != nil {
|
||||
in, out := &in.Remediation, &out.Remediation
|
||||
*out = new(InstallRemediation)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Install.
|
||||
func (in *Install) DeepCopy() *Install {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Install)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *InstallRemediation) DeepCopyInto(out *InstallRemediation) {
|
||||
*out = *in
|
||||
if in.IgnoreTestFailures != nil {
|
||||
in, out := &in.IgnoreTestFailures, &out.IgnoreTestFailures
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.RemediateLastFailure != nil {
|
||||
in, out := &in.RemediateLastFailure, &out.RemediateLastFailure
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstallRemediation.
|
||||
func (in *InstallRemediation) DeepCopy() *InstallRemediation {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(InstallRemediation)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Kustomize) DeepCopyInto(out *Kustomize) {
|
||||
*out = *in
|
||||
if in.Patches != nil {
|
||||
in, out := &in.Patches, &out.Patches
|
||||
*out = make([]kustomize.Patch, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.PatchesStrategicMerge != nil {
|
||||
in, out := &in.PatchesStrategicMerge, &out.PatchesStrategicMerge
|
||||
*out = make([]v1.JSON, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.PatchesJSON6902 != nil {
|
||||
in, out := &in.PatchesJSON6902, &out.PatchesJSON6902
|
||||
*out = make([]kustomize.JSON6902Patch, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Images != nil {
|
||||
in, out := &in.Images, &out.Images
|
||||
*out = make([]kustomize.Image, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Kustomize.
|
||||
func (in *Kustomize) DeepCopy() *Kustomize {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Kustomize)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PostRenderer) DeepCopyInto(out *PostRenderer) {
|
||||
*out = *in
|
||||
if in.Kustomize != nil {
|
||||
in, out := &in.Kustomize, &out.Kustomize
|
||||
*out = new(Kustomize)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostRenderer.
|
||||
func (in *PostRenderer) DeepCopy() *PostRenderer {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PostRenderer)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Rollback) DeepCopyInto(out *Rollback) {
|
||||
*out = *in
|
||||
if in.Timeout != nil {
|
||||
in, out := &in.Timeout, &out.Timeout
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rollback.
|
||||
func (in *Rollback) DeepCopy() *Rollback {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Rollback)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Snapshot) DeepCopyInto(out *Snapshot) {
|
||||
*out = *in
|
||||
in.FirstDeployed.DeepCopyInto(&out.FirstDeployed)
|
||||
in.LastDeployed.DeepCopyInto(&out.LastDeployed)
|
||||
in.Deleted.DeepCopyInto(&out.Deleted)
|
||||
if in.TestHooks != nil {
|
||||
in, out := &in.TestHooks, &out.TestHooks
|
||||
*out = new(map[string]*TestHookStatus)
|
||||
if **in != nil {
|
||||
in, out := *in, *out
|
||||
*out = make(map[string]*TestHookStatus, len(*in))
|
||||
for key, val := range *in {
|
||||
var outVal *TestHookStatus
|
||||
if val == nil {
|
||||
(*out)[key] = nil
|
||||
} else {
|
||||
inVal := (*in)[key]
|
||||
in, out := &inVal, &outVal
|
||||
*out = new(TestHookStatus)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
(*out)[key] = outVal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Snapshot.
|
||||
func (in *Snapshot) DeepCopy() *Snapshot {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Snapshot)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in Snapshots) DeepCopyInto(out *Snapshots) {
|
||||
{
|
||||
in := &in
|
||||
*out = make(Snapshots, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Snapshot)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Snapshots.
|
||||
func (in Snapshots) DeepCopy() Snapshots {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Snapshots)
|
||||
in.DeepCopyInto(out)
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Test) DeepCopyInto(out *Test) {
|
||||
*out = *in
|
||||
if in.Timeout != nil {
|
||||
in, out := &in.Timeout, &out.Timeout
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.Filters != nil {
|
||||
in, out := &in.Filters, &out.Filters
|
||||
*out = new([]Filter)
|
||||
if **in != nil {
|
||||
in, out := *in, *out
|
||||
*out = make([]Filter, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Test.
|
||||
func (in *Test) DeepCopy() *Test {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Test)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TestHookStatus) DeepCopyInto(out *TestHookStatus) {
|
||||
*out = *in
|
||||
in.LastStarted.DeepCopyInto(&out.LastStarted)
|
||||
in.LastCompleted.DeepCopyInto(&out.LastCompleted)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TestHookStatus.
|
||||
func (in *TestHookStatus) DeepCopy() *TestHookStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TestHookStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Uninstall) DeepCopyInto(out *Uninstall) {
|
||||
*out = *in
|
||||
if in.Timeout != nil {
|
||||
in, out := &in.Timeout, &out.Timeout
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.DeletionPropagation != nil {
|
||||
in, out := &in.DeletionPropagation, &out.DeletionPropagation
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Uninstall.
|
||||
func (in *Uninstall) DeepCopy() *Uninstall {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Uninstall)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Upgrade) DeepCopyInto(out *Upgrade) {
|
||||
*out = *in
|
||||
if in.Timeout != nil {
|
||||
in, out := &in.Timeout, &out.Timeout
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.Remediation != nil {
|
||||
in, out := &in.Remediation, &out.Remediation
|
||||
*out = new(UpgradeRemediation)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Upgrade.
|
||||
func (in *Upgrade) DeepCopy() *Upgrade {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Upgrade)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *UpgradeRemediation) DeepCopyInto(out *UpgradeRemediation) {
|
||||
*out = *in
|
||||
if in.IgnoreTestFailures != nil {
|
||||
in, out := &in.IgnoreTestFailures, &out.IgnoreTestFailures
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.RemediateLastFailure != nil {
|
||||
in, out := &in.RemediateLastFailure, &out.RemediateLastFailure
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.Strategy != nil {
|
||||
in, out := &in.Strategy, &out.Strategy
|
||||
*out = new(RemediationStrategy)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpgradeRemediation.
|
||||
func (in *UpgradeRemediation) DeepCopy() *UpgradeRemediation {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(UpgradeRemediation)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ValuesReference) DeepCopyInto(out *ValuesReference) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ValuesReference.
|
||||
func (in *ValuesReference) DeepCopy() *ValuesReference {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ValuesReference)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -2,8 +2,8 @@ apiVersion: kustomize.config.k8s.io/v1beta1
|
|||
kind: Kustomization
|
||||
namespace: helm-system
|
||||
resources:
|
||||
- https://github.com/fluxcd/source-controller/releases/download/v1.6.0/source-controller.crds.yaml
|
||||
- https://github.com/fluxcd/source-controller/releases/download/v1.6.0/source-controller.deployment.yaml
|
||||
- https://github.com/fluxcd/source-controller/releases/download/v0.32.0/source-controller.crds.yaml
|
||||
- https://github.com/fluxcd/source-controller/releases/download/v0.32.0/source-controller.deployment.yaml
|
||||
- ../crd
|
||||
- ../rbac
|
||||
- ../manager
|
||||
|
|
|
@ -5,4 +5,4 @@ resources:
|
|||
images:
|
||||
- name: fluxcd/helm-controller
|
||||
newName: fluxcd/helm-controller
|
||||
newTag: v1.3.0
|
||||
newTag: v0.28.1
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: manager-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
|
@ -45,7 +46,6 @@ rules:
|
|||
- source.toolkit.fluxcd.io
|
||||
resources:
|
||||
- helmcharts
|
||||
- ocirepositories
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
|
@ -54,6 +54,5 @@ rules:
|
|||
- source.toolkit.fluxcd.io
|
||||
resources:
|
||||
- helmcharts/status
|
||||
- ocirepositories/status
|
||||
verbs:
|
||||
- get
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: podinfo-ocirepository
|
||||
spec:
|
||||
interval: 5m
|
||||
chartRef:
|
||||
kind: OCIRepository
|
||||
name: podinfo
|
||||
test:
|
||||
enable: true
|
||||
values:
|
||||
replicaCount: 2
|
|
@ -1,4 +1,4 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: podinfo-gitrepository
|
||||
|
@ -10,3 +10,9 @@ spec:
|
|||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: podinfo
|
||||
interval: 1m
|
||||
upgrade:
|
||||
remediation:
|
||||
remediateLastFailure: true
|
||||
test:
|
||||
enable: true
|
|
@ -1,4 +1,4 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: podinfo-helmrepository
|
||||
|
@ -7,8 +7,13 @@ spec:
|
|||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
interval: 10m
|
||||
interval: 1m
|
||||
upgrade:
|
||||
remediation:
|
||||
remediateLastFailure: true
|
||||
test:
|
||||
enable: true
|
|
@ -1,9 +0,0 @@
|
|||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: OCIRepository
|
||||
metadata:
|
||||
name: podinfo
|
||||
spec:
|
||||
interval: 1m
|
||||
url: oci://ghcr.io/stefanprodan/charts/podinfo
|
||||
ref:
|
||||
semver: 6.x
|
|
@ -1,4 +1,4 @@
|
|||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta2
|
||||
kind: GitRepository
|
||||
metadata:
|
||||
name: podinfo
|
|
@ -1,4 +1,4 @@
|
|||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta2
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: podinfo
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta2
|
||||
kind: GitRepository
|
||||
metadata:
|
||||
name: this
|
||||
|
@ -11,6 +11,6 @@ spec:
|
|||
{{- if .Values.branch }}
|
||||
branch: "{{ .Values.branch }}"
|
||||
{{- end}}
|
||||
{{- if .Values.tag }}
|
||||
{{- if .Values.branch }}
|
||||
tag: "{{ .Values.tag }}"
|
||||
{{- end}}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: crds-upgrade-test
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: crds-upgrade-test
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: crds-upgrade-test
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: delete-ns
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: gotk-reconciler
|
||||
namespace: delete-ns
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: gotk-reconciler
|
||||
namespace: delete-ns
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- '*'
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- '*'
|
||||
verbs:
|
||||
- '*'
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: gotk-reconciler
|
||||
namespace: delete-ns
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: gotk-reconciler
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: gotk-reconciler
|
||||
namespace: delete-ns
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta2
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: delete-ns
|
||||
spec:
|
||||
interval: 1m
|
||||
url: https://stefanprodan.github.io/podinfo
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: delete-ns
|
||||
spec:
|
||||
serviceAccountName: gotk-reconciler
|
||||
interval: 5m
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: 6.3.5
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
|
@ -1,4 +1,4 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: backend
|
||||
|
@ -7,7 +7,7 @@ spec:
|
|||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: frontend
|
||||
|
@ -7,7 +7,7 @@ spec:
|
|||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
|
|
|
@ -42,7 +42,7 @@ subjects:
|
|||
name: gotk-reconciler
|
||||
namespace: impersonation
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta2
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: podinfo
|
||||
|
@ -51,7 +51,7 @@ spec:
|
|||
interval: 1m
|
||||
url: https://stefanprodan.github.io/podinfo
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: podinfo
|
||||
|
@ -62,12 +62,12 @@ spec:
|
|||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: 6.3.5
|
||||
version: 5.0.3
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: podinfo-fail
|
||||
|
@ -78,7 +78,7 @@ spec:
|
|||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: 6.3.5
|
||||
version: 5.0.3
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: install-create-target-ns
|
||||
|
@ -9,7 +9,7 @@ spec:
|
|||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: install-fail-remediate
|
||||
spec:
|
||||
interval: 30s
|
||||
interval: 5m
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
interval: 10m
|
||||
interval: 1m
|
||||
install:
|
||||
remediation:
|
||||
remediateLastFailure: true
|
||||
uninstall:
|
||||
keepHistory: true
|
||||
values:
|
||||
resources:
|
||||
requests:
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: install-fail-retry
|
||||
spec:
|
||||
interval: 30s
|
||||
interval: 5m
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
interval: 10m
|
||||
interval: 1m
|
||||
install:
|
||||
remediation:
|
||||
retries: 1
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: install-fail
|
||||
spec:
|
||||
interval: 30s
|
||||
interval: 5m
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
interval: 10m
|
||||
interval: 1m
|
||||
values:
|
||||
resources:
|
||||
requests:
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta2
|
||||
kind: HelmChart
|
||||
metadata:
|
||||
name: podinfo-hc
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '6.2.1'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo-oci
|
||||
interval: 30s
|
||||
verify:
|
||||
provider: cosign
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: podinfo-from-hc
|
||||
spec:
|
||||
chartRef:
|
||||
kind: HelmChart
|
||||
name: podinfo-hc
|
||||
interval: 30s
|
||||
values:
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 64Mi
|
|
@ -1,25 +0,0 @@
|
|||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: OCIRepository
|
||||
metadata:
|
||||
name: podinfo-ocirepo
|
||||
spec:
|
||||
interval: 30s
|
||||
url: oci://ghcr.io/stefanprodan/charts/podinfo
|
||||
ref:
|
||||
tag: 6.6.0
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: podinfo-from-ocirepo
|
||||
spec:
|
||||
chartRef:
|
||||
kind: OCIRepository
|
||||
name: podinfo-ocirepo
|
||||
interval: 30s
|
||||
values:
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 64Mi
|
|
@ -1,17 +1,17 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: install-test-fail-ignore
|
||||
spec:
|
||||
interval: 30s
|
||||
interval: 5m
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
interval: 10m
|
||||
interval: 1m
|
||||
test:
|
||||
enable: true
|
||||
ignoreFailures: true
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: install-test-fail
|
||||
spec:
|
||||
interval: 30s
|
||||
interval: 5m
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
interval: 10m
|
||||
interval: 1m
|
||||
test:
|
||||
enable: true
|
||||
values:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: podinfo-git
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: podinfo-oci
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: podinfo
|
||||
|
@ -7,7 +7,7 @@ spec:
|
|||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: post-renderer-kustomize
|
||||
|
@ -7,7 +7,7 @@ spec:
|
|||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <6.9.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
|
@ -16,20 +16,20 @@ spec:
|
|||
fullnameOverride: mypodinfo
|
||||
postRenderers:
|
||||
- kustomize:
|
||||
patches:
|
||||
- patch: |
|
||||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: mypodinfo
|
||||
labels:
|
||||
xxxx: yyyy
|
||||
patchesStrategicMerge:
|
||||
- kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: mypodinfo
|
||||
labels:
|
||||
xxxx: yyyy
|
||||
patchesJson6902:
|
||||
- target:
|
||||
group: apps
|
||||
version: v1
|
||||
kind: Deployment
|
||||
name: mypodinfo
|
||||
patch: |
|
||||
- op: add
|
||||
path: /metadata/labels/yyyy
|
||||
value: xxxx
|
||||
patch:
|
||||
- op: add
|
||||
path: /metadata/labels/yyyy
|
||||
value: xxxx
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta2
|
||||
kind: GitRepository
|
||||
metadata:
|
||||
name: podinfo
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: status-defaults
|
||||
|
@ -7,7 +7,7 @@ spec:
|
|||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: targetnamespace
|
||||
|
@ -7,7 +7,7 @@ spec:
|
|||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: upgrade-fail-remediate-uninstall
|
||||
spec:
|
||||
interval: 30s
|
||||
interval: 5m
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
interval: 10m
|
||||
interval: 1m
|
||||
values:
|
||||
resources:
|
||||
requests:
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: upgrade-fail-remediate-uninstall
|
||||
spec:
|
||||
interval: 30s
|
||||
interval: 5m
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
interval: 10m
|
||||
interval: 1m
|
||||
upgrade:
|
||||
remediation:
|
||||
remediateLastFailure: true
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: upgrade-fail-remediate
|
||||
spec:
|
||||
interval: 30s
|
||||
interval: 5m
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
interval: 10m
|
||||
interval: 1m
|
||||
values:
|
||||
resources:
|
||||
requests:
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: upgrade-fail-remediate
|
||||
spec:
|
||||
interval: 30s
|
||||
interval: 5m
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
interval: 10m
|
||||
interval: 1m
|
||||
upgrade:
|
||||
remediation:
|
||||
remediateLastFailure: true
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: upgrade-fail-retry
|
||||
spec:
|
||||
interval: 30s
|
||||
interval: 5m
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
interval: 10m
|
||||
interval: 1m
|
||||
values:
|
||||
resources:
|
||||
requests:
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: upgrade-fail-retry
|
||||
spec:
|
||||
interval: 30s
|
||||
interval: 5m
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
interval: 10m
|
||||
interval: 1m
|
||||
upgrade:
|
||||
remediation:
|
||||
retries: 1
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: upgrade-fail
|
||||
spec:
|
||||
interval: 30s
|
||||
interval: 5m
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
interval: 10m
|
||||
interval: 1m
|
||||
values:
|
||||
resources:
|
||||
requests:
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: upgrade-fail
|
||||
spec:
|
||||
interval: 30s
|
||||
interval: 5m
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
interval: 10m
|
||||
interval: 1m
|
||||
values:
|
||||
resources:
|
||||
requests:
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: OCIRepository
|
||||
metadata:
|
||||
name: upgrade-from-ocirepo-source
|
||||
spec:
|
||||
interval: 30s
|
||||
url: oci://ghcr.io/stefanprodan/charts/podinfo
|
||||
ref:
|
||||
digest: "sha256:cdd538a0167e4b51152b71a477e51eb6737553510ce8797dbcc537e1342311bb"
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: upgrade-from-ocirepo-source
|
||||
spec:
|
||||
chartRef:
|
||||
kind: OCIRepository
|
||||
name: upgrade-from-ocirepo-source
|
||||
interval: 30s
|
||||
values:
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 64Mi
|
|
@ -1,10 +0,0 @@
|
|||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: OCIRepository
|
||||
metadata:
|
||||
name: upgrade-from-ocirepo-source
|
||||
spec:
|
||||
interval: 30s
|
||||
url: oci://ghcr.io/stefanprodan/charts/podinfo
|
||||
ref:
|
||||
digest: "sha256:0cc9a8446c95009ef382f5eade883a67c257f77d50f84e78ecef2aac9428d1e5"
|
|
@ -1,17 +1,17 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: upgrade-test-fail
|
||||
spec:
|
||||
interval: 30s
|
||||
interval: 5m
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
interval: 10m
|
||||
interval: 1m
|
||||
values:
|
||||
resources:
|
||||
requests:
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: upgrade-test-fail
|
||||
spec:
|
||||
interval: 30s
|
||||
interval: 5m
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
interval: 10m
|
||||
interval: 1m
|
||||
test:
|
||||
enable: true
|
||||
values:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: valuesfrom
|
||||
|
@ -7,11 +7,11 @@ spec:
|
|||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
version: '>=6.0.0 <7.0.0'
|
||||
version: '>=4.0.0 <5.0.0'
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
interval: 10m
|
||||
interval: 1m
|
||||
valuesFrom:
|
||||
- kind: ConfigMap
|
||||
name: valuesfrom-config
|
||||
|
|
|
@ -0,0 +1,774 @@
|
|||
/*
|
||||
Copyright 2020 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 controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/storage/driver"
|
||||
"helm.sh/helm/v3/pkg/strvals"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/client-go/rest"
|
||||
kuberecorder "k8s.io/client-go/tools/record"
|
||||
"k8s.io/client-go/tools/reference"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
"sigs.k8s.io/controller-runtime/pkg/ratelimiter"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
|
||||
apiacl "github.com/fluxcd/pkg/apis/acl"
|
||||
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"github.com/fluxcd/pkg/runtime/acl"
|
||||
fluxClient "github.com/fluxcd/pkg/runtime/client"
|
||||
"github.com/fluxcd/pkg/runtime/metrics"
|
||||
"github.com/fluxcd/pkg/runtime/predicates"
|
||||
"github.com/fluxcd/pkg/runtime/transform"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
|
||||
v2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||
"github.com/fluxcd/helm-controller/internal/kube"
|
||||
"github.com/fluxcd/helm-controller/internal/runner"
|
||||
"github.com/fluxcd/helm-controller/internal/util"
|
||||
)
|
||||
|
||||
// +kubebuilder:rbac:groups=helm.toolkit.fluxcd.io,resources=helmreleases,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=helm.toolkit.fluxcd.io,resources=helmreleases/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups=helm.toolkit.fluxcd.io,resources=helmreleases/finalizers,verbs=get;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=helmcharts,verbs=get;list;watch
|
||||
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=helmcharts/status,verbs=get
|
||||
// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch
|
||||
|
||||
// HelmReleaseReconciler reconciles a HelmRelease object
|
||||
type HelmReleaseReconciler struct {
|
||||
client.Client
|
||||
httpClient *retryablehttp.Client
|
||||
Config *rest.Config
|
||||
Scheme *runtime.Scheme
|
||||
requeueDependency time.Duration
|
||||
EventRecorder kuberecorder.EventRecorder
|
||||
MetricsRecorder *metrics.Recorder
|
||||
DefaultServiceAccount string
|
||||
NoCrossNamespaceRef bool
|
||||
ClientOpts fluxClient.Options
|
||||
KubeConfigOpts fluxClient.KubeConfigOptions
|
||||
}
|
||||
|
||||
func (r *HelmReleaseReconciler) SetupWithManager(mgr ctrl.Manager, opts HelmReleaseReconcilerOptions) error {
|
||||
// Index the HelmRelease by the HelmChart references they point at
|
||||
if err := mgr.GetFieldIndexer().IndexField(context.TODO(), &v2.HelmRelease{}, v2.SourceIndexKey,
|
||||
func(o client.Object) []string {
|
||||
hr := o.(*v2.HelmRelease)
|
||||
return []string{
|
||||
fmt.Sprintf("%s/%s", hr.Spec.Chart.GetNamespace(hr.GetNamespace()), hr.GetHelmChartName()),
|
||||
}
|
||||
},
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.requeueDependency = opts.DependencyRequeueInterval
|
||||
|
||||
// Configure the retryable http client used for fetching artifacts.
|
||||
// By default it retries 10 times within a 3.5 minutes window.
|
||||
httpClient := retryablehttp.NewClient()
|
||||
httpClient.RetryWaitMin = 5 * time.Second
|
||||
httpClient.RetryWaitMax = 30 * time.Second
|
||||
httpClient.RetryMax = opts.HTTPRetry
|
||||
httpClient.Logger = nil
|
||||
r.httpClient = httpClient
|
||||
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&v2.HelmRelease{}, builder.WithPredicates(
|
||||
predicate.Or(predicate.GenerationChangedPredicate{}, predicates.ReconcileRequestedPredicate{}),
|
||||
)).
|
||||
Watches(
|
||||
&source.Kind{Type: &sourcev1.HelmChart{}},
|
||||
handler.EnqueueRequestsFromMapFunc(r.requestsForHelmChartChange),
|
||||
builder.WithPredicates(SourceRevisionChangePredicate{}),
|
||||
).
|
||||
WithOptions(controller.Options{
|
||||
MaxConcurrentReconciles: opts.MaxConcurrentReconciles,
|
||||
RateLimiter: opts.RateLimiter,
|
||||
RecoverPanic: true,
|
||||
}).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
// ConditionError represents an error with a status condition reason attached.
|
||||
type ConditionError struct {
|
||||
Reason string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (c ConditionError) Error() string {
|
||||
return c.Err.Error()
|
||||
}
|
||||
|
||||
func (r *HelmReleaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
start := time.Now()
|
||||
log := ctrl.LoggerFrom(ctx)
|
||||
|
||||
var hr v2.HelmRelease
|
||||
if err := r.Get(ctx, req.NamespacedName, &hr); err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
// record suspension metrics
|
||||
defer r.recordSuspension(ctx, hr)
|
||||
|
||||
// Add our finalizer if it does not exist
|
||||
if !controllerutil.ContainsFinalizer(&hr, v2.HelmReleaseFinalizer) {
|
||||
patch := client.MergeFrom(hr.DeepCopy())
|
||||
controllerutil.AddFinalizer(&hr, v2.HelmReleaseFinalizer)
|
||||
if err := r.Patch(ctx, &hr, patch); err != nil {
|
||||
log.Error(err, "unable to register finalizer")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// Examine if the object is under deletion
|
||||
if !hr.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
return r.reconcileDelete(ctx, hr)
|
||||
}
|
||||
|
||||
// Return early if the HelmRelease is suspended.
|
||||
if hr.Spec.Suspend {
|
||||
log.Info("Reconciliation is suspended for this object")
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
hr, result, err := r.reconcile(ctx, hr)
|
||||
|
||||
// Update status after reconciliation.
|
||||
if updateStatusErr := r.patchStatus(ctx, &hr); updateStatusErr != nil {
|
||||
log.Error(updateStatusErr, "unable to update status after reconciliation")
|
||||
return ctrl.Result{Requeue: true}, updateStatusErr
|
||||
}
|
||||
|
||||
// Record ready status
|
||||
r.recordReadiness(ctx, hr)
|
||||
|
||||
// Log reconciliation duration
|
||||
durationMsg := fmt.Sprintf("reconcilation finished in %s", time.Now().Sub(start).String())
|
||||
if result.RequeueAfter > 0 {
|
||||
durationMsg = fmt.Sprintf("%s, next run in %s", durationMsg, result.RequeueAfter.String())
|
||||
}
|
||||
log.Info(durationMsg)
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (r *HelmReleaseReconciler) reconcile(ctx context.Context, hr v2.HelmRelease) (v2.HelmRelease, ctrl.Result, error) {
|
||||
reconcileStart := time.Now()
|
||||
log := ctrl.LoggerFrom(ctx)
|
||||
// Record the value of the reconciliation request, if any
|
||||
if v, ok := meta.ReconcileAnnotationValue(hr.GetAnnotations()); ok {
|
||||
hr.Status.SetLastHandledReconcileRequest(v)
|
||||
}
|
||||
|
||||
// Observe HelmRelease generation.
|
||||
if hr.Status.ObservedGeneration != hr.Generation {
|
||||
hr.Status.ObservedGeneration = hr.Generation
|
||||
hr = v2.HelmReleaseProgressing(hr)
|
||||
if updateStatusErr := r.patchStatus(ctx, &hr); updateStatusErr != nil {
|
||||
log.Error(updateStatusErr, "unable to update status after generation update")
|
||||
return hr, ctrl.Result{Requeue: true}, updateStatusErr
|
||||
}
|
||||
// Record progressing status
|
||||
r.recordReadiness(ctx, hr)
|
||||
}
|
||||
|
||||
// Record reconciliation duration
|
||||
if r.MetricsRecorder != nil {
|
||||
objRef, err := reference.GetReference(r.Scheme, &hr)
|
||||
if err != nil {
|
||||
return hr, ctrl.Result{Requeue: true}, err
|
||||
}
|
||||
defer r.MetricsRecorder.RecordDuration(*objRef, reconcileStart)
|
||||
}
|
||||
|
||||
// Reconcile chart based on the HelmChartTemplate
|
||||
hc, reconcileErr := r.reconcileChart(ctx, &hr)
|
||||
if reconcileErr != nil {
|
||||
if acl.IsAccessDenied(reconcileErr) {
|
||||
log.Error(reconcileErr, "access denied to cross-namespace source")
|
||||
r.event(ctx, hr, hr.Status.LastAttemptedRevision, eventv1.EventSeverityError, reconcileErr.Error())
|
||||
return v2.HelmReleaseNotReady(hr, apiacl.AccessDeniedReason, reconcileErr.Error()),
|
||||
ctrl.Result{RequeueAfter: hr.Spec.Interval.Duration}, nil
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("chart reconciliation failed: %s", reconcileErr.Error())
|
||||
r.event(ctx, hr, hr.Status.LastAttemptedRevision, eventv1.EventSeverityError, msg)
|
||||
return v2.HelmReleaseNotReady(hr, v2.ArtifactFailedReason, msg), ctrl.Result{Requeue: true}, reconcileErr
|
||||
}
|
||||
|
||||
// Check chart readiness
|
||||
if hc.Generation != hc.Status.ObservedGeneration || !apimeta.IsStatusConditionTrue(hc.Status.Conditions, meta.ReadyCondition) {
|
||||
msg := fmt.Sprintf("HelmChart '%s/%s' is not ready", hc.GetNamespace(), hc.GetName())
|
||||
r.event(ctx, hr, hr.Status.LastAttemptedRevision, eventv1.EventSeverityInfo, msg)
|
||||
log.Info(msg)
|
||||
// Do not requeue immediately, when the artifact is created
|
||||
// the watcher should trigger a reconciliation.
|
||||
return v2.HelmReleaseNotReady(hr, v2.ArtifactFailedReason, msg), ctrl.Result{RequeueAfter: hc.Spec.Interval.Duration}, nil
|
||||
}
|
||||
|
||||
// Check dependencies
|
||||
if len(hr.Spec.DependsOn) > 0 {
|
||||
if err := r.checkDependencies(hr); err != nil {
|
||||
msg := fmt.Sprintf("dependencies do not meet ready condition (%s), retrying in %s",
|
||||
err.Error(), r.requeueDependency.String())
|
||||
r.event(ctx, hr, hc.GetArtifact().Revision, eventv1.EventSeverityInfo, msg)
|
||||
log.Info(msg)
|
||||
|
||||
// Exponential backoff would cause execution to be prolonged too much,
|
||||
// instead we requeue on a fixed interval.
|
||||
return v2.HelmReleaseNotReady(hr,
|
||||
v2.DependencyNotReadyReason, err.Error()), ctrl.Result{RequeueAfter: r.requeueDependency}, nil
|
||||
}
|
||||
log.Info("all dependencies are ready, proceeding with release")
|
||||
}
|
||||
|
||||
// Compose values
|
||||
values, err := r.composeValues(ctx, hr)
|
||||
if err != nil {
|
||||
r.event(ctx, hr, hr.Status.LastAttemptedRevision, eventv1.EventSeverityError, err.Error())
|
||||
return v2.HelmReleaseNotReady(hr, v2.InitFailedReason, err.Error()), ctrl.Result{Requeue: true}, nil
|
||||
}
|
||||
|
||||
// Load chart from artifact
|
||||
chart, err := r.loadHelmChart(hc)
|
||||
if err != nil {
|
||||
r.event(ctx, hr, hr.Status.LastAttemptedRevision, eventv1.EventSeverityError, err.Error())
|
||||
return v2.HelmReleaseNotReady(hr, v2.ArtifactFailedReason, err.Error()), ctrl.Result{Requeue: true}, nil
|
||||
}
|
||||
|
||||
// Reconcile Helm release
|
||||
reconciledHr, reconcileErr := r.reconcileRelease(ctx, *hr.DeepCopy(), chart, values)
|
||||
if reconcileErr != nil {
|
||||
r.event(ctx, hr, hc.GetArtifact().Revision, eventv1.EventSeverityError,
|
||||
fmt.Sprintf("reconciliation failed: %s", reconcileErr.Error()))
|
||||
}
|
||||
return reconciledHr, ctrl.Result{RequeueAfter: hr.Spec.Interval.Duration}, reconcileErr
|
||||
}
|
||||
|
||||
type HelmReleaseReconcilerOptions struct {
|
||||
MaxConcurrentReconciles int
|
||||
HTTPRetry int
|
||||
DependencyRequeueInterval time.Duration
|
||||
RateLimiter ratelimiter.RateLimiter
|
||||
}
|
||||
|
||||
func (r *HelmReleaseReconciler) reconcileRelease(ctx context.Context,
|
||||
hr v2.HelmRelease, chart *chart.Chart, values chartutil.Values) (v2.HelmRelease, error) {
|
||||
log := ctrl.LoggerFrom(ctx)
|
||||
|
||||
// Initialize Helm action runner
|
||||
getter, err := r.buildRESTClientGetter(ctx, hr)
|
||||
if err != nil {
|
||||
return v2.HelmReleaseNotReady(hr, v2.InitFailedReason, err.Error()), err
|
||||
}
|
||||
run, err := runner.NewRunner(getter, hr.GetStorageNamespace(), log)
|
||||
if err != nil {
|
||||
return v2.HelmReleaseNotReady(hr, v2.InitFailedReason, "failed to initialize Helm action runner"), err
|
||||
}
|
||||
|
||||
// Determine last release revision.
|
||||
rel, observeLastReleaseErr := run.ObserveLastRelease(hr)
|
||||
if observeLastReleaseErr != nil {
|
||||
err = fmt.Errorf("failed to get last release revision: %w", observeLastReleaseErr)
|
||||
return v2.HelmReleaseNotReady(hr, v2.GetLastReleaseFailedReason, "failed to get last release revision"), err
|
||||
}
|
||||
|
||||
// Register the current release attempt.
|
||||
revision := chart.Metadata.Version
|
||||
releaseRevision := util.ReleaseRevision(rel)
|
||||
valuesChecksum := util.ValuesChecksum(values)
|
||||
hr, hasNewState := v2.HelmReleaseAttempted(hr, revision, releaseRevision, valuesChecksum)
|
||||
if hasNewState {
|
||||
hr = v2.HelmReleaseProgressing(hr)
|
||||
if updateStatusErr := r.patchStatus(ctx, &hr); updateStatusErr != nil {
|
||||
log.Error(updateStatusErr, "unable to update status after state update")
|
||||
return hr, updateStatusErr
|
||||
}
|
||||
// Record progressing status
|
||||
r.recordReadiness(ctx, hr)
|
||||
}
|
||||
|
||||
// Check status of any previous release attempt.
|
||||
released := apimeta.FindStatusCondition(hr.Status.Conditions, v2.ReleasedCondition)
|
||||
if released != nil {
|
||||
switch released.Status {
|
||||
// Succeed if the previous release attempt succeeded.
|
||||
case metav1.ConditionTrue:
|
||||
return v2.HelmReleaseReady(hr), nil
|
||||
case metav1.ConditionFalse:
|
||||
// Fail if the previous release attempt remediation failed.
|
||||
remediated := apimeta.FindStatusCondition(hr.Status.Conditions, v2.RemediatedCondition)
|
||||
if remediated != nil && remediated.Status == metav1.ConditionFalse {
|
||||
err = fmt.Errorf("previous release attempt remediation failed")
|
||||
return v2.HelmReleaseNotReady(hr, remediated.Reason, remediated.Message), err
|
||||
}
|
||||
}
|
||||
|
||||
// Fail if install retries are exhausted.
|
||||
if hr.Spec.GetInstall().GetRemediation().RetriesExhausted(hr) {
|
||||
err = fmt.Errorf("install retries exhausted")
|
||||
return v2.HelmReleaseNotReady(hr, released.Reason, err.Error()), err
|
||||
}
|
||||
|
||||
// Fail if there is a release and upgrade retries are exhausted.
|
||||
// This avoids failing after an upgrade uninstall remediation strategy.
|
||||
if rel != nil && hr.Spec.GetUpgrade().GetRemediation().RetriesExhausted(hr) {
|
||||
err = fmt.Errorf("upgrade retries exhausted")
|
||||
return v2.HelmReleaseNotReady(hr, released.Reason, err.Error()), err
|
||||
}
|
||||
}
|
||||
|
||||
// Deploy the release.
|
||||
var deployAction v2.DeploymentAction
|
||||
if rel == nil {
|
||||
r.event(ctx, hr, revision, eventv1.EventSeverityInfo, "Helm install has started")
|
||||
deployAction = hr.Spec.GetInstall()
|
||||
rel, err = run.Install(hr, chart, values)
|
||||
err = r.handleHelmActionResult(ctx, &hr, revision, err, deployAction.GetDescription(),
|
||||
v2.ReleasedCondition, v2.InstallSucceededReason, v2.InstallFailedReason)
|
||||
} else {
|
||||
r.event(ctx, hr, revision, eventv1.EventSeverityInfo, "Helm upgrade has started")
|
||||
deployAction = hr.Spec.GetUpgrade()
|
||||
rel, err = run.Upgrade(hr, chart, values)
|
||||
err = r.handleHelmActionResult(ctx, &hr, revision, err, deployAction.GetDescription(),
|
||||
v2.ReleasedCondition, v2.UpgradeSucceededReason, v2.UpgradeFailedReason)
|
||||
}
|
||||
remediation := deployAction.GetRemediation()
|
||||
|
||||
// If there is a new release revision...
|
||||
if util.ReleaseRevision(rel) > releaseRevision {
|
||||
// Ensure release is not marked remediated.
|
||||
apimeta.RemoveStatusCondition(&hr.Status.Conditions, v2.RemediatedCondition)
|
||||
|
||||
// If new release revision is successful and tests are enabled, run them.
|
||||
if err == nil && hr.Spec.GetTest().Enable {
|
||||
_, testErr := run.Test(hr)
|
||||
testErr = r.handleHelmActionResult(ctx, &hr, revision, testErr, "test",
|
||||
v2.TestSuccessCondition, v2.TestSucceededReason, v2.TestFailedReason)
|
||||
|
||||
// Propagate any test error if not marked ignored.
|
||||
if testErr != nil && !remediation.MustIgnoreTestFailures(hr.Spec.GetTest().IgnoreFailures) {
|
||||
testsPassing := apimeta.FindStatusCondition(hr.Status.Conditions, v2.TestSuccessCondition)
|
||||
newCondition := metav1.Condition{
|
||||
Type: v2.ReleasedCondition,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: testsPassing.Reason,
|
||||
Message: testsPassing.Message,
|
||||
}
|
||||
apimeta.SetStatusCondition(hr.GetStatusConditions(), newCondition)
|
||||
err = testErr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// Increment failure count for deployment action.
|
||||
remediation.IncrementFailureCount(&hr)
|
||||
// Remediate deployment failure if necessary.
|
||||
if !remediation.RetriesExhausted(hr) || remediation.MustRemediateLastFailure() {
|
||||
if util.ReleaseRevision(rel) <= releaseRevision {
|
||||
log.Info(fmt.Sprintf("skipping remediation, no new release revision created"))
|
||||
} else {
|
||||
var remediationErr error
|
||||
switch remediation.GetStrategy() {
|
||||
case v2.RollbackRemediationStrategy:
|
||||
rollbackErr := run.Rollback(hr)
|
||||
remediationErr = r.handleHelmActionResult(ctx, &hr, revision, rollbackErr, "rollback",
|
||||
v2.RemediatedCondition, v2.RollbackSucceededReason, v2.RollbackFailedReason)
|
||||
case v2.UninstallRemediationStrategy:
|
||||
uninstallErr := run.Uninstall(hr)
|
||||
remediationErr = r.handleHelmActionResult(ctx, &hr, revision, uninstallErr, "uninstall",
|
||||
v2.RemediatedCondition, v2.UninstallSucceededReason, v2.UninstallFailedReason)
|
||||
}
|
||||
if remediationErr != nil {
|
||||
err = remediationErr
|
||||
}
|
||||
}
|
||||
|
||||
// Determine release after remediation.
|
||||
rel, observeLastReleaseErr = run.ObserveLastRelease(hr)
|
||||
if observeLastReleaseErr != nil {
|
||||
err = &ConditionError{
|
||||
Reason: v2.GetLastReleaseFailedReason,
|
||||
Err: errors.New("failed to get last release revision after remediation"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hr.Status.LastReleaseRevision = util.ReleaseRevision(rel)
|
||||
|
||||
if err != nil {
|
||||
reason := v2.ReconciliationFailedReason
|
||||
if condErr := (*ConditionError)(nil); errors.As(err, &condErr) {
|
||||
reason = condErr.Reason
|
||||
}
|
||||
return v2.HelmReleaseNotReady(hr, reason, err.Error()), err
|
||||
}
|
||||
return v2.HelmReleaseReady(hr), nil
|
||||
}
|
||||
|
||||
func (r *HelmReleaseReconciler) checkDependencies(hr v2.HelmRelease) error {
|
||||
for _, d := range hr.Spec.DependsOn {
|
||||
if d.Namespace == "" {
|
||||
d.Namespace = hr.GetNamespace()
|
||||
}
|
||||
dName := types.NamespacedName{
|
||||
Namespace: d.Namespace,
|
||||
Name: d.Name,
|
||||
}
|
||||
var dHr v2.HelmRelease
|
||||
err := r.Get(context.Background(), dName, &dHr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get '%s' dependency: %w", dName, err)
|
||||
}
|
||||
|
||||
if len(dHr.Status.Conditions) == 0 || dHr.Generation != dHr.Status.ObservedGeneration {
|
||||
return fmt.Errorf("dependency '%s' is not ready", dName)
|
||||
}
|
||||
|
||||
if !apimeta.IsStatusConditionTrue(dHr.Status.Conditions, meta.ReadyCondition) {
|
||||
return fmt.Errorf("dependency '%s' is not ready", dName)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *HelmReleaseReconciler) buildRESTClientGetter(ctx context.Context, hr v2.HelmRelease) (genericclioptions.RESTClientGetter, error) {
|
||||
opts := []kube.ClientGetterOption{
|
||||
kube.WithClientOptions(r.ClientOpts),
|
||||
// When ServiceAccountName is empty, it will fall back to the configured default.
|
||||
// If this is not configured either, this option will result in a no-op.
|
||||
kube.WithImpersonate(hr.Spec.ServiceAccountName, hr.GetNamespace()),
|
||||
}
|
||||
if hr.Spec.KubeConfig != nil {
|
||||
secretName := types.NamespacedName{
|
||||
Namespace: hr.GetNamespace(),
|
||||
Name: hr.Spec.KubeConfig.SecretRef.Name,
|
||||
}
|
||||
var secret corev1.Secret
|
||||
if err := r.Get(ctx, secretName, &secret); err != nil {
|
||||
return nil, fmt.Errorf("could not find KubeConfig secret '%s': %w", secretName, err)
|
||||
}
|
||||
kubeConfig, err := kube.ConfigFromSecret(&secret, hr.Spec.KubeConfig.SecretRef.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts = append(opts, kube.WithKubeConfig(kubeConfig, r.KubeConfigOpts))
|
||||
}
|
||||
return kube.BuildClientGetter(hr.GetReleaseNamespace(), opts...)
|
||||
}
|
||||
|
||||
// composeValues attempts to resolve all v2beta1.ValuesReference resources
|
||||
// and merges them as defined. Referenced resources are only retrieved once
|
||||
// to ensure a single version is taken into account during the merge.
|
||||
func (r *HelmReleaseReconciler) composeValues(ctx context.Context, hr v2.HelmRelease) (chartutil.Values, error) {
|
||||
result := chartutil.Values{}
|
||||
|
||||
configMaps := make(map[string]*corev1.ConfigMap)
|
||||
secrets := make(map[string]*corev1.Secret)
|
||||
|
||||
for _, v := range hr.Spec.ValuesFrom {
|
||||
namespacedName := types.NamespacedName{Namespace: hr.Namespace, Name: v.Name}
|
||||
var valuesData []byte
|
||||
|
||||
switch v.Kind {
|
||||
case "ConfigMap":
|
||||
resource, ok := configMaps[namespacedName.String()]
|
||||
if !ok {
|
||||
// The resource may not exist, but we want to act on a single version
|
||||
// of the resource in case the values reference is marked as optional.
|
||||
configMaps[namespacedName.String()] = nil
|
||||
|
||||
resource = &corev1.ConfigMap{}
|
||||
if err := r.Get(ctx, namespacedName, resource); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
if v.Optional {
|
||||
(ctrl.LoggerFrom(ctx)).
|
||||
Info(fmt.Sprintf("could not find optional %s '%s'", v.Kind, namespacedName))
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("could not find %s '%s'", v.Kind, namespacedName)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
configMaps[namespacedName.String()] = resource
|
||||
}
|
||||
if resource == nil {
|
||||
if v.Optional {
|
||||
(ctrl.LoggerFrom(ctx)).Info(fmt.Sprintf("could not find optional %s '%s'", v.Kind, namespacedName))
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("could not find %s '%s'", v.Kind, namespacedName)
|
||||
}
|
||||
if data, ok := resource.Data[v.GetValuesKey()]; !ok {
|
||||
return nil, fmt.Errorf("missing key '%s' in %s '%s'", v.GetValuesKey(), v.Kind, namespacedName)
|
||||
} else {
|
||||
valuesData = []byte(data)
|
||||
}
|
||||
case "Secret":
|
||||
resource, ok := secrets[namespacedName.String()]
|
||||
if !ok {
|
||||
// The resource may not exist, but we want to act on a single version
|
||||
// of the resource in case the values reference is marked as optional.
|
||||
secrets[namespacedName.String()] = nil
|
||||
|
||||
resource = &corev1.Secret{}
|
||||
if err := r.Get(ctx, namespacedName, resource); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
if v.Optional {
|
||||
(ctrl.LoggerFrom(ctx)).
|
||||
Info(fmt.Sprintf("could not find optional %s '%s'", v.Kind, namespacedName))
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("could not find %s '%s'", v.Kind, namespacedName)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
secrets[namespacedName.String()] = resource
|
||||
}
|
||||
if resource == nil {
|
||||
if v.Optional {
|
||||
(ctrl.LoggerFrom(ctx)).Info(fmt.Sprintf("could not find optional %s '%s'", v.Kind, namespacedName))
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("could not find %s '%s'", v.Kind, namespacedName)
|
||||
}
|
||||
if data, ok := resource.Data[v.GetValuesKey()]; !ok {
|
||||
return nil, fmt.Errorf("missing key '%s' in %s '%s'", v.GetValuesKey(), v.Kind, namespacedName)
|
||||
} else {
|
||||
valuesData = data
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported ValuesReference kind '%s'", v.Kind)
|
||||
}
|
||||
switch v.TargetPath {
|
||||
case "":
|
||||
values, err := chartutil.ReadValues(valuesData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read values from key '%s' in %s '%s': %w", v.GetValuesKey(), v.Kind, namespacedName, err)
|
||||
}
|
||||
result = transform.MergeMaps(result, values)
|
||||
default:
|
||||
// TODO(hidde): this is a bit of hack, as it mimics the way the option string is passed
|
||||
// to Helm from a CLI perspective. Given the parser is however not publicly accessible
|
||||
// while it contains all logic around parsing the target path, it is a fair trade-off.
|
||||
stringValuesData := string(valuesData)
|
||||
const singleQuote = "'"
|
||||
const doubleQuote = "\""
|
||||
var err error
|
||||
if (strings.HasPrefix(stringValuesData, singleQuote) && strings.HasSuffix(stringValuesData, singleQuote)) || (strings.HasPrefix(stringValuesData, doubleQuote) && strings.HasSuffix(stringValuesData, doubleQuote)) {
|
||||
stringValuesData = strings.Trim(stringValuesData, singleQuote+doubleQuote)
|
||||
singleValue := v.TargetPath + "=" + stringValuesData
|
||||
err = strvals.ParseIntoString(singleValue, result)
|
||||
} else {
|
||||
singleValue := v.TargetPath + "=" + stringValuesData
|
||||
err = strvals.ParseInto(singleValue, result)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to merge value from key '%s' in %s '%s' into target path '%s': %w", v.GetValuesKey(), v.Kind, namespacedName, v.TargetPath, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return transform.MergeMaps(result, hr.GetValues()), nil
|
||||
}
|
||||
|
||||
// reconcileDelete deletes the v1beta2.HelmChart of the v2beta1.HelmRelease,
|
||||
// and uninstalls the Helm release if the resource has not been suspended.
|
||||
func (r *HelmReleaseReconciler) reconcileDelete(ctx context.Context, hr v2.HelmRelease) (ctrl.Result, error) {
|
||||
r.recordReadiness(ctx, hr)
|
||||
|
||||
// Delete the HelmChart that belongs to this resource.
|
||||
if err := r.deleteHelmChart(ctx, &hr); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// Only uninstall the Helm Release if the resource is not suspended.
|
||||
if !hr.Spec.Suspend {
|
||||
getter, err := r.buildRESTClientGetter(ctx, hr)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
run, err := runner.NewRunner(getter, hr.GetStorageNamespace(), ctrl.LoggerFrom(ctx))
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
if err := run.Uninstall(hr); err != nil && !errors.Is(err, driver.ErrReleaseNotFound) {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
ctrl.LoggerFrom(ctx).Info("uninstalled Helm release for deleted resource")
|
||||
|
||||
} else {
|
||||
ctrl.LoggerFrom(ctx).Info("skipping Helm uninstall for suspended resource")
|
||||
}
|
||||
|
||||
// Remove our finalizer from the list and update it.
|
||||
controllerutil.RemoveFinalizer(&hr, v2.HelmReleaseFinalizer)
|
||||
if err := r.Update(ctx, &hr); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *HelmReleaseReconciler) handleHelmActionResult(ctx context.Context,
|
||||
hr *v2.HelmRelease, revision string, err error, action string, condition string, succeededReason string, failedReason string) error {
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Helm %s failed: %w", action, err)
|
||||
msg := err.Error()
|
||||
if actionErr := (*runner.ActionError)(nil); errors.As(err, &actionErr) {
|
||||
msg = msg + "\n\nLast Helm logs:\n\n" + actionErr.CapturedLogs
|
||||
}
|
||||
newCondition := metav1.Condition{
|
||||
Type: condition,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: failedReason,
|
||||
Message: msg,
|
||||
}
|
||||
apimeta.SetStatusCondition(hr.GetStatusConditions(), newCondition)
|
||||
r.event(ctx, *hr, revision, eventv1.EventSeverityError, msg)
|
||||
return &ConditionError{Reason: failedReason, Err: err}
|
||||
} else {
|
||||
msg := fmt.Sprintf("Helm %s succeeded", action)
|
||||
newCondition := metav1.Condition{
|
||||
Type: condition,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: succeededReason,
|
||||
Message: msg,
|
||||
}
|
||||
apimeta.SetStatusCondition(hr.GetStatusConditions(), newCondition)
|
||||
r.event(ctx, *hr, revision, eventv1.EventSeverityInfo, msg)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r *HelmReleaseReconciler) patchStatus(ctx context.Context, hr *v2.HelmRelease) error {
|
||||
key := client.ObjectKeyFromObject(hr)
|
||||
latest := &v2.HelmRelease{}
|
||||
if err := r.Client.Get(ctx, key, latest); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.Client.Status().Patch(ctx, hr, client.MergeFrom(latest))
|
||||
}
|
||||
|
||||
func (r *HelmReleaseReconciler) requestsForHelmChartChange(o client.Object) []reconcile.Request {
|
||||
hc, ok := o.(*sourcev1.HelmChart)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Expected a HelmChart, got %T", o))
|
||||
}
|
||||
// If we do not have an artifact, we have no requests to make
|
||||
if hc.GetArtifact() == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
var list v2.HelmReleaseList
|
||||
if err := r.List(ctx, &list, client.MatchingFields{
|
||||
v2.SourceIndexKey: client.ObjectKeyFromObject(hc).String(),
|
||||
}); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var reqs []reconcile.Request
|
||||
for _, i := range list.Items {
|
||||
// If the revision of the artifact equals to the last attempted revision,
|
||||
// we should not make a request for this HelmRelease
|
||||
if hc.GetArtifact().Revision == i.Status.LastAttemptedRevision {
|
||||
continue
|
||||
}
|
||||
reqs = append(reqs, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(&i)})
|
||||
}
|
||||
return reqs
|
||||
}
|
||||
|
||||
// event emits a Kubernetes event and forwards the event to notification controller if configured.
|
||||
func (r *HelmReleaseReconciler) event(_ context.Context, hr v2.HelmRelease, revision, severity, msg string) {
|
||||
var meta map[string]string
|
||||
if revision != "" {
|
||||
meta = map[string]string{v2.GroupVersion.Group + "/revision": revision}
|
||||
}
|
||||
eventtype := "Normal"
|
||||
if severity == eventv1.EventSeverityError {
|
||||
eventtype = "Warning"
|
||||
}
|
||||
r.EventRecorder.AnnotatedEventf(&hr, meta, eventtype, severity, msg)
|
||||
}
|
||||
|
||||
func (r *HelmReleaseReconciler) recordSuspension(ctx context.Context, hr v2.HelmRelease) {
|
||||
if r.MetricsRecorder == nil {
|
||||
return
|
||||
}
|
||||
log := ctrl.LoggerFrom(ctx)
|
||||
|
||||
objRef, err := reference.GetReference(r.Scheme, &hr)
|
||||
if err != nil {
|
||||
log.Error(err, "unable to record suspended metric")
|
||||
return
|
||||
}
|
||||
|
||||
if !hr.DeletionTimestamp.IsZero() {
|
||||
r.MetricsRecorder.RecordSuspend(*objRef, false)
|
||||
} else {
|
||||
r.MetricsRecorder.RecordSuspend(*objRef, hr.Spec.Suspend)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *HelmReleaseReconciler) recordReadiness(ctx context.Context, hr v2.HelmRelease) {
|
||||
if r.MetricsRecorder == nil {
|
||||
return
|
||||
}
|
||||
|
||||
objRef, err := reference.GetReference(r.Scheme, &hr)
|
||||
if err != nil {
|
||||
ctrl.LoggerFrom(ctx).Error(err, "unable to record readiness metric")
|
||||
return
|
||||
}
|
||||
if rc := apimeta.FindStatusCondition(hr.Status.Conditions, meta.ReadyCondition); rc != nil {
|
||||
r.MetricsRecorder.RecordCondition(*objRef, *rc, !hr.DeletionTimestamp.IsZero())
|
||||
} else {
|
||||
r.MetricsRecorder.RecordCondition(*objRef, metav1.Condition{
|
||||
Type: meta.ReadyCondition,
|
||||
Status: metav1.ConditionUnknown,
|
||||
}, !hr.DeletionTimestamp.IsZero())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
Copyright 2020 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 controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/fluxcd/pkg/runtime/acl"
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
|
||||
v2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||
)
|
||||
|
||||
func (r *HelmReleaseReconciler) reconcileChart(ctx context.Context, hr *v2.HelmRelease) (*sourcev1.HelmChart, error) {
|
||||
chartName := types.NamespacedName{
|
||||
Namespace: hr.Spec.Chart.GetNamespace(hr.Namespace),
|
||||
Name: hr.GetHelmChartName(),
|
||||
}
|
||||
|
||||
if r.NoCrossNamespaceRef && chartName.Namespace != hr.Namespace {
|
||||
return nil, acl.AccessDeniedError(fmt.Sprintf("can't access '%s/%s', cross-namespace references have been blocked",
|
||||
hr.Spec.Chart.Spec.SourceRef.Kind, types.NamespacedName{
|
||||
Namespace: hr.Spec.Chart.Spec.SourceRef.Namespace,
|
||||
Name: hr.Spec.Chart.Spec.SourceRef.Name,
|
||||
}))
|
||||
}
|
||||
|
||||
// Garbage collect the previous HelmChart if the namespace named changed.
|
||||
if hr.Status.HelmChart != "" && hr.Status.HelmChart != chartName.String() {
|
||||
if err := r.deleteHelmChart(ctx, hr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Continue with the reconciliation of the current template.
|
||||
var helmChart sourcev1.HelmChart
|
||||
err := r.Client.Get(ctx, chartName, &helmChart)
|
||||
if err != nil && !apierrors.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
hc := buildHelmChartFromTemplate(hr)
|
||||
switch {
|
||||
case apierrors.IsNotFound(err):
|
||||
if err = r.Client.Create(ctx, hc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hr.Status.HelmChart = chartName.String()
|
||||
return hc, nil
|
||||
case helmChartRequiresUpdate(hr, &helmChart):
|
||||
ctrl.LoggerFrom(ctx).Info("chart diverged from template", strings.ToLower(sourcev1.HelmChartKind), chartName.String())
|
||||
helmChart.Spec = hc.Spec
|
||||
if err = r.Client.Update(ctx, &helmChart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hr.Status.HelmChart = chartName.String()
|
||||
}
|
||||
return &helmChart, nil
|
||||
}
|
||||
|
||||
// getHelmChart retrieves the v1beta2.HelmChart for the given
|
||||
// v2beta1.HelmRelease using the name that is advertised in the status
|
||||
// object. It returns the v1beta2.HelmChart, or an error.
|
||||
func (r *HelmReleaseReconciler) getHelmChart(ctx context.Context, hr *v2.HelmRelease) (*sourcev1.HelmChart, error) {
|
||||
namespace, name := hr.Status.GetHelmChart()
|
||||
hc := &sourcev1.HelmChart{}
|
||||
if err := r.Client.Get(ctx, types.NamespacedName{Namespace: namespace, Name: name}, hc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return hc, nil
|
||||
}
|
||||
|
||||
// loadHelmChart attempts to download the artifact from the provided source,
|
||||
// loads it into a chart.Chart, and removes the downloaded artifact.
|
||||
// It returns the loaded chart.Chart on success, or an error.
|
||||
func (r *HelmReleaseReconciler) loadHelmChart(source *sourcev1.HelmChart) (*chart.Chart, error) {
|
||||
f, err := os.CreateTemp("", fmt.Sprintf("%s-%s-*.tgz", source.GetNamespace(), source.GetName()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
artifactURL := source.GetArtifact().URL
|
||||
if hostname := os.Getenv("SOURCE_CONTROLLER_LOCALHOST"); hostname != "" {
|
||||
u, err := url.Parse(artifactURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Host = hostname
|
||||
artifactURL = u.String()
|
||||
}
|
||||
|
||||
req, err := retryablehttp.NewRequest(http.MethodGet, artifactURL, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create a new request: %w", err)
|
||||
}
|
||||
|
||||
resp, err := r.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to download artifact, error: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("artifact '%s' download failed (status code: %s)", source.GetArtifact().URL, resp.Status)
|
||||
}
|
||||
|
||||
// verify checksum matches origin
|
||||
if err := r.copyAndVerifyArtifact(source.GetArtifact(), resp.Body, f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return loader.Load(f.Name())
|
||||
}
|
||||
|
||||
func (r *HelmReleaseReconciler) copyAndVerifyArtifact(artifact *sourcev1.Artifact, reader io.Reader, writer io.Writer) error {
|
||||
hasher := sha256.New()
|
||||
|
||||
// for backwards compatibility with source-controller v0.17.2 and older
|
||||
if len(artifact.Checksum) == 40 {
|
||||
hasher = sha1.New()
|
||||
}
|
||||
|
||||
// compute checksum
|
||||
mw := io.MultiWriter(hasher, writer)
|
||||
if _, err := io.Copy(mw, reader); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if checksum := fmt.Sprintf("%x", hasher.Sum(nil)); checksum != artifact.Checksum {
|
||||
return fmt.Errorf("failed to verify artifact: computed checksum '%s' doesn't match advertised '%s'",
|
||||
checksum, artifact.Checksum)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// deleteHelmChart deletes the v1beta2.HelmChart of the v2beta1.HelmRelease.
|
||||
func (r *HelmReleaseReconciler) deleteHelmChart(ctx context.Context, hr *v2.HelmRelease) error {
|
||||
if hr.Status.HelmChart == "" {
|
||||
return nil
|
||||
}
|
||||
var hc sourcev1.HelmChart
|
||||
chartNS, chartName := hr.Status.GetHelmChart()
|
||||
err := r.Client.Get(ctx, types.NamespacedName{Namespace: chartNS, Name: chartName}, &hc)
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
hr.Status.HelmChart = ""
|
||||
return nil
|
||||
}
|
||||
err = fmt.Errorf("failed to delete HelmChart '%s': %w", hr.Status.HelmChart, err)
|
||||
return err
|
||||
}
|
||||
if err = r.Client.Delete(ctx, &hc); err != nil {
|
||||
err = fmt.Errorf("failed to delete HelmChart '%s': %w", hr.Status.HelmChart, err)
|
||||
return err
|
||||
}
|
||||
// Truncate the chart reference in the status object.
|
||||
hr.Status.HelmChart = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildHelmChartFromTemplate builds a v1beta2.HelmChart from the
|
||||
// v2beta1.HelmChartTemplate of the given v2beta1.HelmRelease.
|
||||
func buildHelmChartFromTemplate(hr *v2.HelmRelease) *sourcev1.HelmChart {
|
||||
template := hr.Spec.Chart
|
||||
return &sourcev1.HelmChart{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: hr.GetHelmChartName(),
|
||||
Namespace: hr.Spec.Chart.GetNamespace(hr.Namespace),
|
||||
},
|
||||
Spec: sourcev1.HelmChartSpec{
|
||||
Chart: template.Spec.Chart,
|
||||
Version: template.Spec.Version,
|
||||
SourceRef: sourcev1.LocalHelmChartSourceReference{
|
||||
Name: template.Spec.SourceRef.Name,
|
||||
Kind: template.Spec.SourceRef.Kind,
|
||||
},
|
||||
Interval: template.GetInterval(hr.Spec.Interval),
|
||||
ReconcileStrategy: template.Spec.ReconcileStrategy,
|
||||
ValuesFiles: template.Spec.ValuesFiles,
|
||||
ValuesFile: template.Spec.ValuesFile,
|
||||
Verify: templateVerificationToSourceVerification(template.Spec.Verify),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// helmChartRequiresUpdate compares the v2beta1.HelmChartTemplate of the
|
||||
// v2beta1.HelmRelease to the given v1beta2.HelmChart to determine if an
|
||||
// update is required.
|
||||
func helmChartRequiresUpdate(hr *v2.HelmRelease, chart *sourcev1.HelmChart) bool {
|
||||
template := hr.Spec.Chart
|
||||
switch {
|
||||
case template.Spec.Chart != chart.Spec.Chart:
|
||||
return true
|
||||
// TODO(hidde): remove emptiness checks on next MINOR version
|
||||
case template.Spec.Version == "" && chart.Spec.Version != "*",
|
||||
template.Spec.Version != "" && template.Spec.Version != chart.Spec.Version:
|
||||
return true
|
||||
case template.Spec.SourceRef.Name != chart.Spec.SourceRef.Name:
|
||||
return true
|
||||
case template.Spec.SourceRef.Kind != chart.Spec.SourceRef.Kind:
|
||||
return true
|
||||
case template.GetInterval(hr.Spec.Interval) != chart.Spec.Interval:
|
||||
return true
|
||||
case template.Spec.ReconcileStrategy != chart.Spec.ReconcileStrategy:
|
||||
return true
|
||||
case !reflect.DeepEqual(template.Spec.ValuesFiles, chart.Spec.ValuesFiles):
|
||||
return true
|
||||
case template.Spec.ValuesFile != chart.Spec.ValuesFile:
|
||||
return true
|
||||
case !reflect.DeepEqual(templateVerificationToSourceVerification(template.Spec.Verify), chart.Spec.Verify):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// templateVerificationToSourceVerification converts the HelmChartTemplateVerification to the OCIRepositoryVerification.
|
||||
func templateVerificationToSourceVerification(template *v2.HelmChartTemplateVerification) *sourcev1.OCIRepositoryVerification {
|
||||
if template == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &sourcev1.OCIRepositoryVerification{
|
||||
Provider: template.Provider,
|
||||
SecretRef: template.SecretRef,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,533 @@
|
|||
/*
|
||||
Copyright 2020 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 controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
"github.com/go-logr/logr"
|
||||
. "github.com/onsi/gomega"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
v2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||
)
|
||||
|
||||
func TestHelmReleaseReconciler_reconcileChart(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
hr *v2.HelmRelease
|
||||
hc *sourcev1.HelmChart
|
||||
expectHelmChartStatus string
|
||||
expectGC bool
|
||||
expectErr bool
|
||||
noCrossNamspaceRef bool
|
||||
}{
|
||||
{
|
||||
name: "new HelmChart",
|
||||
hr: &v2.HelmRelease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-release",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: v2.HelmReleaseSpec{
|
||||
Interval: metav1.Duration{Duration: time.Minute},
|
||||
Chart: v2.HelmChartTemplate{
|
||||
Spec: v2.HelmChartTemplateSpec{
|
||||
Chart: "chart",
|
||||
SourceRef: v2.CrossNamespaceObjectReference{
|
||||
Name: "test-repository",
|
||||
Kind: "HelmRepository",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
hc: nil,
|
||||
expectHelmChartStatus: "default/default-test-release",
|
||||
},
|
||||
{
|
||||
name: "existing HelmChart",
|
||||
hr: &v2.HelmRelease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-release",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: v2.HelmReleaseSpec{
|
||||
Interval: metav1.Duration{Duration: time.Minute},
|
||||
Chart: v2.HelmChartTemplate{
|
||||
Spec: v2.HelmChartTemplateSpec{
|
||||
Chart: "chart",
|
||||
SourceRef: v2.CrossNamespaceObjectReference{
|
||||
Name: "test-repository",
|
||||
Kind: "HelmRepository",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
hc: &sourcev1.HelmChart{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "default-test-release",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: sourcev1.HelmChartSpec{
|
||||
Chart: "chart",
|
||||
SourceRef: sourcev1.LocalHelmChartSourceReference{
|
||||
Name: "test-repository",
|
||||
Kind: "HelmRepository",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectHelmChartStatus: "default/default-test-release",
|
||||
},
|
||||
{
|
||||
name: "modified HelmChart",
|
||||
hr: &v2.HelmRelease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-release",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: v2.HelmReleaseSpec{
|
||||
Interval: metav1.Duration{Duration: time.Minute},
|
||||
Chart: v2.HelmChartTemplate{
|
||||
Spec: v2.HelmChartTemplateSpec{
|
||||
Chart: "chart",
|
||||
SourceRef: v2.CrossNamespaceObjectReference{
|
||||
Name: "test-repository",
|
||||
Kind: "HelmRepository",
|
||||
Namespace: "cross",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: v2.HelmReleaseStatus{
|
||||
HelmChart: "default/default-test-release",
|
||||
},
|
||||
},
|
||||
hc: &sourcev1.HelmChart{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "default-test-release",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: sourcev1.HelmChartSpec{
|
||||
Chart: "chart",
|
||||
SourceRef: sourcev1.LocalHelmChartSourceReference{
|
||||
Name: "test-repository",
|
||||
Kind: "HelmRepository",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectHelmChartStatus: "cross/default-test-release",
|
||||
expectGC: true,
|
||||
},
|
||||
{
|
||||
name: "block cross namespace access when flag is set",
|
||||
hr: &v2.HelmRelease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-release",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: v2.HelmReleaseSpec{
|
||||
Interval: metav1.Duration{Duration: time.Minute},
|
||||
Chart: v2.HelmChartTemplate{
|
||||
Spec: v2.HelmChartTemplateSpec{
|
||||
Chart: "chart",
|
||||
SourceRef: v2.CrossNamespaceObjectReference{
|
||||
Name: "test-repository",
|
||||
Kind: "HelmRepository",
|
||||
Namespace: "cross",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: v2.HelmReleaseStatus{
|
||||
HelmChart: "",
|
||||
},
|
||||
},
|
||||
noCrossNamspaceRef: true,
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
g.Expect(v2.AddToScheme(scheme.Scheme)).To(Succeed())
|
||||
g.Expect(sourcev1.AddToScheme(scheme.Scheme)).To(Succeed())
|
||||
|
||||
var c client.Client
|
||||
if tt.hc != nil {
|
||||
c = fake.NewFakeClientWithScheme(scheme.Scheme, tt.hc)
|
||||
} else {
|
||||
c = fake.NewFakeClientWithScheme(scheme.Scheme)
|
||||
}
|
||||
|
||||
r := &HelmReleaseReconciler{
|
||||
Client: c,
|
||||
NoCrossNamespaceRef: tt.noCrossNamspaceRef,
|
||||
}
|
||||
|
||||
hc, err := r.reconcileChart(logr.NewContext(context.TODO(), logr.Discard()), tt.hr)
|
||||
if tt.expectErr {
|
||||
g.Expect(err).To(HaveOccurred())
|
||||
g.Expect(hc).To(BeNil())
|
||||
} else {
|
||||
g.Expect(err).NotTo(HaveOccurred())
|
||||
g.Expect(hc).NotTo(BeNil())
|
||||
}
|
||||
|
||||
g.Expect(tt.hr.Status.HelmChart).To(Equal(tt.expectHelmChartStatus))
|
||||
|
||||
if tt.expectGC {
|
||||
objKey := client.ObjectKeyFromObject(tt.hc)
|
||||
err = c.Get(context.TODO(), objKey, tt.hc.DeepCopy())
|
||||
g.Expect(apierrors.IsNotFound(err)).To(BeTrue())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHelmReleaseReconciler_deleteHelmChart(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
hc *sourcev1.HelmChart
|
||||
hr *v2.HelmRelease
|
||||
expectHelmChartStatus string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "delete existing HelmChart",
|
||||
hc: &sourcev1.HelmChart{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-chart",
|
||||
Namespace: "default",
|
||||
},
|
||||
},
|
||||
hr: &v2.HelmRelease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-release",
|
||||
},
|
||||
Status: v2.HelmReleaseStatus{
|
||||
HelmChart: "default/test-chart",
|
||||
},
|
||||
},
|
||||
expectHelmChartStatus: "",
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "delete already removed HelmChart",
|
||||
hc: nil,
|
||||
hr: &v2.HelmRelease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-release",
|
||||
},
|
||||
Status: v2.HelmReleaseStatus{
|
||||
HelmChart: "default/test-chart",
|
||||
},
|
||||
},
|
||||
expectHelmChartStatus: "",
|
||||
expectErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
g.Expect(v2.AddToScheme(scheme.Scheme)).To(Succeed())
|
||||
g.Expect(sourcev1.AddToScheme(scheme.Scheme)).To(Succeed())
|
||||
|
||||
var c client.Client
|
||||
if tt.hc != nil {
|
||||
c = fake.NewFakeClientWithScheme(scheme.Scheme, tt.hc)
|
||||
} else {
|
||||
c = fake.NewFakeClientWithScheme(scheme.Scheme)
|
||||
}
|
||||
|
||||
r := &HelmReleaseReconciler{
|
||||
Client: c,
|
||||
}
|
||||
|
||||
err := r.deleteHelmChart(context.TODO(), tt.hr)
|
||||
if tt.expectErr {
|
||||
g.Expect(err).To(HaveOccurred())
|
||||
} else {
|
||||
g.Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
g.Expect(tt.hr.Status.HelmChart).To(Equal(tt.expectHelmChartStatus))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_buildHelmChartFromTemplate(t *testing.T) {
|
||||
hrWithChartTemplate := v2.HelmRelease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-release",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: v2.HelmReleaseSpec{
|
||||
Interval: metav1.Duration{Duration: time.Minute},
|
||||
Chart: v2.HelmChartTemplate{
|
||||
Spec: v2.HelmChartTemplateSpec{
|
||||
Chart: "chart",
|
||||
Version: "1.0.0",
|
||||
SourceRef: v2.CrossNamespaceObjectReference{
|
||||
Name: "test-repository",
|
||||
Kind: "HelmRepository",
|
||||
},
|
||||
Interval: &metav1.Duration{Duration: 2 * time.Minute},
|
||||
ValuesFiles: []string{"values.yaml"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
modify func(release *v2.HelmRelease)
|
||||
want *sourcev1.HelmChart
|
||||
}{
|
||||
{
|
||||
name: "builds HelmChart from HelmChartTemplate",
|
||||
modify: func(*v2.HelmRelease) {},
|
||||
want: &sourcev1.HelmChart{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "default-test-release",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: sourcev1.HelmChartSpec{
|
||||
Chart: "chart",
|
||||
Version: "1.0.0",
|
||||
SourceRef: sourcev1.LocalHelmChartSourceReference{
|
||||
Name: "test-repository",
|
||||
Kind: "HelmRepository",
|
||||
},
|
||||
Interval: metav1.Duration{Duration: 2 * time.Minute},
|
||||
ValuesFiles: []string{"values.yaml"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "takes SourceRef namespace into account",
|
||||
modify: func(hr *v2.HelmRelease) {
|
||||
hr.Spec.Chart.Spec.SourceRef.Namespace = "cross"
|
||||
},
|
||||
want: &sourcev1.HelmChart{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "default-test-release",
|
||||
Namespace: "cross",
|
||||
},
|
||||
Spec: sourcev1.HelmChartSpec{
|
||||
Chart: "chart",
|
||||
Version: "1.0.0",
|
||||
SourceRef: sourcev1.LocalHelmChartSourceReference{
|
||||
Name: "test-repository",
|
||||
Kind: "HelmRepository",
|
||||
},
|
||||
Interval: metav1.Duration{Duration: 2 * time.Minute},
|
||||
ValuesFiles: []string{"values.yaml"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "falls back to HelmRelease interval",
|
||||
modify: func(hr *v2.HelmRelease) {
|
||||
hr.Spec.Chart.Spec.Interval = nil
|
||||
},
|
||||
want: &sourcev1.HelmChart{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "default-test-release",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: sourcev1.HelmChartSpec{
|
||||
Chart: "chart",
|
||||
Version: "1.0.0",
|
||||
SourceRef: sourcev1.LocalHelmChartSourceReference{
|
||||
Name: "test-repository",
|
||||
Kind: "HelmRepository",
|
||||
},
|
||||
Interval: metav1.Duration{Duration: time.Minute},
|
||||
ValuesFiles: []string{"values.yaml"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "take cosign verification into account",
|
||||
modify: func(hr *v2.HelmRelease) {
|
||||
hr.Spec.Chart.Spec.Verify = &v2.HelmChartTemplateVerification{
|
||||
Provider: "cosign",
|
||||
SecretRef: &meta.LocalObjectReference{
|
||||
Name: "cosign-key",
|
||||
},
|
||||
}
|
||||
},
|
||||
want: &sourcev1.HelmChart{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "default-test-release",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: sourcev1.HelmChartSpec{
|
||||
Chart: "chart",
|
||||
Version: "1.0.0",
|
||||
SourceRef: sourcev1.LocalHelmChartSourceReference{
|
||||
Name: "test-repository",
|
||||
Kind: "HelmRepository",
|
||||
},
|
||||
Interval: metav1.Duration{Duration: 2 * time.Minute},
|
||||
ValuesFiles: []string{"values.yaml"},
|
||||
Verify: &sourcev1.OCIRepositoryVerification{
|
||||
Provider: "cosign",
|
||||
SecretRef: &meta.LocalObjectReference{
|
||||
Name: "cosign-key",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
hr := hrWithChartTemplate.DeepCopy()
|
||||
tt.modify(hr)
|
||||
g.Expect(buildHelmChartFromTemplate(hr)).To(Equal(tt.want))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_helmChartRequiresUpdate(t *testing.T) {
|
||||
hrWithChartTemplate := v2.HelmRelease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-release",
|
||||
},
|
||||
Spec: v2.HelmReleaseSpec{
|
||||
Interval: metav1.Duration{Duration: time.Minute},
|
||||
Chart: v2.HelmChartTemplate{
|
||||
Spec: v2.HelmChartTemplateSpec{
|
||||
Chart: "chart",
|
||||
Version: "1.0.0",
|
||||
SourceRef: v2.CrossNamespaceObjectReference{
|
||||
Name: "test-repository",
|
||||
Kind: "HelmRepository",
|
||||
},
|
||||
Interval: &metav1.Duration{Duration: 2 * time.Minute},
|
||||
Verify: &v2.HelmChartTemplateVerification{
|
||||
Provider: "cosign",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
modify func(*v2.HelmRelease, *sourcev1.HelmChart)
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "detects no change",
|
||||
modify: func(*v2.HelmRelease, *sourcev1.HelmChart) {},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "detects chart change",
|
||||
modify: func(hr *v2.HelmRelease, hc *sourcev1.HelmChart) {
|
||||
hr.Spec.Chart.Spec.Chart = "new"
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "detects version change",
|
||||
modify: func(hr *v2.HelmRelease, hc *sourcev1.HelmChart) {
|
||||
hr.Spec.Chart.Spec.Version = "2.0.0"
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "detects chart source name change",
|
||||
modify: func(hr *v2.HelmRelease, hc *sourcev1.HelmChart) {
|
||||
hr.Spec.Chart.Spec.SourceRef.Name = "new"
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "detects chart source kind change",
|
||||
modify: func(hr *v2.HelmRelease, hc *sourcev1.HelmChart) {
|
||||
hr.Spec.Chart.Spec.SourceRef.Kind = "GitRepository"
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "detects interval change",
|
||||
modify: func(hr *v2.HelmRelease, hc *sourcev1.HelmChart) {
|
||||
hr.Spec.Chart.Spec.Interval = nil
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "detects reconcile strategy change",
|
||||
modify: func(hr *v2.HelmRelease, hc *sourcev1.HelmChart) {
|
||||
hr.Spec.Chart.Spec.ReconcileStrategy = "Revision"
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "detects values files change",
|
||||
modify: func(hr *v2.HelmRelease, hc *sourcev1.HelmChart) {
|
||||
hr.Spec.Chart.Spec.ValuesFiles = []string{"values-prod.yaml"}
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "detects values file change",
|
||||
modify: func(hr *v2.HelmRelease, hc *sourcev1.HelmChart) {
|
||||
hr.Spec.Chart.Spec.ValuesFile = "values-prod.yaml"
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "detects verify change",
|
||||
modify: func(hr *v2.HelmRelease, hc *sourcev1.HelmChart) {
|
||||
hr.Spec.Chart.Spec.Verify.Provider = "foo-bar"
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
hr := hrWithChartTemplate.DeepCopy()
|
||||
hc := buildHelmChartFromTemplate(hr)
|
||||
// second copy to avoid modifying the original
|
||||
hr = hrWithChartTemplate.DeepCopy()
|
||||
g.Expect(helmChartRequiresUpdate(hr, hc)).To(Equal(false))
|
||||
|
||||
tt.modify(hr, hc)
|
||||
fmt.Println("verify", hr.Spec.Chart.Spec.Verify.Provider, hc.Spec.Verify.Provider)
|
||||
g.Expect(helmChartRequiresUpdate(hr, hc)).To(Equal(tt.want))
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,273 @@
|
|||
//go:build gofuzz_libfuzzer
|
||||
// +build gofuzz_libfuzzer
|
||||
|
||||
/*
|
||||
Copyright 2020 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 controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
v2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
)
|
||||
|
||||
func FuzzHelmReleaseReconciler_composeValues(f *testing.F) {
|
||||
scheme := testScheme()
|
||||
|
||||
tests := []struct {
|
||||
targetPath string
|
||||
valuesKey string
|
||||
hrValues string
|
||||
createObject bool
|
||||
secretData []byte
|
||||
configData string
|
||||
}{
|
||||
{
|
||||
targetPath: "flat",
|
||||
valuesKey: "custom-values.yaml",
|
||||
secretData: []byte(`flat:
|
||||
nested: value
|
||||
nested: value
|
||||
`),
|
||||
configData: `flat: value
|
||||
nested:
|
||||
configuration: value
|
||||
`,
|
||||
hrValues: `
|
||||
other: values
|
||||
`,
|
||||
createObject: true,
|
||||
},
|
||||
{
|
||||
targetPath: "'flat'",
|
||||
valuesKey: "custom-values.yaml",
|
||||
secretData: []byte(`flat:
|
||||
nested: value
|
||||
nested: value
|
||||
`),
|
||||
configData: `flat: value
|
||||
nested:
|
||||
configuration: value
|
||||
`,
|
||||
hrValues: `
|
||||
other: values
|
||||
`,
|
||||
createObject: true,
|
||||
},
|
||||
{
|
||||
targetPath: "flat[0]",
|
||||
secretData: []byte(``),
|
||||
configData: `flat: value`,
|
||||
hrValues: `
|
||||
other: values
|
||||
`,
|
||||
createObject: true,
|
||||
},
|
||||
{
|
||||
secretData: []byte(`flat:
|
||||
nested: value
|
||||
nested: value
|
||||
`),
|
||||
configData: `flat: value
|
||||
nested:
|
||||
configuration: value
|
||||
`,
|
||||
hrValues: `
|
||||
other: values
|
||||
`,
|
||||
createObject: true,
|
||||
},
|
||||
{
|
||||
targetPath: "some-value",
|
||||
hrValues: `
|
||||
other: values
|
||||
`,
|
||||
createObject: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
f.Add(tt.targetPath, tt.valuesKey, tt.hrValues, tt.createObject, tt.secretData, tt.configData)
|
||||
}
|
||||
|
||||
f.Fuzz(func(t *testing.T,
|
||||
targetPath, valuesKey, hrValues string, createObject bool, secretData []byte, configData string) {
|
||||
|
||||
// objectName represents a core Kubernetes name (Secret/ConfigMap) which is validated
|
||||
// upstream, and also validated by us in the OpenAPI-based validation set in
|
||||
// v2.ValuesReference. Therefore a static value here suffices, and instead we just
|
||||
// play with the objects presence/absence.
|
||||
objectName := "values"
|
||||
resources := []runtime.Object{}
|
||||
|
||||
if createObject {
|
||||
resources = append(resources,
|
||||
valuesConfigMap(objectName, map[string]string{valuesKey: configData}),
|
||||
valuesSecret(objectName, map[string][]byte{valuesKey: secretData}),
|
||||
)
|
||||
}
|
||||
|
||||
references := []v2.ValuesReference{
|
||||
{
|
||||
Kind: "ConfigMap",
|
||||
Name: objectName,
|
||||
ValuesKey: valuesKey,
|
||||
TargetPath: targetPath,
|
||||
},
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: objectName,
|
||||
ValuesKey: valuesKey,
|
||||
TargetPath: targetPath,
|
||||
},
|
||||
}
|
||||
|
||||
c := fake.NewFakeClientWithScheme(scheme, resources...)
|
||||
r := &HelmReleaseReconciler{Client: c}
|
||||
var values *apiextensionsv1.JSON
|
||||
if hrValues != "" {
|
||||
v, _ := yaml.YAMLToJSON([]byte(hrValues))
|
||||
values = &apiextensionsv1.JSON{Raw: v}
|
||||
}
|
||||
|
||||
hr := v2.HelmRelease{
|
||||
Spec: v2.HelmReleaseSpec{
|
||||
ValuesFrom: references,
|
||||
Values: values,
|
||||
},
|
||||
}
|
||||
|
||||
// OpenAPI-based validation on schema is not verified here.
|
||||
// Therefore some false positives may be arise, as the apiserver
|
||||
// would not allow such values to make their way into the control plane.
|
||||
//
|
||||
// Testenv could be used so the fuzzing covers the entire E2E.
|
||||
// The downsize being the resource and time cost per test would be a lot higher.
|
||||
//
|
||||
// Another approach could be to add validation to reject invalid inputs before
|
||||
// the r.composeValues call.
|
||||
_, _ = r.composeValues(logr.NewContext(context.TODO(), logr.Discard()), hr)
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzHelmReleaseReconciler_reconcile(f *testing.F) {
|
||||
scheme := testScheme()
|
||||
tests := []struct {
|
||||
valuesKey string
|
||||
hrValues string
|
||||
secretData []byte
|
||||
configData string
|
||||
}{
|
||||
{
|
||||
valuesKey: "custom-values.yaml",
|
||||
secretData: []byte(`flat:
|
||||
nested: value
|
||||
nested: value
|
||||
`),
|
||||
configData: `flat: value
|
||||
nested:
|
||||
configuration: value
|
||||
`,
|
||||
hrValues: `
|
||||
other: values
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
f.Add(tt.valuesKey, tt.hrValues, tt.secretData, tt.configData)
|
||||
}
|
||||
|
||||
f.Fuzz(func(t *testing.T,
|
||||
valuesKey, hrValues string, secretData []byte, configData string) {
|
||||
|
||||
var values *apiextensionsv1.JSON
|
||||
if hrValues != "" {
|
||||
v, _ := yaml.YAMLToJSON([]byte(hrValues))
|
||||
values = &apiextensionsv1.JSON{Raw: v}
|
||||
}
|
||||
|
||||
hr := v2.HelmRelease{
|
||||
Spec: v2.HelmReleaseSpec{
|
||||
Values: values,
|
||||
},
|
||||
}
|
||||
|
||||
hc := sourcev1.HelmChart{}
|
||||
hc.ObjectMeta.Name = hr.GetHelmChartName()
|
||||
hc.ObjectMeta.Namespace = hr.Spec.Chart.GetNamespace(hr.Namespace)
|
||||
|
||||
resources := []runtime.Object{
|
||||
valuesConfigMap("values", map[string]string{valuesKey: configData}),
|
||||
valuesSecret("values", map[string][]byte{valuesKey: secretData}),
|
||||
&hc,
|
||||
}
|
||||
|
||||
c := fake.NewFakeClientWithScheme(scheme, resources...)
|
||||
r := &HelmReleaseReconciler{
|
||||
Client: c,
|
||||
EventRecorder: &DummyRecorder{},
|
||||
}
|
||||
|
||||
_, _, _ = r.reconcile(logr.NewContext(context.TODO(), logr.Discard()), hr)
|
||||
})
|
||||
}
|
||||
|
||||
func valuesSecret(name string, data map[string][]byte) *corev1.Secret {
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: name},
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func valuesConfigMap(name string, data map[string]string) *corev1.ConfigMap {
|
||||
return &corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: name},
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func testScheme() *runtime.Scheme {
|
||||
scheme := runtime.NewScheme()
|
||||
_ = corev1.AddToScheme(scheme)
|
||||
_ = v2.AddToScheme(scheme)
|
||||
_ = sourcev1.AddToScheme(scheme)
|
||||
return scheme
|
||||
}
|
||||
|
||||
// DummyRecorder serves as a dummy for kuberecorder.EventRecorder.
|
||||
type DummyRecorder struct{}
|
||||
|
||||
func (r *DummyRecorder) Event(object runtime.Object, eventtype, reason, message string) {
|
||||
}
|
||||
|
||||
func (r *DummyRecorder) Eventf(object runtime.Object, eventtype, reason, messageFmt string, args ...interface{}) {
|
||||
}
|
||||
|
||||
func (r *DummyRecorder) AnnotatedEventf(object runtime.Object, annotations map[string]string,
|
||||
eventtype, reason string, messageFmt string, args ...interface{}) {
|
||||
}
|
|
@ -0,0 +1,461 @@
|
|||
/*
|
||||
Copyright 2020 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 controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
v2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||
)
|
||||
|
||||
func TestHelmReleaseReconciler_composeValues(t *testing.T) {
|
||||
scheme := runtime.NewScheme()
|
||||
_ = corev1.AddToScheme(scheme)
|
||||
_ = v2.AddToScheme(scheme)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
resources []runtime.Object
|
||||
references []v2.ValuesReference
|
||||
values string
|
||||
want chartutil.Values
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "merges",
|
||||
resources: []runtime.Object{
|
||||
valuesConfigMap("values", map[string]string{
|
||||
"values.yaml": `flat: value
|
||||
nested:
|
||||
configuration: value
|
||||
`,
|
||||
}),
|
||||
valuesSecret("values", map[string][]byte{
|
||||
"values.yaml": []byte(`flat:
|
||||
nested: value
|
||||
nested: value
|
||||
`),
|
||||
}),
|
||||
},
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "ConfigMap",
|
||||
Name: "values",
|
||||
},
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: "values",
|
||||
},
|
||||
},
|
||||
values: `
|
||||
other: values
|
||||
`,
|
||||
want: chartutil.Values{
|
||||
"flat": map[string]interface{}{
|
||||
"nested": "value",
|
||||
},
|
||||
"nested": "value",
|
||||
"other": "values",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "target path",
|
||||
resources: []runtime.Object{
|
||||
valuesSecret("values", map[string][]byte{"single": []byte("value")}),
|
||||
},
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: "values",
|
||||
ValuesKey: "single",
|
||||
TargetPath: "merge.at.specific.path",
|
||||
},
|
||||
},
|
||||
want: chartutil.Values{
|
||||
"merge": map[string]interface{}{
|
||||
"at": map[string]interface{}{
|
||||
"specific": map[string]interface{}{
|
||||
"path": "value",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "target path with boolean value",
|
||||
resources: []runtime.Object{
|
||||
valuesSecret("values", map[string][]byte{"single": []byte("true")}),
|
||||
},
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: "values",
|
||||
ValuesKey: "single",
|
||||
TargetPath: "merge.at.specific.path",
|
||||
},
|
||||
},
|
||||
want: chartutil.Values{
|
||||
"merge": map[string]interface{}{
|
||||
"at": map[string]interface{}{
|
||||
"specific": map[string]interface{}{
|
||||
"path": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "target path with set-string behavior",
|
||||
resources: []runtime.Object{
|
||||
valuesSecret("values", map[string][]byte{"single": []byte("\"true\"")}),
|
||||
},
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: "values",
|
||||
ValuesKey: "single",
|
||||
TargetPath: "merge.at.specific.path",
|
||||
},
|
||||
},
|
||||
want: chartutil.Values{
|
||||
"merge": map[string]interface{}{
|
||||
"at": map[string]interface{}{
|
||||
"specific": map[string]interface{}{
|
||||
"path": "true",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "values reference to non existing secret",
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: "missing",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "optional values reference to non existing secret",
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: "missing",
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
want: chartutil.Values{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "values reference to non existing config map",
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "ConfigMap",
|
||||
Name: "missing",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "optional values reference to non existing config map",
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "ConfigMap",
|
||||
Name: "missing",
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
want: chartutil.Values{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "missing secret key",
|
||||
resources: []runtime.Object{
|
||||
valuesSecret("values", nil),
|
||||
},
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: "values",
|
||||
ValuesKey: "nonexisting",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing config map key",
|
||||
resources: []runtime.Object{
|
||||
valuesConfigMap("values", nil),
|
||||
},
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "ConfigMap",
|
||||
Name: "values",
|
||||
ValuesKey: "nonexisting",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "unsupported values reference kind",
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "Unsupported",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid values",
|
||||
resources: []runtime.Object{
|
||||
valuesConfigMap("values", map[string]string{
|
||||
"values.yaml": `
|
||||
invalid`,
|
||||
}),
|
||||
},
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "ConfigMap",
|
||||
Name: "values",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := fake.NewFakeClientWithScheme(scheme, tt.resources...)
|
||||
r := &HelmReleaseReconciler{Client: c}
|
||||
var values *apiextensionsv1.JSON
|
||||
if tt.values != "" {
|
||||
v, _ := yaml.YAMLToJSON([]byte(tt.values))
|
||||
values = &apiextensionsv1.JSON{Raw: v}
|
||||
}
|
||||
hr := v2.HelmRelease{
|
||||
Spec: v2.HelmReleaseSpec{
|
||||
ValuesFrom: tt.references,
|
||||
Values: values,
|
||||
},
|
||||
}
|
||||
got, err := r.composeValues(logr.NewContext(context.TODO(), logr.Discard()), hr)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("composeValues() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("composeValues() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValuesReferenceValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
references []v2.ValuesReference
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid ValuesKey",
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: "values",
|
||||
ValuesKey: "any-key_na.me",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid ValuesKey: empty",
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: "values",
|
||||
ValuesKey: "",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid ValuesKey: long",
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: "values",
|
||||
ValuesKey: strings.Repeat("a", 253),
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid ValuesKey",
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: "values",
|
||||
ValuesKey: "a($&^%b",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid ValuesKey: too long",
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: "values",
|
||||
ValuesKey: strings.Repeat("a", 254),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid target path: empty",
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: "values",
|
||||
TargetPath: "",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid target path",
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: "values",
|
||||
TargetPath: "list_with.nested-values.and.index[0]",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid target path: long",
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: "values",
|
||||
TargetPath: strings.Repeat("a", 250),
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid target path: too long",
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: "values",
|
||||
TargetPath: strings.Repeat("a", 251),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid target path: opened index",
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: "values",
|
||||
ValuesKey: "single",
|
||||
TargetPath: "a[",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid target path: incorrect index syntax",
|
||||
references: []v2.ValuesReference{
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: "values",
|
||||
ValuesKey: "single",
|
||||
TargetPath: "a]0[",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var values *apiextensionsv1.JSON
|
||||
v, _ := yaml.YAMLToJSON([]byte("values"))
|
||||
values = &apiextensionsv1.JSON{Raw: v}
|
||||
|
||||
hr := v2.HelmRelease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: v2.HelmReleaseSpec{
|
||||
Interval: metav1.Duration{Duration: 5 * time.Minute},
|
||||
Chart: v2.HelmChartTemplate{
|
||||
Spec: v2.HelmChartTemplateSpec{
|
||||
SourceRef: v2.CrossNamespaceObjectReference{
|
||||
Name: "something",
|
||||
},
|
||||
},
|
||||
},
|
||||
ValuesFrom: tt.references,
|
||||
Values: values,
|
||||
},
|
||||
}
|
||||
|
||||
err := k8sClient.Create(context.TODO(), &hr, client.DryRunAll)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("composeValues() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func valuesSecret(name string, data map[string][]byte) *corev1.Secret {
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: name},
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func valuesConfigMap(name string, data map[string]string) *corev1.ConfigMap {
|
||||
return &corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: name},
|
||||
Data: data,
|
||||
}
|
||||
}
|
|
@ -14,18 +14,15 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package predicates
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/fluxcd/pkg/runtime/conditions"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
)
|
||||
|
||||
// SourceRevisionChangePredicate detects revision changes to the v1.Artifact of
|
||||
// a v1.Source object.
|
||||
type SourceRevisionChangePredicate struct {
|
||||
predicate.Funcs
|
||||
}
|
||||
|
@ -50,21 +47,7 @@ func (SourceRevisionChangePredicate) Update(e event.UpdateEvent) bool {
|
|||
}
|
||||
|
||||
if oldSource.GetArtifact() != nil && newSource.GetArtifact() != nil &&
|
||||
!oldSource.GetArtifact().HasRevision(newSource.GetArtifact().Revision) {
|
||||
return true
|
||||
}
|
||||
|
||||
oldConditions, ok := e.ObjectOld.(conditions.Getter)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
newConditions, ok := e.ObjectNew.(conditions.Getter)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if !conditions.IsReady(oldConditions) && conditions.IsReady(newConditions) {
|
||||
oldSource.GetArtifact().Revision != newSource.GetArtifact().Revision {
|
||||
return true
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
Copyright 2020 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 controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
|
||||
"github.com/fluxcd/helm-controller/api/v2beta1"
|
||||
// +kubebuilder:scaffold:imports
|
||||
)
|
||||
|
||||
var cfg *rest.Config
|
||||
var k8sClient client.Client
|
||||
var testEnv *envtest.Environment
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
testEnv = &envtest.Environment{
|
||||
CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
|
||||
}
|
||||
|
||||
var err error
|
||||
cfg, err = testEnv.Start()
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to start testenv: %v", err))
|
||||
}
|
||||
|
||||
utilruntime.Must(v2beta1.AddToScheme(scheme.Scheme))
|
||||
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to create k8s client: %v", err))
|
||||
}
|
||||
|
||||
code := m.Run()
|
||||
|
||||
err = testEnv.Stop()
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to stop testenv: %v", err))
|
||||
}
|
||||
|
||||
os.Exit(code)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
<h1>Helm API reference v2beta1</h1>
|
||||
<h1>HelmRelease API reference</h1>
|
||||
<p>Packages:</p>
|
||||
<ul class="simple">
|
||||
<li>
|
||||
|
@ -92,17 +92,15 @@ Kubernetes meta/v1.Duration
|
|||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>Interval at which to reconcile the Helm release.
|
||||
This interval is approximate and may be subject to jitter to ensure
|
||||
efficient use of resources.</p>
|
||||
<p>Interval at which to reconcile the Helm release.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>kubeConfig</code><br>
|
||||
<em>
|
||||
<a href="https://godoc.org/github.com/fluxcd/pkg/apis/meta#KubeConfigReference">
|
||||
github.com/fluxcd/pkg/apis/meta.KubeConfigReference
|
||||
<a href="#helm.toolkit.fluxcd.io/v2beta1.KubeConfig">
|
||||
KubeConfig
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
|
@ -228,26 +226,6 @@ when reconciling this HelmRelease.</p>
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>persistentClient</code><br>
|
||||
<em>
|
||||
bool
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>PersistentClient tells the controller to use a persistent Kubernetes
|
||||
client for this release. When enabled, the client will be reused for the
|
||||
duration of the reconciliation, instead of being created and destroyed
|
||||
for each (step of a) Helm action.</p>
|
||||
<p>This can improve performance, but may cause issues with some Helm charts
|
||||
that for example do create Custom Resource Definitions during installation
|
||||
outside Helm’s CRD lifecycle hooks, which are then not observed to be
|
||||
available by e.g. post-install hooks.</p>
|
||||
<p>If not set, it defaults to true.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>install</code><br>
|
||||
<em>
|
||||
<a href="#helm.toolkit.fluxcd.io/v2beta1.Install">
|
||||
|
@ -479,20 +457,6 @@ v1beta2.Source.</p>
|
|||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<code>metadata</code><br>
|
||||
<em>
|
||||
<a href="#helm.toolkit.fluxcd.io/v2beta1.HelmChartTemplateObjectMeta">
|
||||
HelmChartTemplateObjectMeta
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>ObjectMeta holds the template for metadata like labels and annotations.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>spec</code><br>
|
||||
<em>
|
||||
<a href="#helm.toolkit.fluxcd.io/v2beta1.HelmChartTemplateSpec">
|
||||
|
@ -627,57 +591,6 @@ Chart dependencies, which are not bundled in the umbrella chart artifact, are no
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="helm.toolkit.fluxcd.io/v2beta1.HelmChartTemplateObjectMeta">HelmChartTemplateObjectMeta
|
||||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#helm.toolkit.fluxcd.io/v2beta1.HelmChartTemplate">HelmChartTemplate</a>)
|
||||
</p>
|
||||
<p>HelmChartTemplateObjectMeta defines the template for the ObjectMeta of a
|
||||
v1beta2.HelmChart.</p>
|
||||
<div class="md-typeset__scrollwrap">
|
||||
<div class="md-typeset__table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<code>labels</code><br>
|
||||
<em>
|
||||
map[string]string
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>Map of string keys and values that can be used to organize and categorize
|
||||
(scope and select) objects.
|
||||
More info: <a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/">https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>annotations</code><br>
|
||||
<em>
|
||||
map[string]string
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>Annotations is an unstructured key value map stored with a resource that may be
|
||||
set by external tools to store and retrieve arbitrary metadata. They are not
|
||||
queryable and should be preserved when modifying objects.
|
||||
More info: <a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/">https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="helm.toolkit.fluxcd.io/v2beta1.HelmChartTemplateSpec">HelmChartTemplateSpec
|
||||
</h3>
|
||||
<p>
|
||||
|
@ -903,17 +816,15 @@ Kubernetes meta/v1.Duration
|
|||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>Interval at which to reconcile the Helm release.
|
||||
This interval is approximate and may be subject to jitter to ensure
|
||||
efficient use of resources.</p>
|
||||
<p>Interval at which to reconcile the Helm release.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>kubeConfig</code><br>
|
||||
<em>
|
||||
<a href="https://godoc.org/github.com/fluxcd/pkg/apis/meta#KubeConfigReference">
|
||||
github.com/fluxcd/pkg/apis/meta.KubeConfigReference
|
||||
<a href="#helm.toolkit.fluxcd.io/v2beta1.KubeConfig">
|
||||
KubeConfig
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
|
@ -1039,26 +950,6 @@ when reconciling this HelmRelease.</p>
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>persistentClient</code><br>
|
||||
<em>
|
||||
bool
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>PersistentClient tells the controller to use a persistent Kubernetes
|
||||
client for this release. When enabled, the client will be reused for the
|
||||
duration of the reconciliation, instead of being created and destroyed
|
||||
for each (step of a) Helm action.</p>
|
||||
<p>This can improve performance, but may cause issues with some Helm charts
|
||||
that for example do create Custom Resource Definitions during installation
|
||||
outside Helm’s CRD lifecycle hooks, which are then not observed to be
|
||||
available by e.g. post-install hooks.</p>
|
||||
<p>If not set, it defaults to true.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>install</code><br>
|
||||
<em>
|
||||
<a href="#helm.toolkit.fluxcd.io/v2beta1.Install">
|
||||
|
@ -1569,6 +1460,48 @@ no retries remain. Defaults to ‘false’.</p>
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="helm.toolkit.fluxcd.io/v2beta1.KubeConfig">KubeConfig
|
||||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#helm.toolkit.fluxcd.io/v2beta1.HelmReleaseSpec">HelmReleaseSpec</a>)
|
||||
</p>
|
||||
<p>KubeConfig references a Kubernetes secret that contains a kubeconfig file.</p>
|
||||
<div class="md-typeset__scrollwrap">
|
||||
<div class="md-typeset__table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<code>secretRef</code><br>
|
||||
<em>
|
||||
<a href="https://godoc.org/github.com/fluxcd/pkg/apis/meta#SecretKeyReference">
|
||||
github.com/fluxcd/pkg/apis/meta.SecretKeyReference
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>SecretRef holds the name to a secret that contains a key with
|
||||
the kubeconfig file as the value. If no key is specified the key will
|
||||
default to ‘value’. The secret must be in the same namespace as
|
||||
the HelmRelease.
|
||||
It is recommended that the kubeconfig is self-contained, and the secret
|
||||
is regularly updated if credentials such as a cloud-access-token expire.
|
||||
Cloud specific <code>cmd-path</code> auth helpers will not function without adding
|
||||
binaries and credentials to the Pod that is responsible for reconciling
|
||||
the HelmRelease.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="helm.toolkit.fluxcd.io/v2beta1.Kustomize">Kustomize
|
||||
</h3>
|
||||
<p>
|
||||
|
@ -1944,19 +1877,6 @@ bool
|
|||
a Helm uninstall is performed.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>deletionPropagation</code><br>
|
||||
<em>
|
||||
string
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>DeletionPropagation specifies the deletion propagation policy when
|
||||
a Helm uninstall is performed.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
3086
docs/api/v2/helm.md
3086
docs/api/v2/helm.md
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -35,7 +35,6 @@ actions that should be (conditionally) executed. Based on this the reconciler:
|
|||
- performs a Helm install or upgrade action if needed
|
||||
- performs a Helm test action if enabled
|
||||
- performs a reconciliation strategy (rollback, uninstall) and retries as configured if any Helm action failed
|
||||
- performs in cluster drift detection and correction if enabled
|
||||
|
||||
The controller that runs these Helm actions relies on [source-controller](https://github.com/fluxcd/source-controller)
|
||||
for providing the Helm charts from Helm repositories or any other source that source-controller
|
||||
|
@ -51,7 +50,7 @@ trigger a Helm uninstall.
|
|||
Alerting can be configured with a Kubernetes custom resource that specifies a webhook address, and a
|
||||
group of `HelmRelease` resources to be monitored using the [notification-controller](https://github.com/fluxcd/notification-controller).
|
||||
|
||||
The API design of the controller can be found at [helm.toolkit.fluxcd.io/v2](./v2/helmreleases.md).
|
||||
The API design of the controller can be found at [helm.toolkit.fluxcd.io/v2beta1](./v2beta1/helmreleases.md).
|
||||
|
||||
## Backward compatibility
|
||||
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
# helm.toolkit.fluxcd.io/v2
|
||||
|
||||
This is the v2 API specification for declaratively managing Helm chart
|
||||
releases with Kubernetes manifests.
|
||||
|
||||
## Specification
|
||||
|
||||
- [HelmRelease CRD](helmreleases.md)
|
||||
+ [Example](helmreleases.md#example)
|
||||
+ [Writing a HelmRelease spec](helmreleases.md#writing-a-helmrelease-spec)
|
||||
+ [Working with HelmReleases](helmreleases.md#working-with-helmreleases)
|
||||
+ [HelmRelease Status](helmreleases.md#helmrelease-status)
|
||||
|
||||
## Implementation
|
||||
|
||||
* [helm-controller](https://github.com/fluxcd/helm-controller/)
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue