Compare commits

..

No commits in common. "main" and "v0.0.1-alpha.4" have entirely different histories.

232 changed files with 4559 additions and 32712 deletions

View File

@ -0,0 +1,6 @@
FROM giantswarm/tiny-tools
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

View File

@ -0,0 +1,9 @@
name: 'kubebuilder'
description: 'A GitHub Action to run kubebuilder commands'
author: 'Stefan Prodan'
branding:
icon: 'command'
color: 'blue'
runs:
using: 'docker'
image: 'Dockerfile'

View File

@ -0,0 +1,12 @@
#!/bin/sh -l
VERSION=2.3.1
curl -sL https://go.kubebuilder.io/dl/${VERSION}/linux/amd64 | tar -xz -C /tmp/
mkdir -p $GITHUB_WORKSPACE/kubebuilder
mv /tmp/kubebuilder_${VERSION}_linux_amd64/* $GITHUB_WORKSPACE/kubebuilder/
ls -lh $GITHUB_WORKSPACE/kubebuilder/bin
echo "::add-path::$GITHUB_WORKSPACE/kubebuilder/bin"
echo "::add-path::$RUNNER_WORKSPACE/$(basename $GITHUB_REPOSITORY)/kubebuilder/bin"

6
.github/actions/kustomize/Dockerfile vendored Normal file
View File

@ -0,0 +1,6 @@
FROM giantswarm/tiny-tools
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

9
.github/actions/kustomize/action.yml vendored Normal file
View File

@ -0,0 +1,9 @@
name: 'kustomize'
description: 'A GitHub Action to run kustomize commands'
author: 'Stefan Prodan'
branding:
icon: 'command'
color: 'blue'
runs:
using: 'docker'
image: 'Dockerfile'

12
.github/actions/kustomize/entrypoint.sh vendored Normal file
View File

@ -0,0 +1,12 @@
#!/bin/sh -l
VERSION=3.1.0
curl -sfLo kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/v${VERSION}/kustomize_${VERSION}_linux_amd64
mkdir -p $GITHUB_WORKSPACE/bin
cp ./kustomize $GITHUB_WORKSPACE/bin
chmod +x $GITHUB_WORKSPACE/bin/kustomize
ls -lh $GITHUB_WORKSPACE/bin
echo "::add-path::$GITHUB_WORKSPACE/bin"
echo "::add-path::$RUNNER_WORKSPACE/$(basename $GITHUB_REPOSITORY)/bin"

View File

@ -1,34 +0,0 @@
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/"
labels: ["dependencies"]
schedule:
interval: "monthly"
groups:
go-deps:
patterns:
- "*"
allow:
- dependency-type: "direct"
ignore:
# Kubernetes deps are updated by fluxcd/pkg
- dependency-name: "k8s.io/*"
- dependency-name: "sigs.k8s.io/*"
# KMS SDKs are updated by SOPS
- dependency-name: "github.com/Azure/*"
- dependency-name: "github.com/aws/*"
- dependency-name: "github.com/hashicorp/vault/*"
# Flux APIs pkg are updated at release time
- dependency-name: "github.com/fluxcd/kustomize-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"

40
.github/labels.yaml vendored
View File

@ -1,40 +0,0 @@
# Configuration file to declaratively configure labels
# Ref: https://github.com/EndBug/label-sync#Config-files
- name: area/kustomize
description: Kustomize related issues and pull requests
color: '#00e54d'
- name: area/kstatus
description: Health checking related issues and pull requests
color: '#25D5CA'
aliases: ['area/health-checks']
- name: area/sops
description: SOPS related issues and pull requests
color: '#FEE5D1'
- name: area/server-side-apply
description: SSA related issues and pull requests
color: '#2819CB'
- name: area/varsub
description: Post-build variable substitution related issues and pull requests
color: '#8D195D'
- 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'
- name: backport:release/v1.4.x
description: To be backported to release/v1.4.x
color: '#ffd700'
- name: backport:release/v1.5.x
description: To be backported to release/v1.5.x
color: '#ffd700'
- name: backport:release/v1.6.x
description: To be backported to release/v1.6.x
color: '#ffd700'

View File

@ -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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Create backport PRs
uses: korthout/backport-action@ca4972adce8039ff995e618f5fc02d1b7961f27a # v3.3.0
# 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}.

View File

@ -1,24 +0,0 @@
name: fuzz
on:
pull_request:
branches:
- main
permissions:
contents: read # for actions/checkout to fetch code
jobs:
smoketest:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version: 1.25.x
cache-dependency-path: |
**/go.sum
**/go.mod
- name: Smoke test Fuzzers
run: make fuzz-smoketest

View File

@ -4,159 +4,64 @@ on:
pull_request:
push:
branches:
- 'main'
- 'release/**'
permissions:
contents: read # for actions/checkout to fetch code
- master
jobs:
kind:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Setup Docker Buildx
id: buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Cache Docker layers
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
id: cache
uses: actions/checkout@v2
- name: Restore Go cache
uses: actions/cache@v1
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-ghcache-${{ github.sha }}
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-buildx-ghcache-
${{ runner.os }}-go-
- name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
uses: actions/setup-go@v2-beta
with:
go-version: 1.25.x
cache-dependency-path: |
**/go.sum
**/go.mod
go-version: 1.14.x
- name: Setup Kubernetes
uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0
with:
version: v0.20.0
cluster_name: kind
node_image: kindest/node:v1.27.3@sha256:3966ac761ae0136263ffdb6cfd4db23ef8a83cba8a463690e98317add2c9ba72
uses: engineerd/setup-kind@v0.3.0
- name: Setup Kustomize
uses: fluxcd/pkg/actions/kustomize@main
- name: Enable integration tests
# Only run integration tests for main branch
if: github.ref == 'refs/heads/main'
run: |
echo 'GO_TEST_ARGS=-tags integration' >> $GITHUB_ENV
- name: Run controller tests
env:
TEST_AZURE_CLIENT_ID: ${{ secrets.TEST_AZURE_CLIENT_ID }}
TEST_AZURE_TENANT_ID: ${{ secrets.TEST_AZURE_TENANT_ID }}
TEST_AZURE_CLIENT_SECRET: ${{ secrets.TEST_AZURE_CLIENT_SECRET }}
TEST_AZURE_VAULT_URL: ${{ secrets.TEST_AZURE_VAULT_URL }}
TEST_AZURE_VAULT_KEY_NAME: ${{ secrets.TEST_AZURE_VAULT_KEY_NAME }}
TEST_AZURE_VAULT_KEY_VERSION: ${{ secrets.TEST_AZURE_VAULT_KEY_VERSION }}
uses: ./.github/actions/kustomize
- name: Setup Kubebuilder
uses: ./.github/actions/kubebuilder
- 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
git --no-pager diff
echo 'run make test and commit changes'
exit 1
fi
- name: Build container image
run: |
make docker-build IMG=test/kustomize-controller:latest \
BUILD_PLATFORMS=linux/amd64 \
BUILD_ARGS="--cache-from=type=local,src=/tmp/.buildx-cache \
--cache-to=type=local,dest=/tmp/.buildx-cache-new,mode=max \
--load"
- # Temp fix
# https://github.com/docker/build-push-action/issues/252
# https://github.com/moby/buildkit/issues/1896
name: Move cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
run: make docker-build IMG=test/kustomize-controller:latest
env:
KUBEBUILDER_ASSETS: ${{ github.workspace }}/kubebuilder/bin
- name: Load test image
run: kind load docker-image test/kustomize-controller:latest
- name: Install CRDs
run: make install
- name: Run default status test
- name: Deploy source-controller
run: kustomize build https://github.com/fluxcd/source-controller//config/default?ref=v0.0.1-alpha.1 | kubectl apply -f-
- name: Deploy kustomize-controller
run: make dev-deploy IMG=test/kustomize-controller:latest
env:
KUBEBUILDER_ASSETS: ${{ github.workspace }}/kubebuilder/bin
- name: Run smoke tests
run: |
kubectl apply -f config/testdata/status-defaults
RESULT=$(kubectl get kustomization status-defaults -o go-template={{.status}})
EXPECTED='map[observedGeneration:-1]'
if [ "${RESULT}" != "${EXPECTED}" ] ; then
echo -e "${RESULT}\n\ndoes not equal\n\n${EXPECTED}"
exit 1
fi
kubectl delete -f config/testdata/status-defaults
- name: Deploy controllers
run: |
make dev-deploy IMG=test/kustomize-controller:latest
kubectl -n kustomize-system rollout status deploy/source-controller --timeout=1m
kubectl -n kustomize-system rollout status deploy/kustomize-controller --timeout=1m
- name: Run tests for removing kubectl managed fields
run: |
kubectl create ns managed-fields
kustomize build github.com/stefanprodan/podinfo//kustomize?ref=6.3.5 > /tmp/podinfo.yaml
kubectl -n managed-fields apply -f /tmp/podinfo.yaml
kubectl -n managed-fields apply -f ./config/testdata/managed-fields
kubectl -n managed-fields wait kustomization/podinfo --for=condition=ready --timeout=4m
OUTDATA=$(kubectl -n managed-fields get deploy podinfo --show-managed-fields -oyaml)
if echo "$OUTDATA" | grep -q "kubectl";then
echo "kubectl client-side manager not removed"
exit 1
fi
kubectl -n managed-fields apply --server-side --force-conflicts -f /tmp/podinfo.yaml
kubectl -n managed-fields annotate --overwrite kustomization/podinfo reconcile.fluxcd.io/requestedAt="$(date +%s)"
kubectl -n managed-fields wait kustomization/podinfo --for=condition=ready --timeout=4m
OUTDATA=$(kubectl -n managed-fields get deploy podinfo --show-managed-fields -oyaml)
if echo "$OUTDATA" | grep -q "kubectl";then
echo "kubectl server-side manager not removed"
exit 1
fi
kubectl delete ns managed-fields
- name: Run overlays tests
run: |
kubectl -n kustomize-system apply -k ./config/testdata/overlays
kubectl -n kustomize-system wait kustomizations/webapp-staging --for=condition=ready --timeout=4m
kubectl -n kustomize-system wait kustomizations/webapp-production --for=condition=ready --timeout=4m
- name: Run dependencies tests
run: |
kubectl -n kustomize-system apply -k ./config/testdata/dependencies
kubectl -n kustomize-system wait kustomizations/common --for=condition=ready --timeout=4m
kubectl -n kustomize-system wait kustomizations/backend --for=condition=ready --timeout=4m
kubectl -n kustomize-system wait kustomizations/frontend --for=condition=ready --timeout=4m
- name: Run impersonation tests
run: |
kubectl -n impersonation apply -f ./config/testdata/impersonation
kubectl -n impersonation wait kustomizations/podinfo --for=condition=ready --timeout=4m
kubectl -n impersonation delete kustomizations/podinfo
until kubectl -n impersonation get deploy/podinfo 2>&1 | grep NotFound ; do sleep 2; done
- name: Run OCI tests
run: |
kubectl create ns oci
kubectl -n oci apply -f ./config/testdata/oci
kubectl -n oci wait kustomizations/oci --for=condition=ready --timeout=4m
- name: Run CRDs + CRs tests
run: |
kubectl -n kustomize-system apply -f ./config/testdata/crds-crs
kubectl -n kustomize-system wait kustomizations/certs --for=condition=ready --timeout=4m
kubectl -n kustomizer-cert-test wait issuers/my-ca-issuer --for=condition=ready --timeout=1m
- name: Logs
run: |
kubectl -n kustomize-system logs deploy/source-controller
kubectl apply -k ./config/testdata/webapp
kubectl apply -k ./config/testdata/generate
kubectl wait kustomizations/frontend --for=condition=ready --timeout=4m
kubectl wait kustomizations/generate --for=condition=ready --timeout=4m
kubectl -n kustomize-system logs deploy/kustomize-controller
- name: Debug failure
if: failure()
run: |
kubectl -n kustomize-system get gitrepositories -oyaml
kubectl -n kustomize-system get kustomizations -oyaml
kubectl get gitrepositories -oyaml
kubectl get kustomizations -oyaml
kubectl -n kustomize-system get all
kubectl -n oci get ocirepository/oci -oyaml
kubectl -n oci get kustomization/oci -oyaml
kubectl -n kustomize-system logs deploy/source-controller
kubectl -n kustomize-system logs deploy/kustomize-controller
kubectl -n kustomize-system logs deploy/kustomize-controller

View File

@ -1,35 +0,0 @@
name: nightly
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
env:
REPOSITORY: ${{ github.repository }}
permissions:
contents: read # for actions/checkout to fetch code
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Setup Docker Buildx
id: buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
with:
buildkitd-flags: "--debug"
- name: Build multi-arch container image
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
push: false
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm/v7,linux/arm64
tags: |
${{ env.REPOSITORY }}:nightly

View File

@ -3,158 +3,50 @@ on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'image tag prefix'
default: 'rc'
required: true
permissions:
contents: read
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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/checkout@v2
- name: Setup Kustomize
uses: fluxcd/pkg/actions/kustomize@main
- name: Prepare
id: prep
run: |
VERSION="${{ github.event.inputs.tag }}-${GITHUB_SHA::8}"
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
- name: Setup QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Setup Docker Buildx
id: buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Login to GitHub Container Registry
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with:
registry: ghcr.io
username: fluxcdbot
password: ${{ secrets.GHCR_TOKEN }}
- name: Login to Docker Hub
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with:
username: fluxcdbot
password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }}
- name: Generate images meta
id: meta
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
with:
images: |
fluxcd/${{ env.CONTROLLER }}
ghcr.io/fluxcd/${{ env.CONTROLLER }}
tags: |
type=raw,value=${{ steps.prep.outputs.VERSION }}
- name: Publish images
id: build-push
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
sbom: true
provenance: true
push: true
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm/v7,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159 # v3.9.2
- 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 }}
- name: Generate release artifacts
if: startsWith(github.ref, 'refs/tags/v')
uses: ./.github/actions/kustomize
- name: Get version
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
- name: Generate release asset
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@da167eac915b4e86f08b264dbdbc867b61be6f0c # v0.20.5
- name: Create release and SBOM
id: run-goreleaser
if: startsWith(github.ref, 'refs/tags/v')
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
cp config/default/* config/release
cd config/release
kustomize edit set image fluxcd/kustomize-controller=fluxcd/kustomize-controller:${{ steps.get_version.outputs.VERSION }}
kustomize build . > kustomize-controller.yaml
- name: Push image
uses: docker/build-push-action@v1
with:
version: latest
args: release --clean --skip=validate
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: fluxcd/kustomize-controller
tag_with_ref: true
- name: Create release
id: create_release
uses: actions/create-release@latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Generate SLSA metadata
id: slsa
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
draft: false
prerelease: true
body: |
[CHANGELOG](https://github.com/fluxcd/kustomize-controller/blob/master/CHANGELOG.md)
- name: Upload artifacts
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
ARTIFACTS: "${{ steps.run-goreleaser.outputs.artifacts }}"
run: |
hashes=$(echo $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 }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./config/release/kustomize-controller.yaml
asset_name: kustomize-controller.yaml
asset_content_type: text/plain

View File

@ -1,52 +0,0 @@
name: scan
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: '18 10 * * 3'
permissions:
contents: read # for actions/checkout to fetch code
security-events: write # for codeQL to write security events
jobs:
fossa:
name: FOSSA
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Run FOSSA scan and upload build data
uses: fossa-contrib/fossa-action@3d2ef181b1820d6dcd1972f86a767d18167fa19b # v3.0.1
with:
# FOSSA Push-Only API Token
fossa-api-key: 5ee8bf422db1471e0bcf2bcb289185de
github-token: ${{ github.token }}
codeql:
name: CodeQL
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version: 1.25.x
cache-dependency-path: |
**/go.sum
**/go.mod
- name: Initialize CodeQL
uses: github/codeql-action/init@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
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@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11

View File

@ -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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- 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

20
.gitignore vendored
View File

@ -1,27 +1,17 @@
# Binaries for programs and plugins.
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`.
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool.
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Build tools downloaded at runtime.
# Dependency directories (remove the comment below to include it)
# vendor/
bin/
# Release manifests generated at runtime.
config/release/
config/crd/bases/ocirepositories.yaml
config/crd/bases/gitrepositories.yaml
config/crd/bases/buckets.yaml
config/crd/bases/externalartifacts.yaml
build/
# CRDs for fuzzing tests.
internal/controllers/testdata/crd

View File

@ -1,57 +0,0 @@
project_name: kustomize-controller
builds:
- skip: true
release:
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:
- glob: config/release/*.yaml
source:
enabled: true
name_template: "{{ .ProjectName }}_{{ .Version }}_source_code"
sboms:
- id: source
artifacts: source
documents:
- "{{ .ProjectName }}_{{ .Version }}_sbom.spdx.json"
# signs the checksum file
# all files (including the sboms) are included in the checksum
# https://goreleaser.com/customization/sign
signs:
- cmd: cosign
env:
- COSIGN_EXPERIMENTAL=1
certificate: "${artifact}.pem"
args:
- sign-blob
- "--yes"
- "--output-certificate=${certificate}"
- "--output-signature=${signature}"
- "${artifact}"
artifacts: checksum
output: true

File diff suppressed because it is too large Load Diff

73
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,73 @@
# Contributing
Kustomize Controller is [Apache 2.0 licensed](LICENSE) and accepts contributions
via GitHub pull requests. This document outlines some of the conventions on
to make it easier to get your contribution accepted.
We gratefully welcome improvements to issues and documentation as well as to
code.
## Certificate of Origin
By contributing to this project you agree to the Developer Certificate of
Origin (DCO). This document was created by the Linux Kernel community and is a
simple statement that you, as a contributor, have the legal right to make the
contribution. No action from you is required, but it's a good idea to see the
[DCO](DCO) file for details before you start contributing code to Kustomize
Controller.
## Communications
The project uses Slack: To join the conversation, simply join the
[CNCF](https://slack.cncf.io/) Slack workspace and use the
[#flux](https://cloud-native.slack.com/messages/flux/) channel.
The developers use a mailing list to discuss development as well.
Simply subscribe to [flux-dev on cncf.io](https://lists.cncf.io/g/cncf-flux-dev)
to join the conversation (this will also add an invitation to your
Google calendar for our [Flux
meeting](https://docs.google.com/document/d/1l_M0om0qUEN_NNiGgpqJ2tvsF2iioHkaARDeh6b70B0/edit#)).
### How to run the test suite
Prerequisites:
* go >= 1.13
* kubebuilder >= 2.3
* kustomize >= 3.1
You can run the unit tests by simply doing
```bash
make test
```
## Acceptance policy
These things will make a PR more likely to be accepted:
- a well-described requirement
- tests for new code
- tests for old code!
- new code and tests follow the conventions in old code and tests
- a good commit message (see below)
- all code must abide [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments)
- names should abide [What's in a name](https://talks.golang.org/2014/names.slide#1)
- code must build on both Linux and Darwin, via plain `go build`
- code should have appropriate test coverage and tests should be written
to work with `go test`
In general, we will merge a PR once one maintainer has endorsed it.
For substantial changes, more people may become involved, and you might
get asked to resubmit the PR or divide the changes into more than one PR.
### Format of the Commit Message
For Kustomize Controller we prefer the following rules for good commit messages:
- Limit the subject to 50 characters and write as the continuation
of the sentence "If applied, this commit will ..."
- Explain what and why in the body, if more than a trivial change;
wrap it at 72 characters.
The [following article](https://chris.beams.io/posts/git-commit/#seven-rules)
has some more helpful advice on documenting your work.

View File

@ -1,94 +0,0 @@
# Development
> **Note:** Please take a look at <https://fluxcd.io/contributing/flux/>
> to find out about how to contribute to Flux and how to interact with the
> Flux Development team.
## Installing required dependencies
There are a number of dependencies required to be able to run the controller and its test suite locally:
- [Install Go](https://golang.org/doc/install)
- [Install Kustomize](https://kubernetes-sigs.github.io/kustomize/installation/)
- [Install Docker](https://docs.docker.com/engine/install/)
- (Optional) [Install Kubebuilder](https://book.kubebuilder.io/quick-start.html#installation)
## How to run the test suite
Prerequisites:
* Go >= 1.25
You can run the test suite by simply doing
```sh
make test
```
## How to run the controller locally
Install the controller's CRDs on your test cluster:
```sh
make install
```
Note that `kustomize-controller` depends on [source-controller](https://github.com/fluxcd/source-controller) to acquire its artifacts. If `source-controller` is not running on your test cluster, you need to tell `kustomize-controller` where to find it.
Port forward to source-controller artifacts server:
```sh
kubectl -n flux-system port-forward svc/source-controller 8080:80
```
Export the local address as `SOURCE_CONTROLLER_LOCALHOST`:
```sh
export SOURCE_CONTROLLER_LOCALHOST=localhost:8080
```
Alternatively, if your test cluster is already running `source-controller` and `kustomize-controller`, you need to scale down the in-cluster `kustomize-controller`:
```
kubectl -n flux-system scale deployment/kustomize-controller --replicas=0
```
Run the controller locally:
```sh
make run
```
## How to install the controller
### Building the container image
Set the name of the container image to be created from the source code. This will be used when building, pushing and referring to the image on YAML files:
```sh
export IMG=registry-path/kustomize-controller:latest
```
Build the container image, tagging it as `$(IMG)`:
```sh
make docker-build
```
Push the image into the repository:
```sh
make docker-push
```
**Note**: `make docker-build` will build an image for the `amd64` architecture.
### Deploying into a cluster
Deploy `kustomize-controller` into the cluster that is configured in the local kubeconfig file (i.e. `~/.kube/config`):
```sh
make deploy
```
Running the above will also deploy `source-controller` and its CRDs to the cluster.

View File

@ -1,19 +1,15 @@
ARG GO_VERSION=1.25
ARG XX_VERSION=1.6.1
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS builder
# Copy the build utilities.
COPY --from=xx / /
ARG TARGETPLATFORM
FROM golang:1.13 as builder
WORKDIR /workspace
# copy api submodule
COPY api/ api/
RUN kustomize_ver=3.5.4 && \
kustomize_url=https://github.com/kubernetes-sigs/kustomize/releases/download && \
curl -sL ${kustomize_url}/kustomize%2Fv${kustomize_ver}/kustomize_v${kustomize_ver}_linux_amd64.tar.gz | \
tar xz && mv kustomize /usr/local/bin/kustomize
RUN kubectl_ver=1.18.2 && \
curl -sL https://storage.googleapis.com/kubernetes-release/release/v${kubectl_ver}/bin/linux/amd64/kubectl \
-o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl
# copy modules manifests
COPY go.mod go.mod
@ -24,23 +20,23 @@ RUN go mod download
# copy source code
COPY main.go main.go
COPY api/ api/
COPY controllers/ controllers/
COPY internal/ internal/
# build
ENV CGO_ENABLED=0
RUN xx-go build -trimpath -a -o kustomize-controller main.go
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o kustomize-controller main.go
FROM alpine:3.22
FROM alpine:3.11
ARG TARGETPLATFORM
RUN apk --no-cache add ca-certificates tini git openssh-client gnupg \
&& update-ca-certificates
RUN apk add --no-cache openssh-client ca-certificates tini 'git>=2.12.0' socat curl bash
COPY --from=builder /usr/local/bin/kustomize /usr/local/bin/
COPY --from=builder /usr/local/bin/kubectl /usr/local/bin/
COPY --from=builder /workspace/kustomize-controller /usr/local/bin/
USER 65534:65534
RUN addgroup -S controller && adduser -S -g controller controller
ENV GNUPGHOME=/tmp
USER controller
ENTRYPOINT [ "/sbin/tini", "--", "kustomize-controller" ]

View File

@ -2,7 +2,6 @@ The maintainers are generally available in Slack at
https://cloud-native.slack.com in #flux (https://cloud-native.slack.com/messages/CLAJ40HV3)
(obtain an invitation at https://slack.cncf.io/).
This project shares maintainers from the main Flux v2 git repository,
as listed in
In alphabetical order:
https://github.com/fluxcd/flux2/blob/main/MAINTAINERS
Stefan Prodan, Weaveworks <stefan@weave.works> (github: @stefanprodan, slack: stefanprodan)

194
Makefile
View File

@ -1,105 +1,29 @@
# Image URL to use all building/pushing image targets
IMG ?= fluxcd/kustomize-controller:latest
# Produce CRDs that work back to Kubernetes 1.16
CRD_OPTIONS ?= crd:crdVersions=v1
SOURCE_VER ?= $(shell go list -m all | grep github.com/fluxcd/source-controller/api | awk '{print $$2}')
# Produce CRDs that work back to Kubernetes 1.13
CRD_OPTIONS ?= crd
# Use the same version of SOPS already referenced on go.mod
SOPS_VER := $(shell go list -m all | grep github.com/getsops/sops | awk '{print $$2}')
# Repository root based on Git metadata
REPOSITORY_ROOT := $(shell git rev-parse --show-toplevel)
BUILD_DIR := $(REPOSITORY_ROOT)/build
# FUZZ_TIME defines the max amount of time, in Go Duration,
# each fuzzer should run for.
FUZZ_TIME ?= 1m
# If gobin not set, create one on ./build and add to path.
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
ifeq (,$(shell go env GOBIN))
GOBIN=$(BUILD_DIR)/gobin
GOBIN=$(shell go env GOPATH)/bin
else
GOBIN=$(shell go env GOBIN)
endif
export PATH:=$(GOBIN):${PATH}
# Allows for defining additional Go test args, e.g. '-tags integration'.
GO_TEST_ARGS ?=
# Allows for defining additional Docker buildx arguments, e.g. '--push'.
BUILD_ARGS ?= --load
# Architectures to build images for.
BUILD_PLATFORMS ?= linux/amd64
# Architecture to use envtest with
ENVTEST_ARCH ?= amd64
# Paths to download the CRD dependencies at.
GITREPO_CRD ?= config/crd/bases/gitrepositories.yaml
BUCKET_CRD ?= config/crd/bases/buckets.yaml
OCIREPO_CRD ?= config/crd/bases/ocirepositories.yaml
EA_CRD ?= config/crd/bases/externalartifacts.yaml
# 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_CRD_VER=$(BUILD_DIR)/.src-crd-$(SOURCE_VER)
# API (doc) generation utilities
CONTROLLER_GEN_VERSION ?= v0.19.0
GEN_API_REF_DOCS_VERSION ?= e327d0730470cbd61b06300f81c5fcf91c23c113
all: manager
# Download the envtest binaries to testbin
ENVTEST_ASSETS_DIR=$(BUILD_DIR)/testbin
ENVTEST_KUBERNETES_VERSION?=latest
install-envtest: setup-envtest
mkdir -p ${ENVTEST_ASSETS_DIR}
$(ENVTEST) use $(ENVTEST_KUBERNETES_VERSION) --arch=$(ENVTEST_ARCH) --bin-dir=$(ENVTEST_ASSETS_DIR)
SOPS = $(GOBIN)/sops
$(SOPS): ## Download latest sops binary if none is found.
$(call go-install-tool,$(SOPS),github.com/getsops/sops/v3/cmd/sops@$(SOPS_VER))
# Run controller 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 download-crd-deps install-envtest $(SOPS)
KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) go test ./... $(GO_TEST_ARGS) -v -coverprofile cover.out
# Run tests
test: generate fmt vet manifests
go test ./... -coverprofile cover.out
# Build manager binary
manager: generate fmt vet
go build -o $(BUILD_DIR)/bin/manager main.go
go build -o bin/manager main.go
# Run against the configured Kubernetes cluster in ~/.kube/config
run: generate fmt vet manifests
go run ./main.go --metrics-addr=:8089
# Delete previously downloaded CRDs and record the new version of the source
# CRDs.
$(SOURCE_CRD_VER):
rm -f $(BUILD_DIR)/.src-crd*
$(MAKE) cleanup-crd-deps
if ! test -d "$(BUILD_DIR)"; then mkdir -p $(BUILD_DIR); fi
touch $(SOURCE_CRD_VER)
$(GITREPO_CRD):
curl -s https://raw.githubusercontent.com/fluxcd/source-controller/${SOURCE_VER}/config/crd/bases/source.toolkit.fluxcd.io_gitrepositories.yaml -o $(GITREPO_CRD)
$(BUCKET_CRD):
curl -s https://raw.githubusercontent.com/fluxcd/source-controller/${SOURCE_VER}/config/crd/bases/source.toolkit.fluxcd.io_buckets.yaml -o $(BUCKET_CRD)
$(OCIREPO_CRD):
curl -s https://raw.githubusercontent.com/fluxcd/source-controller/${SOURCE_VER}/config/crd/bases/source.toolkit.fluxcd.io_ocirepositories.yaml -o $(OCIREPO_CRD)
$(EA_CRD):
curl -s https://raw.githubusercontent.com/fluxcd/source-controller/${SOURCE_VER}/config/crd/bases/source.toolkit.fluxcd.io_externalartifacts.yaml -o $(EA_CRD)
# Download the CRDs the controller depends on
download-crd-deps: $(SOURCE_CRD_VER) $(GITREPO_CRD) $(BUCKET_CRD) $(OCIREPO_CRD) $(EA_CRD)
# Delete the downloaded CRD dependencies.
cleanup-crd-deps:
rm -f $(GITREPO_CRD) $(BUCKET_CRD) $(OCIREPO_CRD) $(EA_CRD)
go run ./main.go
# Install CRDs into a cluster
install: manifests
@ -130,101 +54,41 @@ dev-cleanup: manifests
# Generate manifests e.g. CRD, RBAC etc.
manifests: controller-gen
$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role paths="./..." output:crd:artifacts:config="config/crd/bases"
cd api; $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role paths="./..." output:crd:artifacts:config="../config/crd/bases"
# Generate API reference documentation
api-docs: gen-crd-api-reference-docs
$(GEN_CRD_API_REFERENCE_DOCS) -api-dir=./api/v1 -config=./hack/api-docs/config.json -template-dir=./hack/api-docs/template -out-file=./docs/api/v1/kustomize.md
# Run go mod tidy
tidy:
cd api; rm -f go.sum; go mod tidy -compat=1.25
rm -f go.sum; go mod tidy -compat=1.25
$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role paths="./..." output:crd:artifacts:config=config/crd/bases
# Run go fmt against code
fmt:
go fmt ./...
cd api; go fmt ./...
# Run go vet against code
vet:
go vet ./...
cd api; go vet ./...
# Generate code
generate: controller-gen
cd api; $(CONTROLLER_GEN) object:headerFile="../hack/boilerplate.go.txt" paths="./..."
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
# Build the docker image
docker-build:
docker buildx build \
--platform=$(BUILD_PLATFORMS) \
-t ${IMG} \
${BUILD_ARGS} .
docker build . -t ${IMG}
# Push the docker image
docker-push:
docker push ${IMG}
# Set the docker image in-cluster
docker-deploy:
kubectl -n flux-system set image deployment/kustomize-controller manager=${IMG}
# 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))
# 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))
ENVTEST = $(GOBIN)/setup-envtest
.PHONY: envtest
setup-envtest: ## Download envtest-setup locally if necessary.
$(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest)
# go-install-tool will 'go install' any package $2 and install it to $1.
PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
define go-install-tool
@[ -f $(1) ] || { \
set -e ;\
TMP_DIR=$$(mktemp -d) ;\
cd $$TMP_DIR ;\
go mod init tmp ;\
echo "Downloading $(2)" ;\
GOBIN=$(GOBIN) go install $(2) ;\
rm -rf $$TMP_DIR ;\
}
endef
# Build fuzzers used by oss-fuzz.
fuzz-build:
rm -rf $(BUILD_DIR)/fuzz/
mkdir -p $(BUILD_DIR)/fuzz/out/
docker build . --pull --tag local-fuzzing:latest -f tests/fuzz/Dockerfile.builder
docker run --rm \
-e FUZZING_LANGUAGE=go -e SANITIZER=address \
-e CIFUZZ_DEBUG='True' -e OSS_FUZZ_PROJECT_NAME=fluxcd \
-v "$(shell go env GOMODCACHE):/root/go/pkg/mod" \
-v "$(BUILD_DIR)/fuzz/out":/out \
local-fuzzing:latest
# Run each fuzzer once to ensure they will work when executed by oss-fuzz.
fuzz-smoketest: fuzz-build
docker run --rm \
-v "$(BUILD_DIR)/fuzz/out":/out \
-v "$(shell pwd)/tests/fuzz/oss_fuzz_run.sh":/runner.sh \
local-fuzzing:latest \
bash -c "/runner.sh"
# Run fuzz tests for the duration set in FUZZ_TIME.
fuzz-native:
KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) \
FUZZ_TIME=$(FUZZ_TIME) \
./tests/fuzz/native_go_run.sh
# find or download controller-gen
# download controller-gen if necessary
controller-gen:
ifeq (, $(shell which controller-gen))
@{ \
set -e ;\
CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\
cd $$CONTROLLER_GEN_TMP_DIR ;\
go mod init tmp ;\
go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.2.5 ;\
rm -rf $$CONTROLLER_GEN_TMP_DIR ;\
}
CONTROLLER_GEN=$(GOBIN)/controller-gen
else
CONTROLLER_GEN=$(shell which controller-gen)
endif

11
PROJECT
View File

@ -1,13 +1,10 @@
domain: toolkit.fluxcd.io
domain: fluxcd.io
repo: github.com/fluxcd/kustomize-controller
resources:
- group: kustomize
kind: Kustomization
version: v1
version: v1alpha1
- group: kustomize
kind: Kustomization
version: v1beta2
- group: kustomize
kind: Kustomization
version: v1beta1
kind: Profile
version: v1alpha1
version: "2"

304
README.md
View File

@ -1,55 +1,299 @@
# kustomize-controller
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/4787/badge)](https://bestpractices.coreinfrastructure.org/projects/4787)
[![e2e](https://github.com/fluxcd/kustomize-controller/workflows/e2e/badge.svg)](https://github.com/fluxcd/kustomize-controller/actions)
[![report](https://goreportcard.com/badge/github.com/fluxcd/kustomize-controller)](https://goreportcard.com/report/github.com/fluxcd/kustomize-controller)
[![license](https://img.shields.io/github/license/fluxcd/kustomize-controller.svg)](https://github.com/fluxcd/kustomize-controller/blob/main/LICENSE)
[![license](https://img.shields.io/github/license/fluxcd/kustomize-controller.svg)](https://github.com/fluxcd/kustomize-controller/blob/master/LICENSE)
[![release](https://img.shields.io/github/release/fluxcd/kustomize-controller/all.svg)](https://github.com/fluxcd/kustomize-controller/releases)
The kustomize-controller is a [Flux](https://github.com/fluxcd/flux2) component,
specialized in running continuous delivery pipelines for infrastructure and workloads
The kustomize-controller is a Kubernetes operator, specialized in running
continuous delivery pipelines for infrastructure and workloads
defined with Kubernetes manifests and assembled with Kustomize.
The cluster desired state is described through a Kubernetes Custom Resource named `Kustomization`.
Based on the creation, mutation or removal of a `Kustomization` resource in the cluster,
the controller performs actions to reconcile the cluster current state with the desired state.
![overview](docs/diagrams/kustomize-controller-overview.png)
## Features
Features:
* watches for `Kustomization` objects
* fetches artifacts produced by [source-controller](https://github.com/fluxcd/source-controller) from `Source` objects
* watches `Source` objects for revision changes
* generates the `kustomization.yaml` file if needed
* generates Kubernetes manifests with Kustomize SDK
* decrypts Kubernetes secrets with Mozilla SOPS and KMS
* validates the generated manifests with Kubernetes server-side apply dry-run
- detects drift between the desired and state and cluster state
- corrects drift by patching objects with Kubernetes server-side apply
* generates Kubernetes manifests with kustomize build
* validates the build output with client-side or APIServer dry-run
* applies the generated manifests on the cluster
* prunes the Kubernetes objects removed from source
* checks the health of the deployed workloads
* runs `Kustomizations` in a specific order, taking into account the depends-on relationship
* notifies whenever a `Kustomization` status changes
* reports on Slack or Discord whenever a `Kustomization` status changes
## Specifications
* [API](docs/spec/v1/README.md)
Specifications:
* [API](docs/spec/v1alpha1/README.md)
* [Controller](docs/spec/README.md)
## Guides
## Usage
* [Get started with Flux](https://fluxcd.io/flux/get-started/)
* [Setup Notifications](https://fluxcd.io/flux/guides/notifications/)
* [Manage Kubernetes secrets with Flux and SOPS](https://fluxcd.io/flux/guides/mozilla-sops/)
* [How to build, publish and consume OCI Artifacts with Flux](https://fluxcd.io/flux/cheatsheets/oci-artifacts/)
* [Flux and Kustomize FAQ](https://fluxcd.io/flux/faq/#kustomize-questions)
The kustomize-controller is part of a composable GitOps toolkit and depends on
[source-controller](https://github.com/fluxcd/source-controller) to provide the raw Kubernetes
manifests and `kustomization.yaml` file.
## Roadmap
### Install the controllers
The roadmap for the Flux family of projects can be found at <https://fluxcd.io/roadmap/>.
Install source-controller with:
## Contributing
```bash
kustomize build https://github.com/fluxcd/source-controller//config/default?ref=v0.0.1-alpha.2 \
kubectl apply -f-
```
This project is Apache 2.0 licensed and accepts contributions via GitHub pull requests.
To start contributing please see the [development guide](DEVELOPMENT.md).
Install kustomize-controller with:
```bash
kustomize build https://github.com/fluxcd/kustomize-controller//config/default?ref=v0.0.1-alpha.4 \
kubectl apply -f-
```
### Define a Git repository source
Create a source object that points to a Git repository containing Kubernetes and Kustomize manifests:
```yaml
apiVersion: source.fluxcd.io/v1alpha1
kind: GitRepository
metadata:
name: podinfo
namespace: default
spec:
interval: 1m
url: https://github.com/stefanprodan/podinfo-deploy
ref:
branch: master
```
For private repositories, SSH or token based authentication can be
[configured with Kubernetes secrets](https://github.com/fluxcd/source-controller/blob/master/docs/spec/v1alpha1/gitrepositories.md).
Save the above file and apply it on the cluster.
You can wait for the source controller to assemble an artifact from the head of the repo master branch with:
```bash
kubectl wait gitrepository/podinfo --for=condition=ready
```
The source controller will check for new commits in the master branch every minute. You can force a git sync with:
```bash
kubectl annotate --overwrite gitrepository/podinfo source.fluxcd.io/syncAt="$(date +%s)"
```
### Define a kustomization
Create a kustomization object that uses the git repository defined above:
```yaml
apiVersion: kustomize.fluxcd.io/v1alpha1
kind: Kustomization
metadata:
name: podinfo-dev
spec:
interval: 5m
path: "./overlays/dev/"
prune: "env=dev"
sourceRef:
kind: GitRepository
name: podinfo
validation: client
healthChecks:
- kind: Deployment
name: podinfo
namespace: dev
timeout: 80s
```
> **Note** that if your repository contains only plain Kubernetes manifests,
> you can configure the controller to
> [automatically generate](docs/spec/v1alpha1/kustomization.md#generate-kustomizationyaml)
> a kustomization.yaml file inside the specified path.
A detailed explanation of the Kustomization object and its fields
can be found in the [specification doc](docs/spec/v1alpha1/README.md).
Based on the above definition, the kustomize-controller fetches the Git repository content from source-controller,
generates Kubernetes manifests by running kustomize build inside `./overlays/dev/`,
and validates them with a dry-run apply. If the manifests pass validation, the controller will apply them
on the cluster and starts the health assessment of the deployed workload. If the health checks are passing, the
Kustomization object status transitions to a ready state.
![workflow](docs/diagrams/kustomize-controller-flow.png)
You can wait for the kustomize controller to complete the deployment with:
```bash
kubectl wait kustomization/podinfo-dev --for=condition=ready
```
When the controller finishes the reconciliation, it will log the applied objects:
```bash
kubectl -n kustomize-system logs deploy/kustomize-controller | jq .
```
```json
{
"level": "info",
"ts": 1587195448.071468,
"logger": "controllers.Kustomization",
"msg": "Kustomization applied in 1.436096591s",
"kustomization": "default/podinfo-dev",
"output": {
"namespace/dev": "created",
"service/podinfo": "created",
"deployment.apps/podinfo": "created",
"horizontalpodautoscaler.autoscaling/podinfo": "created"
}
}
```
You can trigger a kustomize build and apply any time with:
```bash
kubectl annotate --overwrite kustomization/podinfo-dev kustomize.fluxcd.io/syncAt="$(date +%s)"
```
When the source controller pulls a new Git revision, the kustomize controller will detect that the
source revision changed, and will apply those changes right away.
If the kustomization build or apply fails, the controller sets the ready condition to `false` and logs the error:
```yaml
status:
conditions:
- lastTransitionTime: "2020-04-16T07:27:58Z"
message: 'apply failed'
reason: ApplyFailed
status: "False"
type: Ready
```
```json
{
"kustomization": "default/podinfo-dev",
"error": "Error from server (NotFound): error when creating \"podinfo-dev.yaml\": namespaces \"dev\" not found\n"
}
```
### Control the execution order
When running a kustomization, you may need to make sure other kustomizations have been
successfully applied beforehand. A kustomization can specify a list of dependencies with `spec.dependsOn`.
When combined with health assessment, a kustomization will run after all its dependencies health checks are passing.
For example, a service mesh proxy injector should be running before deploying applications inside the mesh:
```yaml
apiVersion: kustomize.fluxcd.io/v1alpha1
kind: Kustomization
metadata:
name: istio
spec:
interval: 10m
path: "./profiles/default/"
sourceRef:
kind: GitRepository
name: istio
healthChecks:
- kind: Deployment
name: istiod
namespace: istio-system
timeout: 2m
---
apiVersion: kustomize.fluxcd.io/v1alpha1
kind: Kustomization
metadata:
name: podinfo-dev
spec:
dependsOn:
- istio
interval: 5m
path: "./overlays/dev/"
prune: "env=dev"
sourceRef:
kind: GitRepository
name: podinfo
```
### Deploy releases to production
For production deployments, instead of synchronizing with a branch you can use a semver range to target stable releases:
```yaml
apiVersion: source.fluxcd.io/v1alpha1
kind: GitRepository
metadata:
name: podinfo-releases
spec:
interval: 5m
url: https://github.com/stefanprodan/podinfo-deploy
ref:
semver: ">=0.0.1-rc.1 <1.0.0"
```
With `ref.semver` we configure source controller to pull the Git tags and create an artifact from the most recent tag
that matches the semver range.
Create a production kustomization and reference the git source that follows the latest semver release:
```yaml
apiVersion: kustomize.fluxcd.io/v1alpha1
kind: Kustomization
metadata:
name: podinfo-production
spec:
interval: 10m
path: "./overlays/production/"
sourceRef:
kind: GitRepository
name: podinfo-releases
```
Based on the above definition, the kustomize controller will build and apply a kustomization that matches the semver range
set in the Git repository manifest.
### Configure alerting
The kustomize controller can post message to Slack or Discord whenever a kustomization status changes.
Alerting can be configured by creating a profile that targets a list of kustomizations:
```yaml
apiVersion: kustomize.fluxcd.io/v1alpha1
kind: Profile
metadata:
name: default
spec:
alert:
type: slack
verbosity: info
address: https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK
username: kustomize-controller
channel: general
kustomizations:
- '*'
```
The alert provider type can be: `slack` or `discord` and the verbosity can be set to `info` or `error`.
The `*` wildcard tells the controller to use this profile for all kustomizations that are present
in the same namespace as the profile.
Multiple profiles can be used to send alerts to different channels or Slack organizations.
When the verbosity is set to `error`, the controller will alert on any error encountered during the
reconciliation process. This includes kustomize build and validation errors, apply errors and
health check failures.
![error alert](docs/diagrams/slack-error-alert.png)
When the verbosity is set to `info`, the controller will alert if:
* a Kubernetes object was created, updated or deleted
* heath checks are passing
* a dependency is delaying the execution
* an error occurs
![info alert](docs/diagrams/slack-info-alert.png)

View File

@ -1,32 +0,0 @@
module github.com/fluxcd/kustomize-controller/api
go 1.25.0
require (
github.com/fluxcd/pkg/apis/kustomize v1.12.0
github.com/fluxcd/pkg/apis/meta v1.20.0
k8s.io/apiextensions-apiserver v0.34.0
k8s.io/apimachinery v0.34.0
sigs.k8s.io/controller-runtime v0.22.0
)
require (
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/gogo/protobuf v1.3.2 // 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.3-0.20250322232337-35a7c28c31ee // indirect
github.com/x448/float16 v0.8.4 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/text v0.28.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // 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/v6 v6.3.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)

View File

@ -1,119 +0,0 @@
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.12.0 h1:KvZN6xwgP/dNSeckL4a/Uv715XqiN1C3xS+jGcPejtE=
github.com/fluxcd/pkg/apis/kustomize v1.12.0/go.mod h1:OojLxIdKm1JAAdh3sL4j4F+vfrLKb7kq1vr8bpyEKgg=
github.com/fluxcd/pkg/apis/meta v1.20.0 h1:l9h0kWoDZTcYV0WJkFMgDXq6Q4tSojrJ+bHpFJSsaW0=
github.com/fluxcd/pkg/apis/meta v1.20.0/go.mod h1:XUAEUgT4gkWDAEN79E141tmL+v4SV50tVZ/Ojpc/ueg=
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.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/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.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
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/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/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/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/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/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/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/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.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
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/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
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.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
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/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.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.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.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
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.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
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=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
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/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.34.0 h1:L+JtP2wDbEYPUeNGbeSa/5GwFtIA662EmT2YSLOkAVE=
k8s.io/api v0.34.0/go.mod h1:YzgkIzOOlhl9uwWCZNqpw6RJy9L2FK4dlJeayUoydug=
k8s.io/apiextensions-apiserver v0.34.0 h1:B3hiB32jV7BcyKcMU5fDaDxk882YrJ1KU+ZSkA9Qxoc=
k8s.io/apiextensions-apiserver v0.34.0/go.mod h1:hLI4GxE1BDBy9adJKxUxCEHBGZtGfIg98Q+JmTD7+g0=
k8s.io/apimachinery v0.34.0 h1:eR1WO5fo0HyoQZt1wdISpFDffnWOvFLOOeJ7MgIv4z0=
k8s.io/apimachinery v0.34.0/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
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-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/controller-runtime v0.22.0 h1:mTOfibb8Hxwpx3xEkR56i7xSjB+nH4hZG37SrlCY5e0=
sigs.k8s.io/controller-runtime v0.22.0/go.mod h1:FwiwRjkRPbiN+zp2QRp7wlTCzbUXxZ/D4OzuQUDwBHY=
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 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/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@ -1,21 +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 v1 contains API Schema definitions for the kustomize.toolkit.fluxcd.io
// v1 API group.
// +kubebuilder:object:generate=true
// +groupName=kustomize.toolkit.fluxcd.io
package v1

View File

@ -1,33 +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 v1
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: "kustomize.toolkit.fluxcd.io", Version: "v1"}
// 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
)

View File

@ -1,34 +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 v1
// ResourceInventory contains a list of Kubernetes resource object references
// that have been applied by a Kustomization.
type ResourceInventory struct {
// Entries of Kubernetes resource object references.
Entries []ResourceRef `json:"entries"`
}
// ResourceRef contains the information necessary to locate a resource within a cluster.
type ResourceRef struct {
// ID is the string representation of the Kubernetes resource object's metadata,
// in the format '<namespace>_<name>_<group>_<kind>'.
ID string `json:"id"`
// Version is the API version of the Kubernetes resource object's kind.
Version string `json:"v"`
}

View File

@ -1,402 +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 v1
import (
"time"
"github.com/fluxcd/pkg/apis/kustomize"
"github.com/fluxcd/pkg/apis/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
KustomizationKind = "Kustomization"
KustomizationFinalizer = "finalizers.fluxcd.io"
MaxConditionMessageLength = 20000
EnabledValue = "enabled"
DisabledValue = "disabled"
MergeValue = "Merge"
IfNotPresentValue = "IfNotPresent"
IgnoreValue = "Ignore"
DeletionPolicyMirrorPrune = "MirrorPrune"
DeletionPolicyDelete = "Delete"
DeletionPolicyWaitForTermination = "WaitForTermination"
DeletionPolicyOrphan = "Orphan"
)
// KustomizationSpec defines the configuration to calculate the desired state
// from a Source using Kustomize.
type KustomizationSpec struct {
// CommonMetadata specifies the common labels and annotations that are
// applied to all resources. Any existing label or annotation will be
// overridden if its key matches a common one.
// +optional
CommonMetadata *CommonMetadata `json:"commonMetadata,omitempty"`
// DependsOn may contain a DependencyReference slice
// with references to Kustomization resources that must be ready before this
// Kustomization can be reconciled.
// +optional
DependsOn []DependencyReference `json:"dependsOn,omitempty"`
// Decrypt Kubernetes secrets before applying them on the cluster.
// +optional
Decryption *Decryption `json:"decryption,omitempty"`
// The interval at which to reconcile the Kustomization.
// 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
Interval metav1.Duration `json:"interval"`
// The interval at which to retry a previously failed reconciliation.
// When not specified, the controller uses the KustomizationSpec.Interval
// value to retry failures.
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$"
// +optional
RetryInterval *metav1.Duration `json:"retryInterval,omitempty"`
// The KubeConfig for reconciling the Kustomization on a remote cluster.
// When used in combination with KustomizationSpec.ServiceAccountName,
// forces the controller to act on behalf of that Service Account at the
// target cluster.
// If the --default-service-account flag is set, its value will be used as
// a controller level fallback for when KustomizationSpec.ServiceAccountName
// is empty.
// +optional
KubeConfig *meta.KubeConfigReference `json:"kubeConfig,omitempty"`
// Path to the directory containing the kustomization.yaml file, or the
// set of plain YAMLs a kustomization.yaml should be generated for.
// Defaults to 'None', which translates to the root path of the SourceRef.
// +optional
Path string `json:"path,omitempty"`
// PostBuild describes which actions to perform on the YAML manifest
// generated by building the kustomize overlay.
// +optional
PostBuild *PostBuild `json:"postBuild,omitempty"`
// Prune enables garbage collection.
// +required
Prune bool `json:"prune"`
// DeletionPolicy can be used to control garbage collection when this
// Kustomization is deleted. Valid values are ('MirrorPrune', 'Delete',
// 'WaitForTermination', 'Orphan'). 'MirrorPrune' mirrors the Prune field
// (orphan if false, delete if true). Defaults to 'MirrorPrune'.
// +kubebuilder:validation:Enum=MirrorPrune;Delete;WaitForTermination;Orphan
// +optional
DeletionPolicy string `json:"deletionPolicy,omitempty"`
// A list of resources to be included in the health assessment.
// +optional
HealthChecks []meta.NamespacedObjectKindReference `json:"healthChecks,omitempty"`
// NamePrefix will prefix the names of all managed resources.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=200
// +kubebuilder:validation:Optional
// +optional
NamePrefix string `json:"namePrefix,omitempty" yaml:"namePrefix,omitempty"`
// NameSuffix will suffix the names of all managed resources.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=200
// +kubebuilder:validation:Optional
// +optional
NameSuffix string `json:"nameSuffix,omitempty" yaml:"nameSuffix,omitempty"`
// Strategic merge and JSON patches, defined as inline YAML objects,
// capable of targeting objects based on kind, label and annotation selectors.
// +optional
Patches []kustomize.Patch `json:"patches,omitempty"`
// Images is a list of (image name, new name, new tag or digest)
// for changing image names, tags or digests. This can also be achieved with a
// patch, but this operator is simpler to specify.
// +optional
Images []kustomize.Image `json:"images,omitempty"`
// The name of the Kubernetes service account to impersonate
// when reconciling this Kustomization.
// +optional
ServiceAccountName string `json:"serviceAccountName,omitempty"`
// Reference of the source where the kustomization file is.
// +required
SourceRef CrossNamespaceSourceReference `json:"sourceRef"`
// This flag tells the controller to suspend subsequent kustomize executions,
// it does not apply to already started executions. Defaults to false.
// +optional
Suspend bool `json:"suspend,omitempty"`
// TargetNamespace sets or overrides the namespace in the
// kustomization.yaml file.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=63
// +kubebuilder:validation:Optional
// +optional
TargetNamespace string `json:"targetNamespace,omitempty"`
// Timeout for validation, apply and health checking operations.
// Defaults to 'Interval' duration.
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$"
// +optional
Timeout *metav1.Duration `json:"timeout,omitempty"`
// Force instructs the controller to recreate resources
// when patching fails due to an immutable field change.
// +kubebuilder:default:=false
// +optional
Force bool `json:"force,omitempty"`
// Wait instructs the controller to check the health of all the reconciled
// resources. When enabled, the HealthChecks are ignored. Defaults to false.
// +optional
Wait bool `json:"wait,omitempty"`
// Components specifies relative paths to kustomize Components.
// +optional
Components []string `json:"components,omitempty"`
// IgnoreMissingComponents instructs the controller to ignore Components paths
// not found in source by removing them from the generated kustomization.yaml
// before running kustomize build.
// +optional
IgnoreMissingComponents bool `json:"ignoreMissingComponents,omitempty"`
// HealthCheckExprs is a list of healthcheck expressions for evaluating the
// health of custom resources using Common Expression Language (CEL).
// The expressions are evaluated only when Wait or HealthChecks are specified.
// +optional
HealthCheckExprs []kustomize.CustomHealthCheck `json:"healthCheckExprs,omitempty"`
}
// CommonMetadata defines the common labels and annotations.
type CommonMetadata struct {
// Annotations to be added to the object's metadata.
// +optional
Annotations map[string]string `json:"annotations,omitempty"`
// Labels to be added to the object's metadata.
// +optional
Labels map[string]string `json:"labels,omitempty"`
}
// Decryption defines how decryption is handled for Kubernetes manifests.
type Decryption struct {
// Provider is the name of the decryption engine.
// +kubebuilder:validation:Enum=sops
// +required
Provider string `json:"provider"`
// ServiceAccountName is the name of the service account used to
// authenticate with KMS services from cloud providers. If a
// static credential for a given cloud provider is defined
// inside the Secret referenced by SecretRef, that static
// credential takes priority.
// +optional
ServiceAccountName string `json:"serviceAccountName,omitempty"`
// The secret name containing the private OpenPGP keys used for decryption.
// A static credential for a cloud provider defined inside the Secret
// takes priority to secret-less authentication with the ServiceAccountName
// field.
// +optional
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
}
// PostBuild describes which actions to perform on the YAML manifest
// generated by building the kustomize overlay.
type PostBuild struct {
// Substitute holds a map of key/value pairs.
// The variables defined in your YAML manifests that match any of the keys
// defined in the map will be substituted with the set value.
// Includes support for bash string replacement functions
// e.g. ${var:=default}, ${var:position} and ${var/substring/replacement}.
// +optional
Substitute map[string]string `json:"substitute,omitempty"`
// SubstituteFrom holds references to ConfigMaps and Secrets containing
// the variables and their values to be substituted in the YAML manifests.
// The ConfigMap and the Secret data keys represent the var names, and they
// must match the vars declared in the manifests for the substitution to
// happen.
// +optional
SubstituteFrom []SubstituteReference `json:"substituteFrom,omitempty"`
}
// SubstituteReference contains a reference to a resource containing
// the variables name and value.
type SubstituteReference 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"`
// Optional indicates whether the referenced resource must exist, or whether to
// tolerate its absence. If true and the referenced resource is absent, proceed
// as if the resource was present but empty, without any variables defined.
// +kubebuilder:default:=false
// +optional
Optional bool `json:"optional,omitempty"`
}
// KustomizationStatus defines the observed state of a kustomization.
type KustomizationStatus struct {
meta.ReconcileRequestStatus `json:",inline"`
// ObservedGeneration is the last reconciled generation.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
// The last successfully applied revision.
// Equals the Revision of the applied Artifact from the referenced Source.
// +optional
LastAppliedRevision string `json:"lastAppliedRevision,omitempty"`
// The last successfully applied origin revision.
// Equals the origin revision of the applied Artifact from the referenced Source.
// Usually present on the Metadata of the applied Artifact and depends on the
// Source type, e.g. for OCI it's the value associated with the key
// "org.opencontainers.image.revision".
// +optional
LastAppliedOriginRevision string `json:"lastAppliedOriginRevision,omitempty"`
// LastAttemptedRevision is the revision of the last reconciliation attempt.
// +optional
LastAttemptedRevision string `json:"lastAttemptedRevision,omitempty"`
// Inventory contains the list of Kubernetes resource object references that
// have been successfully applied.
// +optional
Inventory *ResourceInventory `json:"inventory,omitempty"`
// History contains a set of snapshots of the last reconciliation attempts
// tracking the revision, the state and the duration of each attempt.
// +optional
History meta.History `json:"history,omitempty"`
}
// GetTimeout returns the timeout with default.
func (in Kustomization) GetTimeout() time.Duration {
duration := in.Spec.Interval.Duration - 30*time.Second
if in.Spec.Timeout != nil {
duration = in.Spec.Timeout.Duration
}
if duration < 30*time.Second {
return 30 * time.Second
}
return duration
}
// GetRetryInterval returns the retry interval
func (in Kustomization) GetRetryInterval() time.Duration {
if in.Spec.RetryInterval != nil {
return in.Spec.RetryInterval.Duration
}
return in.GetRequeueAfter()
}
// GetRequeueAfter returns the duration after which the Kustomization must be
// reconciled again.
func (in Kustomization) GetRequeueAfter() time.Duration {
return in.Spec.Interval.Duration
}
// GetDeletionPolicy returns the deletion policy and default value if not specified.
func (in Kustomization) GetDeletionPolicy() string {
if in.Spec.DeletionPolicy == "" {
return DeletionPolicyMirrorPrune
}
return in.Spec.DeletionPolicy
}
// GetDependsOn returns the dependencies as a list of meta.NamespacedObjectReference.
//
// This function makes the Kustomization type conformant with the meta.ObjectWithDependencies interface
// and allows the controller-runtime to index Kustomizations by their dependencies.
func (in Kustomization) GetDependsOn() []meta.NamespacedObjectReference {
deps := make([]meta.NamespacedObjectReference, len(in.Spec.DependsOn))
for i := range in.Spec.DependsOn {
deps[i] = meta.NamespacedObjectReference{
Name: in.Spec.DependsOn[i].Name,
Namespace: in.Spec.DependsOn[i].Namespace,
}
}
return deps
}
// GetConditions returns the status conditions of the object.
func (in Kustomization) GetConditions() []metav1.Condition {
return in.Status.Conditions
}
// SetConditions sets the status conditions on the object.
func (in *Kustomization) SetConditions(conditions []metav1.Condition) {
in.Status.Conditions = conditions
}
// +genclient
// +kubebuilder:storageversion
// +kubebuilder:object:root=true
// +kubebuilder:resource:shortName=ks
// +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=""
// Kustomization is the Schema for the kustomizations API.
type Kustomization struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec KustomizationSpec `json:"spec,omitempty"`
// +kubebuilder:default:={"observedGeneration":-1}
Status KustomizationStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// KustomizationList contains a list of kustomizations.
type KustomizationList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Kustomization `json:"items"`
}
func init() {
SchemeBuilder.Register(&Kustomization{}, &KustomizationList{})
}

View File

@ -1,72 +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 v1
import (
"fmt"
)
// CrossNamespaceSourceReference contains enough information to let you locate the
// typed Kubernetes resource object at cluster level.
type CrossNamespaceSourceReference struct {
// API version of the referent.
// +optional
APIVersion string `json:"apiVersion,omitempty"`
// Kind of the referent.
// +kubebuilder:validation:Enum=OCIRepository;GitRepository;Bucket;ExternalArtifact
// +required
Kind string `json:"kind"`
// Name of the referent.
// +required
Name string `json:"name"`
// Namespace of the referent, defaults to the namespace of the Kubernetes
// resource object that contains the reference.
// +optional
Namespace string `json:"namespace,omitempty"`
}
// String returns a string representation of the CrossNamespaceSourceReference
// in the format "Kind/Name" or "Kind/Namespace/Name" if Namespace is set.
func (s *CrossNamespaceSourceReference) String() string {
if s.Namespace != "" {
return fmt.Sprintf("%s/%s/%s", s.Kind, s.Namespace, s.Name)
}
return fmt.Sprintf("%s/%s", s.Kind, s.Name)
}
// DependencyReference defines a Kustomization dependency on another Kustomization resource.
type DependencyReference struct {
// Name of the referent.
// +required
Name string `json:"name"`
// Namespace of the referent, defaults to the namespace of the Kustomization
// 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"`
}

View File

@ -1,357 +0,0 @@
//go:build !ignore_autogenerated
/*
Copyright 2025 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 v1
import (
"github.com/fluxcd/pkg/apis/kustomize"
"github.com/fluxcd/pkg/apis/meta"
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 *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 *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 *Decryption) DeepCopyInto(out *Decryption) {
*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 Decryption.
func (in *Decryption) DeepCopy() *Decryption {
if in == nil {
return nil
}
out := new(Decryption)
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 *Kustomization) DeepCopyInto(out *Kustomization) {
*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 Kustomization.
func (in *Kustomization) DeepCopy() *Kustomization {
if in == nil {
return nil
}
out := new(Kustomization)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Kustomization) 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 *KustomizationList) DeepCopyInto(out *KustomizationList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Kustomization, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KustomizationList.
func (in *KustomizationList) DeepCopy() *KustomizationList {
if in == nil {
return nil
}
out := new(KustomizationList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *KustomizationList) 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 *KustomizationSpec) DeepCopyInto(out *KustomizationSpec) {
*out = *in
if in.CommonMetadata != nil {
in, out := &in.CommonMetadata, &out.CommonMetadata
*out = new(CommonMetadata)
(*in).DeepCopyInto(*out)
}
if in.DependsOn != nil {
in, out := &in.DependsOn, &out.DependsOn
*out = make([]DependencyReference, len(*in))
copy(*out, *in)
}
if in.Decryption != nil {
in, out := &in.Decryption, &out.Decryption
*out = new(Decryption)
(*in).DeepCopyInto(*out)
}
out.Interval = in.Interval
if in.RetryInterval != nil {
in, out := &in.RetryInterval, &out.RetryInterval
*out = new(metav1.Duration)
**out = **in
}
if in.KubeConfig != nil {
in, out := &in.KubeConfig, &out.KubeConfig
*out = new(meta.KubeConfigReference)
(*in).DeepCopyInto(*out)
}
if in.PostBuild != nil {
in, out := &in.PostBuild, &out.PostBuild
*out = new(PostBuild)
(*in).DeepCopyInto(*out)
}
if in.HealthChecks != nil {
in, out := &in.HealthChecks, &out.HealthChecks
*out = make([]meta.NamespacedObjectKindReference, len(*in))
copy(*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)
}
out.SourceRef = in.SourceRef
if in.Timeout != nil {
in, out := &in.Timeout, &out.Timeout
*out = new(metav1.Duration)
**out = **in
}
if in.Components != nil {
in, out := &in.Components, &out.Components
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.HealthCheckExprs != nil {
in, out := &in.HealthCheckExprs, &out.HealthCheckExprs
*out = make([]kustomize.CustomHealthCheck, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KustomizationSpec.
func (in *KustomizationSpec) DeepCopy() *KustomizationSpec {
if in == nil {
return nil
}
out := new(KustomizationSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KustomizationStatus) DeepCopyInto(out *KustomizationStatus) {
*out = *in
out.ReconcileRequestStatus = in.ReconcileRequestStatus
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.Inventory != nil {
in, out := &in.Inventory, &out.Inventory
*out = new(ResourceInventory)
(*in).DeepCopyInto(*out)
}
if in.History != nil {
in, out := &in.History, &out.History
*out = make(meta.History, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KustomizationStatus.
func (in *KustomizationStatus) DeepCopy() *KustomizationStatus {
if in == nil {
return nil
}
out := new(KustomizationStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PostBuild) DeepCopyInto(out *PostBuild) {
*out = *in
if in.Substitute != nil {
in, out := &in.Substitute, &out.Substitute
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.SubstituteFrom != nil {
in, out := &in.SubstituteFrom, &out.SubstituteFrom
*out = make([]SubstituteReference, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostBuild.
func (in *PostBuild) DeepCopy() *PostBuild {
if in == nil {
return nil
}
out := new(PostBuild)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceInventory) DeepCopyInto(out *ResourceInventory) {
*out = *in
if in.Entries != nil {
in, out := &in.Entries, &out.Entries
*out = make([]ResourceRef, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceInventory.
func (in *ResourceInventory) DeepCopy() *ResourceInventory {
if in == nil {
return nil
}
out := new(ResourceInventory)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceRef) DeepCopyInto(out *ResourceRef) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceRef.
func (in *ResourceRef) DeepCopy() *ResourceRef {
if in == nil {
return nil
}
out := new(ResourceRef)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SubstituteReference) DeepCopyInto(out *SubstituteReference) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubstituteReference.
func (in *SubstituteReference) DeepCopy() *SubstituteReference {
if in == nil {
return nil
}
out := new(SubstituteReference)
in.DeepCopyInto(out)
return out
}

View File

@ -0,0 +1,83 @@
/*
Copyright 2020 The Flux CD contributors.
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 v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Condition contains condition information for a kustomization.
type Condition struct {
// Type of the condition, currently ('Ready').
// +required
Type string `json:"type"`
// Status of the condition, one of ('True', 'False', 'Unknown').
// +required
Status corev1.ConditionStatus `json:"status"`
// LastTransitionTime is the timestamp corresponding to the last status
// change of this condition.
// +required
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
// Reason is a brief machine readable explanation for the condition's last
// transition.
// +required
Reason string `json:"reason,omitempty"`
// Message is a human readable description of the details of the last
// transition, complementing reason.
// +optional
Message string `json:"message,omitempty"`
}
const (
// ReadyCondition represents the fact that a given kustomization has passed
// validation and was successfully applied on the cluster.
ReadyCondition string = "Ready"
)
const (
// ApplySucceedReason represents the fact that the kustomization apply succeed.
ApplySucceedReason string = "ApplySucceed"
// ApplyFailedReason represents the fact that the kustomization apply failed.
ApplyFailedReason string = "ApplyFailed"
// ArtifactFailedReason represents the fact that the artifact download failed.
ArtifactFailedReason string = "ArtifactFailed"
// BuildFailedReason represents the fact that the kustomize build command failed.
BuildFailedReason string = "BuildFailed"
// DependencyNotReady represents the fact that the one of the dependencies is not ready.
DependencyNotReadyReason string = "DependencyNotReady"
// HealthCheckFailedReason represents the fact that the one of the health check failed.
HealthCheckFailedReason string = "HealthCheckFailed"
// InitializedReason represents the fact that a given resource has been initialized.
InitializedReason string = "Initialized"
// SuspendedReason represents the fact that the kustomization execution is suspended.
SuspendedReason string = "Suspended"
// ValidationFailedReason represents the fact that the dry-run apply failed.
ValidationFailedReason string = "ValidationFailed"
)

View File

@ -1,5 +1,5 @@
/*
Copyright 2020 The Flux authors
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -14,7 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package v1beta1
// Package v1alpha1 contains API Schema definitions for the kustomize v1alpha1 API group
// +kubebuilder:object:generate=true
// +groupName=kustomize.fluxcd.io
package v1alpha1
import (
"k8s.io/apimachinery/pkg/runtime/schema"
@ -23,7 +26,7 @@ import (
var (
// GroupVersion is group version used to register these objects
GroupVersion = schema.GroupVersion{Group: "kustomize.toolkit.fluxcd.io", Version: "v1beta1"}
GroupVersion = schema.GroupVersion{Group: "kustomize.fluxcd.io", Version: "v1alpha1"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}

View File

@ -0,0 +1,185 @@
/*
Copyright 2020 The Flux CD contributors.
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 v1alpha1
import (
"time"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// KustomizationSpec defines the desired state of a kustomization.
type KustomizationSpec struct {
// A list of kustomizations that must be ready before this
// kustomization can be applied.
// +optional
DependsOn []string `json:"dependsOn,omitempty"`
// When enabled, the kustomization.yaml is automatically generated
// for all the Kubernetes manifests in the specified path and sub-directories.
// The generated kustomization.yaml contains a label transformer matching the prune field.
// +optional
Generate bool `json:"generate,omitempty"`
// The interval at which to apply the kustomization.
// +required
Interval metav1.Duration `json:"interval"`
// Path to the directory containing the kustomization file.
// +kubebuilder:validation:Pattern="^\\./"
// +required
Path string `json:"path"`
// Label selector used for garbage collection.
// +kubebuilder:validation:Pattern="^.*=.*$"
// +optional
Prune string `json:"prune,omitempty"`
// A list of workloads (Deployments, DaemonSets and StatefulSets)
// to be included in the health assessment.
// +optional
HealthChecks []WorkloadReference `json:"healthChecks,omitempty"`
// Reference of the source where the kustomization file is.
// +required
SourceRef corev1.TypedLocalObjectReference `json:"sourceRef"`
// This flag tells the controller to suspend subsequent kustomize executions,
// it does not apply to already started executions. Defaults to false.
// +optional
Suspend bool `json:"suspend,omitempty"`
// Timeout for validation, apply and health checking operations.
// Defaults to 'Interval' duration.
// +optional
Timeout *metav1.Duration `json:"timeout,omitempty"`
// Validate the Kubernetes objects before applying them on the cluster.
// The validation strategy can be 'client' (local dry-run) or 'server' (APIServer dry-run).
// +kubebuilder:validation:Enum=client;server
// +optional
Validation string `json:"validation,omitempty"`
}
// WorkloadReference defines a reference to a Deployment, DaemonSet or StatefulSet.
type WorkloadReference struct {
// Kind is the type of resource being referenced.
// +kubebuilder:validation:Enum=Deployment;DaemonSet;StatefulSet
// +required
Kind string `json:"kind"`
// Name is the name of resource being referenced.
// +required
Name string `json:"name"`
// Namespace is the namespace of resource being referenced.
// +required
Namespace string `json:"namespace,omitempty"`
}
// KustomizationStatus defines the observed state of a kustomization.
type KustomizationStatus struct {
// +optional
Conditions []Condition `json:"conditions,omitempty"`
// The last successfully applied revision.
// The revision format for Git sources is <branch|tag>/<commit-sha>.
// +optional
LastAppliedRevision string `json:"lastAppliedRevision,omitempty"`
}
func KustomizationReady(kustomization Kustomization, revision, reason, message string) Kustomization {
kustomization.Status.Conditions = []Condition{
{
Type: ReadyCondition,
Status: corev1.ConditionTrue,
LastTransitionTime: metav1.Now(),
Reason: reason,
Message: message,
},
}
kustomization.Status.LastAppliedRevision = revision
return kustomization
}
func KustomizationNotReady(kustomization Kustomization, reason, message string) Kustomization {
kustomization.Status.Conditions = []Condition{
{
Type: ReadyCondition,
Status: corev1.ConditionFalse,
LastTransitionTime: metav1.Now(),
Reason: reason,
Message: message,
},
}
return kustomization
}
// GetTimeout returns the timeout with default
func (in *Kustomization) GetTimeout() time.Duration {
duration := in.Spec.Interval.Duration
if in.Spec.Timeout != nil {
duration = in.Spec.Timeout.Duration
}
if duration < time.Minute {
return time.Minute
}
return duration
}
const (
// SyncAtAnnotation is the annotation used for triggering a
// sync outside of the specified schedule.
SyncAtAnnotation string = "kustomize.fluxcd.io/syncAt"
// SourceIndexKey is the key used for indexing kustomizations
// based on their sources.
SourceIndexKey string = ".metadata.source"
// DependencyIndexKey is the key used for indexing kustomizations
// based on their dependencies.
DependencyIndexKey string = ".metadata.dependency"
)
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +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=""
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
// Kustomization is the Schema for the kustomizations API.
type Kustomization struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec KustomizationSpec `json:"spec,omitempty"`
Status KustomizationStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// KustomizationList contains a list of kustomizations.
type KustomizationList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Kustomization `json:"items"`
}
func init() {
SchemeBuilder.Register(&Kustomization{}, &KustomizationList{})
}

View File

@ -0,0 +1,91 @@
/*
Copyright 2020 The Flux CD contributors.
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 v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ProfileSpec defines the desired state of Profile
type ProfileSpec struct {
// Alerting configuration of the kustomizations targeted by this profile.
// +optional
Alert *AlertProvider `json:"alert"`
// The list of kustomizations that this profile applies to.
// +required
Kustomizations []string `json:"kustomizations"`
}
// Alert is the configuration of alerting for a specific provider
type AlertProvider struct {
// HTTP(S) webhook address of this provider
// +required
Address string `json:"address"`
// Alert channel for this provider
// +required
Channel string `json:"channel"`
// Bot username for this provider
// +required
Username string `json:"username"`
// Filter alerts based on verbosity level, defaults to ('error').
// +kubebuilder:validation:Enum=info;error
// +optional
Verbosity string `json:"verbosity,omitempty"`
// Type of provider
// +kubebuilder:validation:Enum=slack;discord
// +required
Type string `json:"type"`
}
// ProfileStatus defines the observed state of Profile
type ProfileStatus struct {
// +optional
Conditions []Condition `json:"conditions,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +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=""
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
// Profile is the Schema for the profiles API
type Profile struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ProfileSpec `json:"spec,omitempty"`
Status ProfileStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// ProfileList contains a list of Profile
type ProfileList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Profile `json:"items"`
}
func init() {
SchemeBuilder.Register(&Profile{}, &ProfileList{})
}

View File

@ -1,7 +1,7 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright 2025 The Flux authors
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -18,63 +18,40 @@ limitations under the License.
// Code generated by controller-gen. DO NOT EDIT.
package v1beta1
package v1alpha1
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"
"k8s.io/apimachinery/pkg/runtime"
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 *CrossNamespaceSourceReference) DeepCopyInto(out *CrossNamespaceSourceReference) {
func (in *AlertProvider) DeepCopyInto(out *AlertProvider) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CrossNamespaceSourceReference.
func (in *CrossNamespaceSourceReference) DeepCopy() *CrossNamespaceSourceReference {
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AlertProvider.
func (in *AlertProvider) DeepCopy() *AlertProvider {
if in == nil {
return nil
}
out := new(CrossNamespaceSourceReference)
out := new(AlertProvider)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Decryption) DeepCopyInto(out *Decryption) {
func (in *Condition) DeepCopyInto(out *Condition) {
*out = *in
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(meta.LocalObjectReference)
**out = **in
}
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Decryption.
func (in *Decryption) DeepCopy() *Decryption {
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition.
func (in *Condition) DeepCopy() *Condition {
if in == nil {
return nil
}
out := new(Decryption)
in.DeepCopyInto(out)
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)
out := new(Condition)
in.DeepCopyInto(out)
return out
}
@ -143,62 +120,16 @@ func (in *KustomizationSpec) DeepCopyInto(out *KustomizationSpec) {
*out = *in
if in.DependsOn != nil {
in, out := &in.DependsOn, &out.DependsOn
*out = make([]meta.NamespacedObjectReference, len(*in))
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Decryption != nil {
in, out := &in.Decryption, &out.Decryption
*out = new(Decryption)
(*in).DeepCopyInto(*out)
}
out.Interval = in.Interval
if in.RetryInterval != nil {
in, out := &in.RetryInterval, &out.RetryInterval
*out = new(v1.Duration)
**out = **in
}
if in.KubeConfig != nil {
in, out := &in.KubeConfig, &out.KubeConfig
*out = new(KubeConfig)
**out = **in
}
if in.PostBuild != nil {
in, out := &in.PostBuild, &out.PostBuild
*out = new(PostBuild)
(*in).DeepCopyInto(*out)
}
if in.HealthChecks != nil {
in, out := &in.HealthChecks, &out.HealthChecks
*out = make([]meta.NamespacedObjectKindReference, len(*in))
*out = make([]WorkloadReference, len(*in))
copy(*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([]apiextensionsv1.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)
}
out.SourceRef = in.SourceRef
in.SourceRef.DeepCopyInto(&out.SourceRef)
if in.Timeout != nil {
in, out := &in.Timeout, &out.Timeout
*out = new(v1.Duration)
@ -221,17 +152,11 @@ func (in *KustomizationStatus) DeepCopyInto(out *KustomizationStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]v1.Condition, len(*in))
*out = make([]Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
out.ReconcileRequestStatus = in.ReconcileRequestStatus
if in.Snapshot != nil {
in, out := &in.Snapshot, &out.Snapshot
*out = new(Snapshot)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KustomizationStatus.
@ -245,87 +170,122 @@ func (in *KustomizationStatus) DeepCopy() *KustomizationStatus {
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PostBuild) DeepCopyInto(out *PostBuild) {
func (in *Profile) DeepCopyInto(out *Profile) {
*out = *in
if in.Substitute != nil {
in, out := &in.Substitute, &out.Substitute
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.SubstituteFrom != nil {
in, out := &in.SubstituteFrom, &out.SubstituteFrom
*out = make([]SubstituteReference, len(*in))
copy(*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 PostBuild.
func (in *PostBuild) DeepCopy() *PostBuild {
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Profile.
func (in *Profile) DeepCopy() *Profile {
if in == nil {
return nil
}
out := new(PostBuild)
out := new(Profile)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Profile) 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 *Snapshot) DeepCopyInto(out *Snapshot) {
func (in *ProfileList) DeepCopyInto(out *ProfileList) {
*out = *in
if in.Entries != nil {
in, out := &in.Entries, &out.Entries
*out = make([]SnapshotEntry, len(*in))
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Profile, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Snapshot.
func (in *Snapshot) DeepCopy() *Snapshot {
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProfileList.
func (in *ProfileList) DeepCopy() *ProfileList {
if in == nil {
return nil
}
out := new(Snapshot)
out := new(ProfileList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ProfileList) 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 *ProfileSpec) DeepCopyInto(out *ProfileSpec) {
*out = *in
if in.Alert != nil {
in, out := &in.Alert, &out.Alert
*out = new(AlertProvider)
**out = **in
}
if in.Kustomizations != nil {
in, out := &in.Kustomizations, &out.Kustomizations
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProfileSpec.
func (in *ProfileSpec) DeepCopy() *ProfileSpec {
if in == nil {
return nil
}
out := new(ProfileSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SnapshotEntry) DeepCopyInto(out *SnapshotEntry) {
func (in *ProfileStatus) DeepCopyInto(out *ProfileStatus) {
*out = *in
if in.Kinds != nil {
in, out := &in.Kinds, &out.Kinds
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SnapshotEntry.
func (in *SnapshotEntry) DeepCopy() *SnapshotEntry {
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProfileStatus.
func (in *ProfileStatus) DeepCopy() *ProfileStatus {
if in == nil {
return nil
}
out := new(SnapshotEntry)
out := new(ProfileStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SubstituteReference) DeepCopyInto(out *SubstituteReference) {
func (in *WorkloadReference) DeepCopyInto(out *WorkloadReference) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubstituteReference.
func (in *SubstituteReference) DeepCopy() *SubstituteReference {
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkloadReference.
func (in *WorkloadReference) DeepCopy() *WorkloadReference {
if in == nil {
return nil
}
out := new(SubstituteReference)
out := new(WorkloadReference)
in.DeepCopyInto(out)
return out
}

View File

@ -1,43 +0,0 @@
/*
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 v1beta1
const (
// HealthyCondition is the condition type used
// to record the last health assessment result.
HealthyCondition string = "Healthy"
// PruneFailedReason represents the fact that the
// pruning of the Kustomization failed.
PruneFailedReason string = "PruneFailed"
// ArtifactFailedReason represents the fact that the
// artifact download of the kustomization failed.
ArtifactFailedReason string = "ArtifactFailed"
// BuildFailedReason represents the fact that the
// kustomize build of the Kustomization failed.
BuildFailedReason string = "BuildFailed"
// HealthCheckFailedReason represents the fact that
// one of the health checks of the Kustomization failed.
HealthCheckFailedReason string = "HealthCheckFailed"
// ValidationFailedReason represents the fact that the
// validation of the Kustomization manifests has failed.
ValidationFailedReason string = "ValidationFailed"
)

View File

@ -1,23 +0,0 @@
/*
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 v1beta1 contains API Schema definitions for the kustomize v1beta1 API group
//
// Deprecated: v1beta1 is no longer supported, use v1 instead.
//
// +kubebuilder:object:generate=true
// +groupName=kustomize.toolkit.fluxcd.io
package v1beta1

View File

@ -1,306 +0,0 @@
/*
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 v1beta1
import (
"time"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/pkg/apis/kustomize"
"github.com/fluxcd/pkg/apis/meta"
)
const (
KustomizationKind = "Kustomization"
KustomizationFinalizer = "finalizers.fluxcd.io"
MaxConditionMessageLength = 20000
DisabledValue = "disabled"
)
// KustomizationSpec defines the desired state of a kustomization.
type KustomizationSpec struct {
// DependsOn may contain a meta.NamespacedObjectReference slice
// with references to Kustomization resources that must be ready before this
// Kustomization can be reconciled.
// +optional
DependsOn []meta.NamespacedObjectReference `json:"dependsOn,omitempty"`
// Decrypt Kubernetes secrets before applying them on the cluster.
// +optional
Decryption *Decryption `json:"decryption,omitempty"`
// The interval at which to reconcile the Kustomization.
// +required
Interval metav1.Duration `json:"interval"`
// The interval at which to retry a previously failed reconciliation.
// When not specified, the controller uses the KustomizationSpec.Interval
// value to retry failures.
// +optional
RetryInterval *metav1.Duration `json:"retryInterval,omitempty"`
// The KubeConfig for reconciling the Kustomization on a remote cluster.
// When specified, KubeConfig takes precedence over ServiceAccountName.
// +optional
KubeConfig *KubeConfig `json:"kubeConfig,omitempty"`
// Path to the directory containing the kustomization.yaml file, or the
// set of plain YAMLs a kustomization.yaml should be generated for.
// Defaults to 'None', which translates to the root path of the SourceRef.
// +optional
Path string `json:"path,omitempty"`
// PostBuild describes which actions to perform on the YAML manifest
// generated by building the kustomize overlay.
// +optional
PostBuild *PostBuild `json:"postBuild,omitempty"`
// Prune enables garbage collection.
// +required
Prune bool `json:"prune"`
// A list of resources to be included in the health assessment.
// +optional
HealthChecks []meta.NamespacedObjectKindReference `json:"healthChecks,omitempty"`
// Strategic merge and JSON patches, defined as inline YAML objects,
// capable of targeting objects based on kind, label and annotation selectors.
// +optional
Patches []kustomize.Patch `json:"patches,omitempty"`
// Strategic merge patches, defined as inline YAML objects.
// +optional
PatchesStrategicMerge []apiextensionsv1.JSON `json:"patchesStrategicMerge,omitempty"`
// JSON 6902 patches, defined as inline YAML objects.
// +optional
PatchesJSON6902 []kustomize.JSON6902Patch `json:"patchesJson6902,omitempty"`
// Images is a list of (image name, new name, new tag or digest)
// for changing image names, tags or digests. This can also be achieved with a
// patch, but this operator is simpler to specify.
// +optional
Images []kustomize.Image `json:"images,omitempty"`
// The name of the Kubernetes service account to impersonate
// when reconciling this Kustomization.
// +optional
ServiceAccountName string `json:"serviceAccountName,omitempty"`
// Reference of the source where the kustomization file is.
// +required
SourceRef CrossNamespaceSourceReference `json:"sourceRef"`
// This flag tells the controller to suspend subsequent kustomize executions,
// it does not apply to already started executions. Defaults to false.
// +optional
Suspend bool `json:"suspend,omitempty"`
// TargetNamespace sets or overrides the namespace in the
// kustomization.yaml file.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=63
// +kubebuilder:validation:Optional
// +optional
TargetNamespace string `json:"targetNamespace,omitempty"`
// Timeout for validation, apply and health checking operations.
// Defaults to 'Interval' duration.
// +optional
Timeout *metav1.Duration `json:"timeout,omitempty"`
// Validate the Kubernetes objects before applying them on the cluster.
// The validation strategy can be 'client' (local dry-run), 'server'
// (APIServer dry-run) or 'none'.
// When 'Force' is 'true', validation will fallback to 'client' if set to
// 'server' because server-side validation is not supported in this scenario.
// +kubebuilder:validation:Enum=none;client;server
// +optional
Validation string `json:"validation,omitempty"`
// Force instructs the controller to recreate resources
// when patching fails due to an immutable field change.
// +kubebuilder:default:=false
// +optional
Force bool `json:"force,omitempty"`
}
// Decryption defines how decryption is handled for Kubernetes manifests.
type Decryption struct {
// Provider is the name of the decryption engine.
// +kubebuilder:validation:Enum=sops
// +required
Provider string `json:"provider"`
// The secret name containing the private OpenPGP keys used for decryption.
// +optional
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
}
// KubeConfig references a Kubernetes secret that contains a kubeconfig file.
type KubeConfig struct {
// SecretRef holds the name to a secret that contains a 'value' key with
// the kubeconfig file as the value. It must be in the same namespace as
// the Kustomization.
// 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 Kustomization.
// +required
SecretRef meta.LocalObjectReference `json:"secretRef,omitempty"`
}
// PostBuild describes which actions to perform on the YAML manifest
// generated by building the kustomize overlay.
type PostBuild struct {
// Substitute holds a map of key/value pairs.
// The variables defined in your YAML manifests
// that match any of the keys defined in the map
// will be substituted with the set value.
// Includes support for bash string replacement functions
// e.g. ${var:=default}, ${var:position} and ${var/substring/replacement}.
// +optional
Substitute map[string]string `json:"substitute,omitempty"`
// SubstituteFrom holds references to ConfigMaps and Secrets containing
// the variables and their values to be substituted in the YAML manifests.
// The ConfigMap and the Secret data keys represent the var names and they
// must match the vars declared in the manifests for the substitution to happen.
// +optional
SubstituteFrom []SubstituteReference `json:"substituteFrom,omitempty"`
}
// SubstituteReference contains a reference to a resource containing
// the variables name and value.
type SubstituteReference 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"`
}
// KustomizationStatus defines the observed state of a kustomization.
type KustomizationStatus struct {
// ObservedGeneration is the last reconciled generation.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
// The last successfully applied revision.
// The revision format for Git sources is <branch|tag>/<commit-sha>.
// +optional
LastAppliedRevision string `json:"lastAppliedRevision,omitempty"`
// LastAttemptedRevision is the revision of the last reconciliation attempt.
// +optional
LastAttemptedRevision string `json:"lastAttemptedRevision,omitempty"`
meta.ReconcileRequestStatus `json:",inline"`
// The last successfully applied revision metadata.
// +optional
Snapshot *Snapshot `json:"snapshot,omitempty"`
}
// GetTimeout returns the timeout with default.
func (in Kustomization) GetTimeout() time.Duration {
duration := in.Spec.Interval.Duration
if in.Spec.Timeout != nil {
duration = in.Spec.Timeout.Duration
}
if duration < time.Minute {
return time.Minute
}
return duration
}
// GetRetryInterval returns the retry interval
func (in Kustomization) GetRetryInterval() time.Duration {
if in.Spec.RetryInterval != nil {
return in.Spec.RetryInterval.Duration
}
return in.Spec.Interval.Duration
}
func (in Kustomization) GetDependsOn() (types.NamespacedName, []meta.NamespacedObjectReference) {
return types.NamespacedName{
Namespace: in.Namespace,
Name: in.Name,
}, in.Spec.DependsOn
}
// GetStatusConditions returns a pointer to the Status.Conditions slice
func (in *Kustomization) GetStatusConditions() *[]metav1.Condition {
return &in.Status.Conditions
}
const (
// GitRepositoryIndexKey is the key used for indexing kustomizations
// based on their Git sources.
GitRepositoryIndexKey string = ".metadata.gitRepository"
// BucketIndexKey is the key used for indexing kustomizations
// based on their S3 sources.
BucketIndexKey string = ".metadata.bucket"
)
// +genclient
// +kubebuilder:object:root=true
// +kubebuilder:skipversion
// Kustomization is the Schema for the kustomizations API.
type Kustomization struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec KustomizationSpec `json:"spec,omitempty"`
// +kubebuilder:default:={"observedGeneration":-1}
Status KustomizationStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// KustomizationList contains a list of kustomizations.
type KustomizationList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Kustomization `json:"items"`
}
func init() {
SchemeBuilder.Register(&Kustomization{}, &KustomizationList{})
}
func trimString(str string, limit int) string {
if len(str) <= limit {
return str
}
return str[0:limit] + "..."
}

View File

@ -1,47 +0,0 @@
/*
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 v1beta1
import "fmt"
// CrossNamespaceSourceReference contains enough information to let you locate the
// typed referenced object at cluster level
type CrossNamespaceSourceReference struct {
// API version of the referent
// +optional
APIVersion string `json:"apiVersion,omitempty"`
// Kind of the referent
// +kubebuilder:validation:Enum=GitRepository;Bucket
// +required
Kind string `json:"kind"`
// Name of the referent
// +required
Name string `json:"name"`
// Namespace of the referent, defaults to the Kustomization namespace
// +optional
Namespace string `json:"namespace,omitempty"`
}
func (s *CrossNamespaceSourceReference) String() string {
if s.Namespace != "" {
return fmt.Sprintf("%s/%s/%s", s.Kind, s.Namespace, s.Name)
}
return fmt.Sprintf("%s/%s", s.Kind, s.Name)
}

View File

@ -1,139 +0,0 @@
/*
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 v1beta1
import (
"bytes"
"io"
"strings"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/yaml"
)
// Snapshot holds the metadata of the Kubernetes objects
// generated for a source revision
type Snapshot struct {
// The manifests sha1 checksum.
// +required
Checksum string `json:"checksum"`
// A list of Kubernetes kinds grouped by namespace.
// +required
Entries []SnapshotEntry `json:"entries"`
}
// Snapshot holds the metadata of namespaced
// Kubernetes objects
type SnapshotEntry struct {
// The namespace of this entry.
// +optional
Namespace string `json:"namespace"`
// The list of Kubernetes kinds.
// +required
Kinds map[string]string `json:"kinds"`
}
func NewSnapshot(manifests []byte, checksum string) (*Snapshot, error) {
snapshot := Snapshot{
Checksum: checksum,
Entries: []SnapshotEntry{},
}
reader := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(manifests), 2048)
for {
var obj unstructured.Unstructured
err := reader.Decode(&obj)
if err == io.EOF {
break
} else if err != nil {
return nil, err
}
if obj.IsList() {
err := obj.EachListItem(func(item runtime.Object) error {
snapshot.addEntry(item.(*unstructured.Unstructured))
return nil
})
if err != nil {
return nil, err
}
} else {
snapshot.addEntry(&obj)
}
}
return &snapshot, nil
}
func (s *Snapshot) addEntry(item *unstructured.Unstructured) {
found := false
for _, tracker := range s.Entries {
if tracker.Namespace == item.GetNamespace() {
tracker.Kinds[item.GroupVersionKind().String()] = item.GetKind()
found = true
break
}
}
if !found {
s.Entries = append(s.Entries, SnapshotEntry{
Namespace: item.GetNamespace(),
Kinds: map[string]string{
item.GroupVersionKind().String(): item.GetKind(),
},
})
}
}
func (s *Snapshot) NonNamespacedKinds() []schema.GroupVersionKind {
kinds := make([]schema.GroupVersionKind, 0)
for _, tracker := range s.Entries {
if tracker.Namespace == "" {
for gvk, kind := range tracker.Kinds {
if strings.Contains(gvk, ",") {
gv, err := schema.ParseGroupVersion(strings.Split(gvk, ",")[0])
if err == nil {
kinds = append(kinds, gv.WithKind(kind))
}
}
}
}
}
return kinds
}
func (s *Snapshot) NamespacedKinds() map[string][]schema.GroupVersionKind {
nsk := make(map[string][]schema.GroupVersionKind)
for _, tracker := range s.Entries {
if tracker.Namespace != "" {
var kinds []schema.GroupVersionKind
for gvk, kind := range tracker.Kinds {
if strings.Contains(gvk, ",") {
gv, err := schema.ParseGroupVersion(strings.Split(gvk, ",")[0])
if err == nil {
kinds = append(kinds, gv.WithKind(kind))
}
}
}
nsk[tracker.Namespace] = kinds
}
}
return nsk
}

View File

@ -1,55 +0,0 @@
/*
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.
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 v1beta2
const (
// HealthyCondition represents the last recorded
// health assessment result.
HealthyCondition string = "Healthy"
// PruneFailedReason represents the fact that the
// pruning of the Kustomization failed.
PruneFailedReason string = "PruneFailed"
// ArtifactFailedReason represents the fact that the
// source artifact download failed.
ArtifactFailedReason string = "ArtifactFailed"
// BuildFailedReason represents the fact that the
// kustomize build failed.
BuildFailedReason string = "BuildFailed"
// HealthCheckFailedReason represents the fact that
// one of the health checks failed.
HealthCheckFailedReason string = "HealthCheckFailed"
// 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"
// ProgressingWithRetryReason represents the fact that
// the reconciliation encountered an error that will be retried.
ProgressingWithRetryReason string = "ProgressingWithRetry"
)

View File

@ -1,20 +0,0 @@
/*
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.
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 v1beta2 contains API Schema definitions for the kustomize.toolkit.fluxcd.io v1beta2 API group.
// +kubebuilder:object:generate=true
// +groupName=kustomize.toolkit.fluxcd.io
package v1beta2

View File

@ -1,33 +0,0 @@
/*
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.
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 v1beta2
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: "kustomize.toolkit.fluxcd.io", Version: "v1beta2"}
// 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
)

View File

@ -1,33 +0,0 @@
/*
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.
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 v1beta2
// ResourceInventory contains a list of Kubernetes resource object references that have been applied by a Kustomization.
type ResourceInventory struct {
// Entries of Kubernetes resource object references.
Entries []ResourceRef `json:"entries"`
}
// ResourceRef contains the information necessary to locate a resource within a cluster.
type ResourceRef struct {
// ID is the string representation of the Kubernetes resource object's metadata,
// in the format '<namespace>_<name>_<group>_<kind>'.
ID string `json:"id"`
// Version is the API version of the Kubernetes resource object's kind.
Version string `json:"v"`
}

View File

@ -1,336 +0,0 @@
/*
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.
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 v1beta2
import (
"time"
"github.com/fluxcd/pkg/apis/kustomize"
"github.com/fluxcd/pkg/apis/meta"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
KustomizationKind = "Kustomization"
KustomizationFinalizer = "finalizers.fluxcd.io"
MaxConditionMessageLength = 20000
EnabledValue = "enabled"
DisabledValue = "disabled"
MergeValue = "merge"
)
// KustomizationSpec defines the configuration to calculate the desired state from a Source using Kustomize.
type KustomizationSpec struct {
// CommonMetadata specifies the common labels and annotations that are applied to all resources.
// Any existing label or annotation will be overridden if its key matches a common one.
// +optional
CommonMetadata *CommonMetadata `json:"commonMetadata,omitempty"`
// DependsOn may contain a meta.NamespacedObjectReference slice
// with references to Kustomization resources that must be ready before this
// Kustomization can be reconciled.
// +optional
DependsOn []meta.NamespacedObjectReference `json:"dependsOn,omitempty"`
// Decrypt Kubernetes secrets before applying them on the cluster.
// +optional
Decryption *Decryption `json:"decryption,omitempty"`
// The interval at which to reconcile the Kustomization.
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$"
// +required
Interval metav1.Duration `json:"interval"`
// The interval at which to retry a previously failed reconciliation.
// When not specified, the controller uses the KustomizationSpec.Interval
// value to retry failures.
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$"
// +optional
RetryInterval *metav1.Duration `json:"retryInterval,omitempty"`
// The KubeConfig for reconciling the Kustomization on a remote cluster.
// When used in combination with KustomizationSpec.ServiceAccountName,
// forces the controller to act on behalf of that Service Account at the
// target cluster.
// If the --default-service-account flag is set, its value will be used as
// a controller level fallback for when KustomizationSpec.ServiceAccountName
// is empty.
// +optional
KubeConfig *meta.KubeConfigReference `json:"kubeConfig,omitempty"`
// Path to the directory containing the kustomization.yaml file, or the
// set of plain YAMLs a kustomization.yaml should be generated for.
// Defaults to 'None', which translates to the root path of the SourceRef.
// +optional
Path string `json:"path,omitempty"`
// PostBuild describes which actions to perform on the YAML manifest
// generated by building the kustomize overlay.
// +optional
PostBuild *PostBuild `json:"postBuild,omitempty"`
// Prune enables garbage collection.
// +required
Prune bool `json:"prune"`
// A list of resources to be included in the health assessment.
// +optional
HealthChecks []meta.NamespacedObjectKindReference `json:"healthChecks,omitempty"`
// Strategic merge and JSON patches, defined as inline YAML objects,
// capable of targeting objects based on kind, label and annotation selectors.
// +optional
Patches []kustomize.Patch `json:"patches,omitempty"`
// Strategic merge patches, defined as inline YAML objects.
// Deprecated: Use Patches instead.
// +optional
PatchesStrategicMerge []apiextensionsv1.JSON `json:"patchesStrategicMerge,omitempty"`
// JSON 6902 patches, defined as inline YAML objects.
// Deprecated: Use Patches instead.
// +optional
PatchesJSON6902 []kustomize.JSON6902Patch `json:"patchesJson6902,omitempty"`
// Images is a list of (image name, new name, new tag or digest)
// for changing image names, tags or digests. This can also be achieved with a
// patch, but this operator is simpler to specify.
// +optional
Images []kustomize.Image `json:"images,omitempty"`
// The name of the Kubernetes service account to impersonate
// when reconciling this Kustomization.
// +optional
ServiceAccountName string `json:"serviceAccountName,omitempty"`
// Reference of the source where the kustomization file is.
// +required
SourceRef CrossNamespaceSourceReference `json:"sourceRef"`
// This flag tells the controller to suspend subsequent kustomize executions,
// it does not apply to already started executions. Defaults to false.
// +optional
Suspend bool `json:"suspend,omitempty"`
// TargetNamespace sets or overrides the namespace in the
// kustomization.yaml file.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=63
// +kubebuilder:validation:Optional
// +optional
TargetNamespace string `json:"targetNamespace,omitempty"`
// Timeout for validation, apply and health checking operations.
// Defaults to 'Interval' duration.
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$"
// +optional
Timeout *metav1.Duration `json:"timeout,omitempty"`
// Force instructs the controller to recreate resources
// when patching fails due to an immutable field change.
// +kubebuilder:default:=false
// +optional
Force bool `json:"force,omitempty"`
// Wait instructs the controller to check the health of all the reconciled resources.
// When enabled, the HealthChecks are ignored. Defaults to false.
// +optional
Wait bool `json:"wait,omitempty"`
// Components specifies relative paths to specifications of other Components.
// +optional
Components []string `json:"components,omitempty"`
// Deprecated: Not used in v1beta2.
// +kubebuilder:validation:Enum=none;client;server
// +optional
Validation string `json:"validation,omitempty"`
}
// CommonMetadata defines the common labels and annotations.
type CommonMetadata struct {
// Annotations to be added to the object's metadata.
// +optional
Annotations map[string]string `json:"annotations,omitempty"`
// Labels to be added to the object's metadata.
// +optional
Labels map[string]string `json:"labels,omitempty"`
}
// Decryption defines how decryption is handled for Kubernetes manifests.
type Decryption struct {
// Provider is the name of the decryption engine.
// +kubebuilder:validation:Enum=sops
// +required
Provider string `json:"provider"`
// The secret name containing the private OpenPGP keys used for decryption.
// +optional
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
}
// PostBuild describes which actions to perform on the YAML manifest
// generated by building the kustomize overlay.
type PostBuild struct {
// Substitute holds a map of key/value pairs.
// The variables defined in your YAML manifests
// that match any of the keys defined in the map
// will be substituted with the set value.
// Includes support for bash string replacement functions
// e.g. ${var:=default}, ${var:position} and ${var/substring/replacement}.
// +optional
Substitute map[string]string `json:"substitute,omitempty"`
// SubstituteFrom holds references to ConfigMaps and Secrets containing
// the variables and their values to be substituted in the YAML manifests.
// The ConfigMap and the Secret data keys represent the var names and they
// must match the vars declared in the manifests for the substitution to happen.
// +optional
SubstituteFrom []SubstituteReference `json:"substituteFrom,omitempty"`
}
// SubstituteReference contains a reference to a resource containing
// the variables name and value.
type SubstituteReference 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"`
// Optional indicates whether the referenced resource must exist, or whether to
// tolerate its absence. If true and the referenced resource is absent, proceed
// as if the resource was present but empty, without any variables defined.
// +kubebuilder:default:=false
// +optional
Optional bool `json:"optional,omitempty"`
}
// KustomizationStatus defines the observed state of a kustomization.
type KustomizationStatus struct {
meta.ReconcileRequestStatus `json:",inline"`
// ObservedGeneration is the last reconciled generation.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
// The last successfully applied revision.
// Equals the Revision of the applied Artifact from the referenced Source.
// +optional
LastAppliedRevision string `json:"lastAppliedRevision,omitempty"`
// LastAttemptedRevision is the revision of the last reconciliation attempt.
// +optional
LastAttemptedRevision string `json:"lastAttemptedRevision,omitempty"`
// Inventory contains the list of Kubernetes resource object references that have been successfully applied.
// +optional
Inventory *ResourceInventory `json:"inventory,omitempty"`
}
// GetTimeout returns the timeout with default.
func (in Kustomization) GetTimeout() time.Duration {
duration := in.Spec.Interval.Duration - 30*time.Second
if in.Spec.Timeout != nil {
duration = in.Spec.Timeout.Duration
}
if duration < 30*time.Second {
return 30 * time.Second
}
return duration
}
// GetRetryInterval returns the retry interval
func (in Kustomization) GetRetryInterval() time.Duration {
if in.Spec.RetryInterval != nil {
return in.Spec.RetryInterval.Duration
}
return in.GetRequeueAfter()
}
// GetRequeueAfter returns the duration after which the Kustomization must be
// reconciled again.
func (in Kustomization) GetRequeueAfter() time.Duration {
return in.Spec.Interval.Duration
}
// GetDependsOn returns the list of dependencies across-namespaces.
func (in Kustomization) GetDependsOn() []meta.NamespacedObjectReference {
return in.Spec.DependsOn
}
// GetConditions returns the status conditions of the object.
func (in Kustomization) GetConditions() []metav1.Condition {
return in.Status.Conditions
}
// SetConditions sets the status conditions on the object.
func (in *Kustomization) SetConditions(conditions []metav1.Condition) {
in.Status.Conditions = conditions
}
// GetStatusConditions returns a pointer to the Status.Conditions slice.
// Deprecated: use GetConditions instead.
func (in *Kustomization) GetStatusConditions() *[]metav1.Condition {
return &in.Status.Conditions
}
// +genclient
// +kubebuilder:object:root=true
// +kubebuilder:resource:shortName=ks
// +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=""
// +kubebuilder:deprecatedversion:warning="v1beta2 Kustomization is deprecated, upgrade to v1"
// Kustomization is the Schema for the kustomizations API.
type Kustomization struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec KustomizationSpec `json:"spec,omitempty"`
// +kubebuilder:default:={"observedGeneration":-1}
Status KustomizationStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// KustomizationList contains a list of kustomizations.
type KustomizationList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Kustomization `json:"items"`
}
func init() {
SchemeBuilder.Register(&Kustomization{}, &KustomizationList{})
}

View File

@ -1,47 +0,0 @@
/*
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.
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 v1beta2
import "fmt"
// CrossNamespaceSourceReference contains enough information to let you locate the
// typed Kubernetes resource object at cluster level.
type CrossNamespaceSourceReference struct {
// API version of the referent.
// +optional
APIVersion string `json:"apiVersion,omitempty"`
// Kind of the referent.
// +kubebuilder:validation:Enum=OCIRepository;GitRepository;Bucket
// +required
Kind string `json:"kind"`
// Name of the referent.
// +required
Name string `json:"name"`
// Namespace of the referent, defaults to the namespace of the Kubernetes resource object that contains the reference.
// +optional
Namespace string `json:"namespace,omitempty"`
}
func (s *CrossNamespaceSourceReference) String() string {
if s.Namespace != "" {
return fmt.Sprintf("%s/%s/%s", s.Kind, s.Namespace, s.Name)
}
return fmt.Sprintf("%s/%s", s.Kind, s.Name)
}

View File

@ -1,345 +0,0 @@
//go:build !ignore_autogenerated
/*
Copyright 2025 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 v1beta2
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 *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 *Decryption) DeepCopyInto(out *Decryption) {
*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 Decryption.
func (in *Decryption) DeepCopy() *Decryption {
if in == nil {
return nil
}
out := new(Decryption)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Kustomization) DeepCopyInto(out *Kustomization) {
*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 Kustomization.
func (in *Kustomization) DeepCopy() *Kustomization {
if in == nil {
return nil
}
out := new(Kustomization)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Kustomization) 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 *KustomizationList) DeepCopyInto(out *KustomizationList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Kustomization, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KustomizationList.
func (in *KustomizationList) DeepCopy() *KustomizationList {
if in == nil {
return nil
}
out := new(KustomizationList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *KustomizationList) 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 *KustomizationSpec) DeepCopyInto(out *KustomizationSpec) {
*out = *in
if in.CommonMetadata != nil {
in, out := &in.CommonMetadata, &out.CommonMetadata
*out = new(CommonMetadata)
(*in).DeepCopyInto(*out)
}
if in.DependsOn != nil {
in, out := &in.DependsOn, &out.DependsOn
*out = make([]meta.NamespacedObjectReference, len(*in))
copy(*out, *in)
}
if in.Decryption != nil {
in, out := &in.Decryption, &out.Decryption
*out = new(Decryption)
(*in).DeepCopyInto(*out)
}
out.Interval = in.Interval
if in.RetryInterval != nil {
in, out := &in.RetryInterval, &out.RetryInterval
*out = new(v1.Duration)
**out = **in
}
if in.KubeConfig != nil {
in, out := &in.KubeConfig, &out.KubeConfig
*out = new(meta.KubeConfigReference)
(*in).DeepCopyInto(*out)
}
if in.PostBuild != nil {
in, out := &in.PostBuild, &out.PostBuild
*out = new(PostBuild)
(*in).DeepCopyInto(*out)
}
if in.HealthChecks != nil {
in, out := &in.HealthChecks, &out.HealthChecks
*out = make([]meta.NamespacedObjectKindReference, len(*in))
copy(*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([]apiextensionsv1.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)
}
out.SourceRef = in.SourceRef
if in.Timeout != nil {
in, out := &in.Timeout, &out.Timeout
*out = new(v1.Duration)
**out = **in
}
if in.Components != nil {
in, out := &in.Components, &out.Components
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KustomizationSpec.
func (in *KustomizationSpec) DeepCopy() *KustomizationSpec {
if in == nil {
return nil
}
out := new(KustomizationSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KustomizationStatus) DeepCopyInto(out *KustomizationStatus) {
*out = *in
out.ReconcileRequestStatus = in.ReconcileRequestStatus
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.Inventory != nil {
in, out := &in.Inventory, &out.Inventory
*out = new(ResourceInventory)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KustomizationStatus.
func (in *KustomizationStatus) DeepCopy() *KustomizationStatus {
if in == nil {
return nil
}
out := new(KustomizationStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PostBuild) DeepCopyInto(out *PostBuild) {
*out = *in
if in.Substitute != nil {
in, out := &in.Substitute, &out.Substitute
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.SubstituteFrom != nil {
in, out := &in.SubstituteFrom, &out.SubstituteFrom
*out = make([]SubstituteReference, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostBuild.
func (in *PostBuild) DeepCopy() *PostBuild {
if in == nil {
return nil
}
out := new(PostBuild)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceInventory) DeepCopyInto(out *ResourceInventory) {
*out = *in
if in.Entries != nil {
in, out := &in.Entries, &out.Entries
*out = make([]ResourceRef, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceInventory.
func (in *ResourceInventory) DeepCopy() *ResourceInventory {
if in == nil {
return nil
}
out := new(ResourceInventory)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceRef) DeepCopyInto(out *ResourceRef) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceRef.
func (in *ResourceRef) DeepCopy() *ResourceRef {
if in == nil {
return nil
}
out := new(ResourceRef)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SubstituteReference) DeepCopyInto(out *SubstituteReference) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubstituteReference.
func (in *SubstituteReference) DeepCopy() *SubstituteReference {
if in == nil {
return nil
}
out := new(SubstituteReference)
in.DeepCopyInto(out)
return out
}

View File

@ -0,0 +1,185 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.5
creationTimestamp: null
name: kustomizations.kustomize.fluxcd.io
spec:
additionalPrinterColumns:
- JSONPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
- JSONPath: .status.conditions[?(@.type=="Ready")].message
name: Status
type: string
- JSONPath: .metadata.creationTimestamp
name: Age
type: date
group: kustomize.fluxcd.io
names:
kind: Kustomization
listKind: KustomizationList
plural: kustomizations
singular: kustomization
scope: Namespaced
subresources:
status: {}
validation:
openAPIV3Schema:
description: Kustomization is the Schema for the kustomizations API.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: KustomizationSpec defines the desired state of a kustomization.
properties:
dependsOn:
description: A list of kustomizations that must be ready before this
kustomization can be applied.
items:
type: string
type: array
generate:
description: When enabled, the kustomization.yaml is automatically generated
for all the Kubernetes manifests in the specified path and sub-directories.
The generated kustomization.yaml contains a label transformer matching
the prune field.
type: boolean
healthChecks:
description: A list of workloads (Deployments, DaemonSets and StatefulSets)
to be included in the health assessment.
items:
description: WorkloadReference defines a reference to a Deployment,
DaemonSet or StatefulSet.
properties:
kind:
description: Kind is the type of resource being referenced.
enum:
- Deployment
- DaemonSet
- StatefulSet
type: string
name:
description: Name is the name of resource being referenced.
type: string
namespace:
description: Namespace is the namespace of resource being referenced.
type: string
required:
- kind
- name
type: object
type: array
interval:
description: The interval at which to apply the kustomization.
type: string
path:
description: Path to the directory containing the kustomization file.
pattern: ^\./
type: string
prune:
description: Label selector used for garbage collection.
pattern: ^.*=.*$
type: string
sourceRef:
description: Reference of the source where the kustomization file is.
properties:
apiGroup:
description: APIGroup is the group for the resource being referenced.
If APIGroup is not specified, the specified Kind must be in the
core API group. For any other third-party types, APIGroup is required.
type: string
kind:
description: Kind is the type of resource being referenced
type: string
name:
description: Name is the name of resource being referenced
type: string
required:
- kind
- name
type: object
suspend:
description: This flag tells the controller to suspend subsequent kustomize
executions, it does not apply to already started executions. Defaults
to false.
type: boolean
timeout:
description: Timeout for validation, apply and health checking operations.
Defaults to 'Interval' duration.
type: string
validation:
description: Validate the Kubernetes objects before applying them on
the cluster. The validation strategy can be 'client' (local dry-run)
or 'server' (APIServer dry-run).
enum:
- client
- server
type: string
required:
- interval
- path
- sourceRef
type: object
status:
description: KustomizationStatus defines the observed state of a kustomization.
properties:
conditions:
items:
description: Condition contains condition information for a kustomization.
properties:
lastTransitionTime:
description: LastTransitionTime is the timestamp corresponding
to the last status change of this condition.
format: date-time
type: string
message:
description: Message is a human readable description of the details
of the last transition, complementing reason.
type: string
reason:
description: Reason is a brief machine readable explanation for
the condition's last transition.
type: string
status:
description: Status of the condition, one of ('True', 'False',
'Unknown').
type: string
type:
description: Type of the condition, currently ('Ready').
type: string
required:
- status
- type
type: object
type: array
lastAppliedRevision:
description: The last successfully applied revision. The revision format
for Git sources is <branch|tag>/<commit-sha>.
type: string
type: object
type: object
version: v1alpha1
versions:
- name: v1alpha1
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@ -0,0 +1,133 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.5
creationTimestamp: null
name: profiles.kustomize.fluxcd.io
spec:
additionalPrinterColumns:
- JSONPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
- JSONPath: .status.conditions[?(@.type=="Ready")].message
name: Status
type: string
- JSONPath: .metadata.creationTimestamp
name: Age
type: date
group: kustomize.fluxcd.io
names:
kind: Profile
listKind: ProfileList
plural: profiles
singular: profile
scope: Namespaced
subresources:
status: {}
validation:
openAPIV3Schema:
description: Profile is the Schema for the profiles API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: ProfileSpec defines the desired state of Profile
properties:
alert:
description: Alerting configuration of the kustomizations targeted by
this profile.
properties:
address:
description: HTTP(S) webhook address of this provider
type: string
channel:
description: Alert channel for this provider
type: string
type:
description: Type of provider
enum:
- slack
- discord
type: string
username:
description: Bot username for this provider
type: string
verbosity:
description: Filter alerts based on verbosity level, defaults to
('error').
enum:
- info
- error
type: string
required:
- address
- channel
- type
- username
type: object
kustomizations:
description: The list of kustomizations that this profile applies to.
items:
type: string
type: array
required:
- kustomizations
type: object
status:
description: ProfileStatus defines the observed state of Profile
properties:
conditions:
items:
description: Condition contains condition information for a kustomization.
properties:
lastTransitionTime:
description: LastTransitionTime is the timestamp corresponding
to the last status change of this condition.
format: date-time
type: string
message:
description: Message is a human readable description of the details
of the last transition, complementing reason.
type: string
reason:
description: Reason is a brief machine readable explanation for
the condition's last transition.
type: string
status:
description: Status of the condition, one of ('True', 'False',
'Unknown').
type: string
type:
description: Type of the condition, currently ('Ready').
type: string
required:
- status
- type
type: object
type: array
type: object
type: object
version: v1alpha1
versions:
- name: v1alpha1
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@ -1,6 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- bases/kustomize.toolkit.fluxcd.io_kustomizations.yaml
- bases/kustomize.fluxcd.io_kustomizations.yaml
- bases/kustomize.fluxcd.io_profiles.yaml
# +kubebuilder:scaffold:crdkustomizeresource

View File

@ -1,10 +1,10 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: kustomize-system
resources:
- https://github.com/fluxcd/source-controller/releases/download/v1.7.0-rc.3/source-controller.crds.yaml
- https://github.com/fluxcd/source-controller/releases/download/v1.7.0-rc.3/source-controller.deployment.yaml
bases:
- ../crd
- ../rbac
- ../manager
- namespace.yaml
resources:
- namespace.yaml

View File

@ -15,13 +15,9 @@ spec:
app: kustomize-controller
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/port: "8282"
spec:
terminationGracePeriodSeconds: 60
# Required for AWS IAM Role bindings
# https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts-technical-overview.html
securityContext:
fsGroup: 1337
terminationGracePeriodSeconds: 10
containers:
- name: manager
image: fluxcd/kustomize-controller
@ -29,37 +25,16 @@ spec:
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
capabilities:
drop: [ "ALL" ]
seccompProfile:
type: RuntimeDefault
ports:
- containerPort: 8080
- containerPort: 8282
name: http-prom
protocol: TCP
- containerPort: 9440
name: healthz
protocol: TCP
env:
- name: RUNTIME_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
args:
- --watch-all-namespaces
- --log-level=info
- --log-encoding=json
- --enable-leader-election
readinessProbe:
httpGet:
path: /readyz
port: healthz
livenessProbe:
httpGet:
path: /healthz
port: healthz
port: http-prom
path: /metrics
args:
- --enable-leader-election
- --log-json
resources:
limits:
cpu: 1000m

View File

@ -5,4 +5,4 @@ resources:
images:
- name: fluxcd/kustomize-controller
newName: fluxcd/kustomize-controller
newTag: v1.6.0
newTag: v0.0.1-alpha.4

View File

@ -0,0 +1,10 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: reconciler-role
rules:
- apiGroups: ['*']
resources: ['*']
verbs: ['*']
- nonResourceURLs: ['*']
verbs: ['*']

View File

@ -1,11 +1,11 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-reconciler
name: reconciler-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
name: reconciler-role
subjects:
- kind: ServiceAccount
name: default

View File

@ -5,5 +5,6 @@ resources:
- role_binding.yaml
- leader_election_role.yaml
- leader_election_role_binding.yaml
- cluster_role.yaml
- cluster_role_binding.yaml
namePrefix: kustomize-

View File

@ -5,7 +5,7 @@ metadata:
name: kustomization-editor-role
rules:
- apiGroups:
- kustomize.toolkit.fluxcd.io
- kustomize.fluxcd.io
resources:
- kustomizations
verbs:
@ -17,7 +17,7 @@ rules:
- update
- watch
- apiGroups:
- kustomize.toolkit.fluxcd.io
- kustomize.fluxcd.io
resources:
- kustomizations/status
verbs:

View File

@ -5,7 +5,7 @@ metadata:
name: kustomization-viewer-role
rules:
- apiGroups:
- kustomize.toolkit.fluxcd.io
- kustomize.fluxcd.io
resources:
- kustomizations
verbs:
@ -13,7 +13,7 @@ rules:
- list
- watch
- apiGroups:
- kustomize.toolkit.fluxcd.io
- kustomize.fluxcd.io
resources:
- kustomizations/status
verbs:

View File

@ -4,41 +4,29 @@ kind: Role
metadata:
name: leader-election-role
rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- configmaps/status
verbs:
- get
- update
- patch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- apiGroups:
- "coordination.k8s.io"
resources:
- leases
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- configmaps/status
verbs:
- get
- update
- patch
- apiGroups:
- ""
resources:
- events
verbs:
- create

View File

@ -0,0 +1,24 @@
# permissions for end users to edit profiles.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: profile-editor-role
rules:
- apiGroups:
- kustomize.fluxcd.io
resources:
- profiles
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- kustomize.fluxcd.io
resources:
- profiles/status
verbs:
- get

View File

@ -0,0 +1,20 @@
# permissions for end users to view profiles.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: profile-viewer-role
rules:
- apiGroups:
- kustomize.fluxcd.io
resources:
- profiles
verbs:
- get
- list
- watch
- apiGroups:
- kustomize.fluxcd.io
resources:
- profiles/status
verbs:
- get

View File

@ -1,34 +1,13 @@
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: manager-role
rules:
- apiGroups:
- ""
resources:
- configmaps
- secrets
- serviceaccounts
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- apiGroups:
- ""
resources:
- serviceaccounts/token
verbs:
- create
- apiGroups:
- kustomize.toolkit.fluxcd.io
- kustomize.fluxcd.io
resources:
- kustomizations
verbs:
@ -40,17 +19,7 @@ rules:
- update
- watch
- apiGroups:
- kustomize.toolkit.fluxcd.io
resources:
- kustomizations/finalizers
verbs:
- create
- delete
- get
- patch
- update
- apiGroups:
- kustomize.toolkit.fluxcd.io
- kustomize.fluxcd.io
resources:
- kustomizations/status
verbs:
@ -58,20 +27,36 @@ rules:
- patch
- update
- apiGroups:
- source.toolkit.fluxcd.io
- kustomize.fluxcd.io
resources:
- profiles
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- kustomize.fluxcd.io
resources:
- profiles/status
verbs:
- get
- patch
- update
- apiGroups:
- source.fluxcd.io
resources:
- buckets
- gitrepositories
- ocirepositories
verbs:
- get
- list
- watch
- apiGroups:
- source.toolkit.fluxcd.io
- source.fluxcd.io
resources:
- buckets/status
- gitrepositories/status
- ocirepositories/status
verbs:
- get

View File

@ -1,33 +0,0 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: webapp-dev
spec:
interval: 5m
path: "./deploy/webapp/"
prune: true
sourceRef:
kind: GitRepository
name: webapp-latest
wait: true
timeout: 2m
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: webapp-production
spec:
interval: 15m
path: "./deploy/overlays/production/"
prune: true
sourceRef:
kind: GitRepository
name: webapp-releases
healthChecks:
- kind: Deployment
name: backend
namespace: production
- kind: Deployment
name: frontend
namespace: production
timeout: 2m

View File

@ -0,0 +1,43 @@
apiVersion: kustomize.fluxcd.io/v1alpha1
kind: Kustomization
metadata:
name: podinfo-dev
spec:
interval: 5m
path: "./overlays/dev/"
prune: "env=dev"
suspend: false
sourceRef:
kind: GitRepository
name: podinfo
healthChecks:
- kind: Deployment
name: podinfo
namespace: dev
timeout: 80s
---
apiVersion: kustomize.fluxcd.io/v1alpha1
kind: Kustomization
metadata:
name: podinfo-staging
spec:
interval: 5m
path: "./overlays/staging/"
prune: "env=staging"
sourceRef:
kind: GitRepository
name: podinfo
validation: client
---
apiVersion: kustomize.fluxcd.io/v1alpha1
kind: Kustomization
metadata:
name: podinfo-production
spec:
interval: 5m
path: "./overlays/production/"
prune: "env=production"
sourceRef:
kind: GitRepository
name: podinfo-releases
validation: client

View File

@ -0,0 +1,13 @@
apiVersion: kustomize.fluxcd.io/v1alpha1
kind: Profile
metadata:
name: default
spec:
alert:
type: slack
address: https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK
username: kustomize-controller
channel: general
verbosity: info
kustomizations:
- '*'

View File

@ -1,19 +0,0 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: webapp-latest
spec:
interval: 1m
url: https://github.com/stefanprodan/podinfo
ref:
branch: master
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: webapp-releases
spec:
interval: 1m
url: https://github.com/stefanprodan/podinfo
ref:
semver: ">=3.2.3"

View File

@ -0,0 +1,19 @@
apiVersion: source.fluxcd.io/v1alpha1
kind: GitRepository
metadata:
name: podinfo
spec:
interval: 1m
url: https://github.com/stefanprodan/podinfo-deploy
ref:
branch: master
---
apiVersion: source.fluxcd.io/v1alpha1
kind: GitRepository
metadata:
name: podinfo-releases
spec:
interval: 1m
url: https://github.com/stefanprodan/podinfo-deploy
ref:
semver: ">=0.0.1"

View File

@ -1,23 +0,0 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: certs
spec:
interval: 15m
url: https://github.com/stefanprodan/kustomizer
ref:
tag: "v1.1.0"
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: certs
spec:
interval: 10m
path: "./testdata/certs"
prune: true
sourceRef:
kind: GitRepository
name: certs
wait: true
timeout: 2m

View File

@ -1,19 +0,0 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: frontend
spec:
dependsOn:
- name: common
- name: backend
interval: 5m
path: "./deploy/webapp/frontend/"
prune: true
sourceRef:
kind: GitRepository
name: webapp
healthChecks:
- kind: Deployment
name: frontend
namespace: webapp
timeout: 2m

View File

@ -1,9 +0,0 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: webapp
spec:
interval: 10m
url: https://github.com/stefanprodan/podinfo
ref:
semver: ">=6.3.5"

19
config/testdata/generate/generate.yaml vendored Normal file
View File

@ -0,0 +1,19 @@
apiVersion: kustomize.fluxcd.io/v1alpha1
kind: Kustomization
metadata:
name: generate
spec:
interval: 5m
generate: true
path: "./plain/"
prune: "env=test"
sourceRef:
kind: GitRepository
name: webapp
healthChecks:
- kind: Deployment
name: backend
namespace: test
- kind: Deployment
name: frontend
namespace: test

View File

@ -0,0 +1,9 @@
apiVersion: source.fluxcd.io/v1alpha1
kind: GitRepository
metadata:
name: webapp
spec:
interval: 10m
url: https://github.com/stefanprodan/podinfo-deploy
ref:
branch: master

View File

@ -1,6 +1,6 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- gitrepository.yaml
- generate.yaml

View File

@ -1,72 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: impersonation
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: gotk-reconciler
namespace: impersonation
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: gotk-reconciler
namespace: impersonation
rules:
- apiGroups: ['*']
resources: ['*']
verbs: ['*']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: gotk-reconciler
namespace: impersonation
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: gotk-reconciler
subjects:
- kind: ServiceAccount
name: gotk-reconciler
namespace: impersonation
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: podinfo
namespace: impersonation
spec:
interval: 5m
url: https://github.com/stefanprodan/podinfo
ref:
tag: "6.3.5"
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: podinfo
namespace: impersonation
spec:
targetNamespace: impersonation
serviceAccountName: gotk-reconciler
interval: 5m
path: "./kustomize"
prune: true
sourceRef:
kind: GitRepository
name: podinfo
patches:
- patch: |
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: podinfo
spec:
minReplicas: 1
target:
kind: HorizontalPodAutoscaler
wait: true
timeout: 1m

View File

@ -1,23 +0,0 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: podinfo
spec:
interval: 15m
path: "./kustomize/"
prune: true
sourceRef:
kind: GitRepository
name: podinfo
timeout: 1m
targetNamespace: managed-fields
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: podinfo
spec:
interval: 5m
url: https://github.com/stefanprodan/podinfo
ref:
semver: "6.3.5"

View File

@ -1,37 +0,0 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
name: oci
namespace: oci
spec:
interval: 10m
url: oci://ghcr.io/stefanprodan/manifests/podinfo
ref:
tag: "6.3.5"
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: oci
namespace: oci
spec:
targetNamespace: oci
interval: 10m
path: "./"
prune: true
sourceRef:
kind: OCIRepository
name: oci
wait: true
timeout: 2m
patches:
- patch: |-
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: podinfo
spec:
minReplicas: 1
target:
name: podinfo
kind: HorizontalPodAutoscaler

View File

@ -1,6 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- staging.yaml
- production.yaml

View File

@ -1,29 +0,0 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: webapp-production
spec:
interval: 15m
path: "./deploy/overlays/production/"
prune: true
sourceRef:
kind: GitRepository
name: webapp-releases
healthChecks:
- kind: Deployment
name: backend
namespace: production
- kind: Deployment
name: frontend
namespace: production
timeout: 2m
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: webapp-releases
spec:
interval: 5m
url: https://github.com/stefanprodan/podinfo
ref:
semver: ">=6.3.5"

View File

@ -1,29 +0,0 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: webapp-staging
spec:
interval: 5m
path: "./deploy/overlays/staging/"
prune: true
sourceRef:
kind: GitRepository
name: webapp-releases
healthChecks:
- kind: Deployment
name: backend
namespace: staging
- kind: Deployment
name: frontend
namespace: staging
timeout: 2m
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: webapp-latest
spec:
interval: 5m
url: https://github.com/stefanprodan/podinfo
ref:
branch: master

View File

@ -1,4 +0,0 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: status-defaults

View File

@ -1,13 +1,13 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
apiVersion: kustomize.fluxcd.io/v1alpha1
kind: Kustomization
metadata:
name: backend
spec:
dependsOn:
- name: common
- common
interval: 5m
path: "./deploy/webapp/backend/"
prune: true
path: "./webapp/backend/"
prune: "part-of=webapp,component=backend"
sourceRef:
kind: GitRepository
name: webapp
@ -15,4 +15,3 @@ spec:
- kind: Deployment
name: backend
namespace: webapp
timeout: 2m

View File

@ -1,11 +1,12 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
apiVersion: kustomize.fluxcd.io/v1alpha1
kind: Kustomization
metadata:
name: common
spec:
interval: 5m
path: "./deploy/webapp/common/"
prune: true
path: "./webapp/common/"
prune: "part-of=webapp"
sourceRef:
kind: GitRepository
name: webapp
validate: client

14
config/testdata/webapp/frontend.yaml vendored Normal file
View File

@ -0,0 +1,14 @@
apiVersion: kustomize.fluxcd.io/v1alpha1
kind: Kustomization
metadata:
name: frontend
spec:
dependsOn:
- common
- backend
interval: 5m
path: "./webapp/frontend/"
prune: "part-of=webapp,component=frontend"
sourceRef:
kind: GitRepository
name: webapp

View File

@ -0,0 +1,9 @@
apiVersion: source.fluxcd.io/v1alpha1
kind: GitRepository
metadata:
name: webapp
spec:
interval: 10m
url: https://github.com/stefanprodan/podinfo-deploy
ref:
branch: master

View File

@ -1,7 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- source.yaml
- gitrepository.yaml
- common.yaml
- backend.yaml
- frontend.yaml

14
config/testdata/webapp/test.yaml vendored Normal file
View File

@ -0,0 +1,14 @@
apiVersion: kustomize.fluxcd.io/v1alpha1
kind: Kustomization
metadata:
name: test
spec:
dependsOn:
- common
interval: 5m
path: "./webapp/test/"
prune: "part-of=webapp,component=test"
sourceRef:
kind: GitRepository
name: webapp
validation: client

View File

@ -0,0 +1,99 @@
/*
Copyright 2020 The Flux CD contributors.
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"
"strings"
"time"
"github.com/go-logr/logr"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1alpha1"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
)
// KustomizationReconciler watches a GitRepository object
type GitRepositoryWatcher struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
}
// +kubebuilder:rbac:groups=source.fluxcd.io,resources=gitrepositories,verbs=get;list;watch
// +kubebuilder:rbac:groups=source.fluxcd.io,resources=gitrepositories/status,verbs=get
func (r *GitRepositoryWatcher) Reconcile(req ctrl.Request) (ctrl.Result, error) {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
var repo sourcev1.GitRepository
if err := r.Get(ctx, req.NamespacedName, &repo); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
log := r.Log.WithValues(strings.ToLower(repo.Kind), req.NamespacedName)
log.Info("New artifact detected")
// get the list of kustomizations that are using this Git repository
var list kustomizev1.KustomizationList
if err := r.List(ctx, &list, client.InNamespace(req.Namespace),
client.MatchingFields{kustomizev1.SourceIndexKey: req.Name}); err != nil {
log.Error(err, "unable to list kustomizations")
return ctrl.Result{}, err
}
// trigger apply for each kustomization using this Git repository
for _, kustomization := range list.Items {
kustomization.Annotations[kustomizev1.SyncAtAnnotation] = metav1.Now().String()
if kustomization.Spec.SourceRef.APIGroup == nil {
emptyAPIGroup := ""
kustomization.Spec.SourceRef.APIGroup = &emptyAPIGroup
}
if err := r.Update(ctx, &kustomization); err != nil {
log.Error(err, "unable to annotate kustomization", "kustomization", kustomization.GetName())
}
log.Info("Run kustomization", "kustomization", kustomization.GetName())
}
return ctrl.Result{}, nil
}
func (r *GitRepositoryWatcher) SetupWithManager(mgr ctrl.Manager) error {
// create a kustomization index based on Git repository name
err := mgr.GetFieldIndexer().IndexField(&kustomizev1.Kustomization{}, kustomizev1.SourceIndexKey,
func(rawObj runtime.Object) []string {
k := rawObj.(*kustomizev1.Kustomization)
if k.Spec.SourceRef.Kind == "GitRepository" {
return []string{k.Spec.SourceRef.Name}
}
return nil
},
)
if err != nil {
return err
}
return ctrl.NewControllerManagedBy(mgr).
For(&sourcev1.GitRepository{}).
WithEventFilter(GitRepositoryRevisionChangePredicate{}).
Complete(r)
}

View File

@ -0,0 +1,63 @@
/*
Copyright 2020 The Flux CD contributors.
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 (
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/predicate"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
)
type GitRepositoryRevisionChangePredicate struct {
predicate.Funcs
}
func (GitRepositoryRevisionChangePredicate) Update(e event.UpdateEvent) bool {
if e.MetaOld == nil || e.MetaNew == nil {
return false
}
oldRepo, ok := e.ObjectOld.(*sourcev1.GitRepository)
if !ok {
return false
}
newRepo, ok := e.ObjectNew.(*sourcev1.GitRepository)
if !ok {
return false
}
if oldRepo.GetArtifact() == nil && newRepo.GetArtifact() != nil {
return true
}
if oldRepo.GetArtifact() != nil && newRepo.GetArtifact() != nil &&
oldRepo.GetArtifact().Revision != newRepo.GetArtifact().Revision {
return true
}
return false
}
func (GitRepositoryRevisionChangePredicate) Create(e event.CreateEvent) bool {
return false
}
func (GitRepositoryRevisionChangePredicate) Delete(e event.DeleteEvent) bool {
return false
}

View File

@ -0,0 +1,605 @@
/*
Copyright 2020 The Flux CD contributors.
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 (
"bufio"
"bytes"
"context"
"errors"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"strings"
"text/template"
"time"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1alpha1"
"github.com/fluxcd/kustomize-controller/internal/alert"
"github.com/fluxcd/kustomize-controller/internal/lockedfile"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
)
// KustomizationReconciler reconciles a Kustomization object
type KustomizationReconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
}
// +kubebuilder:rbac:groups=kustomize.fluxcd.io,resources=kustomizations,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=kustomize.fluxcd.io,resources=kustomizations/status,verbs=get;update;patch
func (r *KustomizationReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
ctx := context.Background()
syncStart := time.Now()
var kustomization kustomizev1.Kustomization
if err := r.Get(ctx, req.NamespacedName, &kustomization); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
log := r.Log.WithValues(strings.ToLower(kustomization.Kind), req.NamespacedName)
if kustomization.Spec.Suspend {
msg := "Kustomization is suspended, skipping execution"
kustomization = kustomizev1.KustomizationNotReady(kustomization, kustomizev1.SuspendedReason, msg)
if err := r.Status().Update(ctx, &kustomization); err != nil {
log.Error(err, "unable to update Kustomization status")
return ctrl.Result{Requeue: true}, err
}
log.Info(msg)
return ctrl.Result{}, nil
}
var source sourcev1.Source
// get artifact source from Git repository
if kustomization.Spec.SourceRef.Kind == "GitRepository" {
var repository sourcev1.GitRepository
repositoryName := types.NamespacedName{
Namespace: kustomization.GetNamespace(),
Name: kustomization.Spec.SourceRef.Name,
}
err := r.Client.Get(ctx, repositoryName, &repository)
if err != nil {
log.Error(err, "GitRepository not found", "gitrepository", repositoryName)
return ctrl.Result{Requeue: true}, err
}
source = &repository
}
if source == nil {
err := fmt.Errorf("source `%s` kind '%s' not supported",
kustomization.Spec.SourceRef.Name, kustomization.Spec.SourceRef.Kind)
return ctrl.Result{}, err
}
// check source readiness
if source.GetArtifact() == nil {
msg := "Source is not ready"
kustomization = kustomizev1.KustomizationNotReady(kustomization, kustomizev1.ArtifactFailedReason, msg)
if err := r.Status().Update(ctx, &kustomization); err != nil {
log.Error(err, "unable to update Kustomization status")
return ctrl.Result{Requeue: true}, err
}
log.Info(msg)
return ctrl.Result{}, nil
}
// check dependencies
if len(kustomization.Spec.DependsOn) > 0 {
if err := r.checkDependencies(kustomization); err != nil {
kustomization = kustomizev1.KustomizationNotReady(kustomization, kustomizev1.DependencyNotReadyReason, err.Error())
if err := r.Status().Update(ctx, &kustomization); err != nil {
log.Error(err, "unable to update Kustomization status")
return ctrl.Result{Requeue: true}, err
}
// we can't rely on exponential backoff because it will prolong the execution too much,
// instead we requeue every half a minute.
requeueAfter := 30 * time.Second
msg := fmt.Sprintf("Dependencies do not meet ready condition, retrying in %s", requeueAfter.String())
log.Error(err, msg)
r.alert(kustomization, msg, "info")
return ctrl.Result{RequeueAfter: requeueAfter}, nil
}
log.Info("All dependencies area ready, proceeding with apply")
}
// try sync
syncedKustomization, err := r.sync(*kustomization.DeepCopy(), source)
if err != nil {
log.Error(err, "Kustomization apply failed")
r.alert(kustomization, err.Error(), "error")
}
// update status
if err := r.Status().Update(ctx, &syncedKustomization); err != nil {
log.Error(err, "unable to update Kustomization status")
return ctrl.Result{Requeue: true}, err
}
// log sync duration
log.Info(fmt.Sprintf("Kustomization sync finished in %s, next run in %s",
time.Now().Sub(syncStart).String(),
kustomization.Spec.Interval.Duration.String(),
))
// requeue kustomization
return ctrl.Result{RequeueAfter: kustomization.Spec.Interval.Duration}, nil
}
func (r *KustomizationReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&kustomizev1.Kustomization{}).
WithEventFilter(KustomizationGarbageCollectPredicate{Log: r.Log}).
WithEventFilter(KustomizationSyncAtPredicate{}).
WithOptions(controller.Options{MaxConcurrentReconciles: 4}).
Complete(r)
}
func (r *KustomizationReconciler) sync(
kustomization kustomizev1.Kustomization,
source sourcev1.Source) (kustomizev1.Kustomization, error) {
// acquire lock
unlock, err := r.lock(fmt.Sprintf("%s-%s", kustomization.GetName(), kustomization.GetNamespace()))
if err != nil {
err = fmt.Errorf("tmp dir error: %w", err)
return kustomizev1.KustomizationNotReady(kustomization, sourcev1.StorageOperationFailedReason, err.Error()), err
}
defer unlock()
// create tmp dir
tmpDir, err := ioutil.TempDir("", kustomization.Name)
if err != nil {
err = fmt.Errorf("tmp dir error: %w", err)
return kustomizev1.KustomizationNotReady(kustomization, sourcev1.StorageOperationFailedReason, err.Error()), err
}
defer os.RemoveAll(tmpDir)
// download artifact and extract files
err = r.download(kustomization, source.GetArtifact().URL, tmpDir)
if err != nil {
return kustomizev1.KustomizationNotReady(
kustomization,
kustomizev1.ArtifactFailedReason,
"artifact acquisition failed",
), err
}
// check build path exists
if _, err := os.Stat(path.Join(tmpDir, kustomization.Spec.Path)); err != nil {
err = fmt.Errorf("kustomization path not found: %w", err)
return kustomizev1.KustomizationNotReady(
kustomization,
kustomizev1.ArtifactFailedReason,
err.Error(),
), err
}
// generate kustomization.yaml
err = r.generate(kustomization, tmpDir)
if err != nil {
return kustomizev1.KustomizationNotReady(
kustomization,
kustomizev1.BuildFailedReason,
"kustomize create failed",
), err
}
// kustomize build
err = r.build(kustomization, tmpDir)
if err != nil {
return kustomizev1.KustomizationNotReady(
kustomization,
kustomizev1.BuildFailedReason,
"kustomize build failed",
), err
}
// dry-run apply
err = r.validate(kustomization, tmpDir)
if err != nil {
return kustomizev1.KustomizationNotReady(
kustomization,
kustomizev1.ValidationFailedReason,
fmt.Sprintf("%s-side validation failed", kustomization.Spec.Validation),
), err
}
// apply
err = r.apply(kustomization, tmpDir)
if err != nil {
return kustomizev1.KustomizationNotReady(
kustomization,
kustomizev1.ApplyFailedReason,
"apply failed",
), err
}
// health assessment
err = r.checkHealth(kustomization)
if err != nil {
return kustomizev1.KustomizationNotReady(
kustomization,
kustomizev1.HealthCheckFailedReason,
"health check failed",
), err
}
return kustomizev1.KustomizationReady(
kustomization,
source.GetArtifact().Revision,
kustomizev1.ApplySucceedReason,
"kustomization was successfully applied",
), nil
}
func (r *KustomizationReconciler) download(kustomization kustomizev1.Kustomization, url string, tmpDir string) error {
timeout := kustomization.GetTimeout() + (time.Second * 1)
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
cmd := fmt.Sprintf("cd %s && curl -sL %s | tar -xz --strip-components=1 -C .",
tmpDir, url)
command := exec.CommandContext(ctx, "/bin/sh", "-c", cmd)
output, err := command.CombinedOutput()
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
return err
}
return fmt.Errorf("artifact `%s` download failed: %s", url, string(output))
}
return nil
}
func (r *KustomizationReconciler) generate(kustomization kustomizev1.Kustomization, tmpDir string) error {
if !kustomization.Spec.Generate {
return nil
}
timeout := kustomization.GetTimeout() + (time.Second * 1)
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
dirPath := path.Join(tmpDir, kustomization.Spec.Path)
cmd := fmt.Sprintf("cd %s && kustomize create --autodetect --recursive", dirPath)
command := exec.CommandContext(ctx, "/bin/sh", "-c", cmd)
output, err := command.CombinedOutput()
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
return err
}
return fmt.Errorf("kustomize create failed: %s", string(output))
}
if err := r.generateLabelTransformer(kustomization, dirPath); err != nil {
return err
}
return nil
}
func (r *KustomizationReconciler) generateLabelTransformer(kustomization kustomizev1.Kustomization, dirPath string) error {
labelTransformer := `
apiVersion: builtin
kind: LabelTransformer
metadata:
name: labels
labels:
{{- range $key, $value := . }}
{{ $key }}: {{ $value }}
{{- end }}
fieldSpecs:
- path: metadata/labels
create: true
`
transformers := `
transformers:
- gc-labels.yaml
`
prune := kustomization.Spec.Prune
if prune == "" {
return nil
}
// transform prune into label selectors
selectors := make(map[string]string)
for _, ls := range strings.Split(prune, ",") {
if kv := strings.Split(ls, "="); len(kv) == 2 {
selectors[kv[0]] = kv[1]
}
}
t, err := template.New("tmpl").Parse(labelTransformer)
if err != nil {
return fmt.Errorf("labelTransformer template parsing failed: %w", err)
}
var data bytes.Buffer
writer := bufio.NewWriter(&data)
if err := t.Execute(writer, selectors); err != nil {
return fmt.Errorf("labelTransformer template excution failed: %w", err)
}
if err := writer.Flush(); err != nil {
return fmt.Errorf("labelTransformer flush failed: %w", err)
}
file, err := os.Create(path.Join(dirPath, "gc-labels.yaml"))
if err != nil {
return fmt.Errorf("labelTransformer create failed: %w", err)
}
defer file.Close()
if _, err := file.WriteString(data.String()); err != nil {
return fmt.Errorf("labelTransformer write failed: %w", err)
}
if err := file.Sync(); err != nil {
return fmt.Errorf("labelTransformer sync failed: %w", err)
}
kfile, err := os.OpenFile(path.Join(dirPath, "kustomization.yaml"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("kustomization file open failed: %w", err)
}
defer kfile.Close()
if _, err := kfile.WriteString(transformers); err != nil {
return fmt.Errorf("kustomization file append failed: %w", err)
}
return nil
}
func (r *KustomizationReconciler) build(kustomization kustomizev1.Kustomization, tmpDir string) error {
timeout := kustomization.GetTimeout() + (time.Second * 1)
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
cmd := fmt.Sprintf("cd %s && kustomize build %s > %s.yaml",
tmpDir, kustomization.Spec.Path, kustomization.GetName())
command := exec.CommandContext(ctx, "/bin/sh", "-c", cmd)
output, err := command.CombinedOutput()
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
return err
}
return fmt.Errorf("kustomize build failed: %s", string(output))
}
return nil
}
func (r *KustomizationReconciler) validate(kustomization kustomizev1.Kustomization, tmpDir string) error {
if kustomization.Spec.Validation == "" {
return nil
}
timeout := kustomization.GetTimeout() + (time.Second * 1)
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
cmd := fmt.Sprintf("cd %s && kubectl apply -f %s.yaml --timeout=%s --dry-run=%s",
tmpDir, kustomization.GetName(), kustomization.GetTimeout().String(), kustomization.Spec.Validation)
command := exec.CommandContext(ctx, "/bin/sh", "-c", cmd)
output, err := command.CombinedOutput()
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
return fmt.Errorf("validation timeout: %w", err)
}
return fmt.Errorf("validation failed: %s", string(output))
}
return nil
}
func (r *KustomizationReconciler) apply(kustomization kustomizev1.Kustomization, tmpDir string) error {
start := time.Now()
timeout := kustomization.GetTimeout() + (time.Second * 1)
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
cmd := fmt.Sprintf("cd %s && kubectl apply -f %s.yaml --timeout=%s",
tmpDir, kustomization.GetName(), kustomization.Spec.Interval.Duration.String())
if kustomization.Spec.Prune != "" {
cmd = fmt.Sprintf("%s --prune -l %s", cmd, kustomization.Spec.Prune)
}
command := exec.CommandContext(ctx, "/bin/sh", "-c", cmd)
output, err := command.CombinedOutput()
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
return fmt.Errorf("apply timeout: %w", err)
}
return fmt.Errorf("apply failed: %s", string(output))
}
resources := r.parseApplyOutput(output)
r.Log.WithValues(
strings.ToLower(kustomization.Kind),
fmt.Sprintf("%s/%s", kustomization.GetNamespace(), kustomization.GetName()),
).Info(
fmt.Sprintf("Kustomization applied in %s",
time.Now().Sub(start).String()),
"output", resources,
)
var diff bool
for _, action := range resources {
if action != "unchanged" {
diff = true
break
}
}
if diff {
r.alert(kustomization, string(output), "info")
}
return nil
}
func (r *KustomizationReconciler) checkHealth(kustomization kustomizev1.Kustomization) error {
timeout := kustomization.GetTimeout() + (time.Second * 1)
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
var alerts string
for _, check := range kustomization.Spec.HealthChecks {
cmd := fmt.Sprintf("kubectl -n %s rollout status %s %s --timeout=%s",
check.Namespace, check.Kind, check.Name, kustomization.GetTimeout())
command := exec.CommandContext(ctx, "/bin/sh", "-c", cmd)
output, err := command.CombinedOutput()
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
return fmt.Errorf("health check timeout for %s '%s/%s': %w",
check.Kind, check.Namespace, check.Name, err)
}
return fmt.Errorf("health check failed for %s '%s/%s': %s",
check.Kind, check.Namespace, check.Name, string(output))
} else {
msg := fmt.Sprintf("Health check passed for %s '%s/%s'",
check.Kind, check.Namespace, check.Name)
r.Log.WithValues(
strings.ToLower(kustomization.Kind),
fmt.Sprintf("%s/%s", kustomization.GetNamespace(), kustomization.GetName()),
).Info(msg)
alerts += msg + "\n"
}
}
if alerts != "" {
r.alert(kustomization, alerts, "info")
}
return nil
}
func (r *KustomizationReconciler) lock(name string) (unlock func(), err error) {
lockFile := path.Join(os.TempDir(), name+".lock")
mutex := lockedfile.MutexAt(lockFile)
return mutex.Lock()
}
func (r *KustomizationReconciler) parseApplyOutput(in []byte) map[string]string {
result := make(map[string]string)
input := strings.Split(string(in), "\n")
if len(input) == 0 {
return result
}
var parts []string
for _, str := range input {
if str != "" {
parts = append(parts, str)
}
}
for _, str := range parts {
kv := strings.Split(str, " ")
if len(kv) > 1 {
result[kv[0]] = kv[1]
}
}
return result
}
func (r *KustomizationReconciler) checkDependencies(kustomization kustomizev1.Kustomization) error {
for _, dep := range kustomization.Spec.DependsOn {
depName := types.NamespacedName{
Namespace: kustomization.GetNamespace(),
Name: dep,
}
var k kustomizev1.Kustomization
err := r.Get(context.Background(), depName, &k)
if err != nil {
return fmt.Errorf("unable to get '%s' dependency: %w", depName, err)
}
if len(k.Status.Conditions) == 0 {
return fmt.Errorf("dependency '%s' is not ready", depName)
}
for _, condition := range k.Status.Conditions {
if condition.Type == kustomizev1.ReadyCondition && condition.Status != corev1.ConditionTrue {
return fmt.Errorf("dependency '%s' is not ready", depName)
}
}
}
return nil
}
func (r *KustomizationReconciler) getProfiles(kustomization kustomizev1.Kustomization) ([]kustomizev1.Profile, error) {
list := make([]kustomizev1.Profile, 0)
var profiles kustomizev1.ProfileList
err := r.List(context.TODO(), &profiles, client.InNamespace(kustomization.GetNamespace()))
if err != nil {
return list, err
}
// filter profiles that match this kustomization taking into account '*' wildcard
for _, profile := range profiles.Items {
for _, name := range profile.Spec.Kustomizations {
if name == kustomization.GetName() || name == "*" {
list = append(list, profile)
break
}
}
}
return list, nil
}
func (r *KustomizationReconciler) alert(kustomization kustomizev1.Kustomization, msg string, verbosity string) {
profiles, err := r.getProfiles(kustomization)
if err != nil {
r.Log.WithValues(
strings.ToLower(kustomization.Kind),
fmt.Sprintf("%s/%s", kustomization.GetNamespace(), kustomization.GetName()),
).Error(err, "unable to list profiles")
return
}
for _, profile := range profiles {
if settings := profile.Spec.Alert; settings != nil {
provider, err := alert.NewProvider(settings.Type, settings.Address, settings.Username, settings.Channel)
if err != nil {
r.Log.WithValues(
strings.ToLower(kustomization.Kind),
fmt.Sprintf("%s/%s", kustomization.GetNamespace(), kustomization.GetName()),
).Error(err, "unable to configure alert provider")
continue
}
if settings.Verbosity == verbosity || verbosity == "error" {
err = provider.Post(kustomization.GetName(), kustomization.GetNamespace(), msg, verbosity)
if err != nil {
r.Log.WithValues(
strings.ToLower(kustomization.Kind),
fmt.Sprintf("%s/%s", kustomization.GetNamespace(), kustomization.GetName()),
).Error(err, "unable to send alert")
}
}
}
}
}

View File

@ -0,0 +1,89 @@
/*
Copyright 2020 The Flux CD contributors.
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"
"os/exec"
"github.com/go-logr/logr"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/predicate"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1alpha1"
)
type KustomizationSyncAtPredicate struct {
predicate.Funcs
}
func (KustomizationSyncAtPredicate) Update(e event.UpdateEvent) bool {
if e.MetaOld == nil || e.MetaNew == nil {
// ignore objects without metadata
return false
}
if e.MetaNew.GetGeneration() != e.MetaOld.GetGeneration() {
// reconcile on spec changes
return true
}
// handle syncAt annotation
if val, ok := e.MetaNew.GetAnnotations()[kustomizev1.SyncAtAnnotation]; ok {
if valOld, okOld := e.MetaOld.GetAnnotations()[kustomizev1.SyncAtAnnotation]; okOld {
if val != valOld {
return true
}
} else {
return true
}
}
return false
}
type KustomizationGarbageCollectPredicate struct {
predicate.Funcs
Log logr.Logger
}
// Delete removes all Kubernetes objects based on the prune label selector.
func (gc KustomizationGarbageCollectPredicate) Delete(e event.DeleteEvent) bool {
if k, ok := e.Object.(*kustomizev1.Kustomization); ok {
if k.Spec.Prune != "" && !k.Spec.Suspend {
timeout := k.GetTimeout()
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
resources := `$(kubectl api-resources --verbs=delete -o name | tr "\n" "," | sed -e 's/,$//')`
cmd := fmt.Sprintf("kubectl delete %s --all-namespaces --timeout=%s -l %s",
resources, timeout.String(), k.Spec.Prune)
command := exec.CommandContext(ctx, "/bin/sh", "-c", cmd)
if output, err := command.CombinedOutput(); err != nil {
gc.Log.Error(err, "Garbage collection failed",
"kustomization", fmt.Sprintf("%s/%s", k.GetNamespace(), k.GetName()),
"output", string(output))
} else {
gc.Log.Info("Garbage collection completed",
"kustomization", fmt.Sprintf("%s/%s", k.GetNamespace(), k.GetName()),
"output", string(output))
}
}
}
return true
}

View File

@ -0,0 +1,84 @@
/*
Copyright 2020 The Flux CD contributors.
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"
"strings"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1alpha1"
)
// ProfileReconciler reconciles a Profile object
type ProfileReconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
}
// +kubebuilder:rbac:groups=kustomize.fluxcd.io,resources=profiles,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=kustomize.fluxcd.io,resources=profiles/status,verbs=get;update;patch
func (r *ProfileReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
ctx := context.Background()
var profile kustomizev1.Profile
if err := r.Get(ctx, req.NamespacedName, &profile); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
log := r.Log.WithValues(strings.ToLower(profile.Kind), req.NamespacedName)
init := true
for _, condition := range profile.Status.Conditions {
if condition.Type == kustomizev1.ReadyCondition && condition.Status == corev1.ConditionTrue {
init = false
break
}
}
if init {
profile.Status.Conditions = []kustomizev1.Condition{
{
Type: kustomizev1.ReadyCondition,
Status: corev1.ConditionTrue,
LastTransitionTime: metav1.Now(),
Reason: kustomizev1.InitializedReason,
Message: kustomizev1.InitializedReason,
},
}
if err := r.Status().Update(ctx, &profile); err != nil {
return ctrl.Result{Requeue: true}, err
}
log.Info("Profile initialised")
}
return ctrl.Result{}, nil
}
func (r *ProfileReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&kustomizev1.Profile{}).
Complete(r)
}

81
controllers/suite_test.go Normal file
View File

@ -0,0 +1,81 @@
/*
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 (
"path/filepath"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"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"
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
kustomizev1alpha1 "github.com/fluxcd/kustomize-controller/api/v1alpha1"
// +kubebuilder:scaffold:imports
)
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
var cfg *rest.Config
var k8sClient client.Client
var testEnv *envtest.Environment
func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecsWithDefaultAndCustomReporters(t,
"Controller Suite",
[]Reporter{printer.NewlineReporter{}})
}
var _ = BeforeSuite(func(done Done) {
logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))
By("bootstrapping test environment")
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
}
var err error
cfg, err = testEnv.Start()
Expect(err).ToNot(HaveOccurred())
Expect(cfg).ToNot(BeNil())
err = kustomizev1alpha1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
// +kubebuilder:scaffold:scheme
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
Expect(err).ToNot(HaveOccurred())
Expect(k8sClient).ToNot(BeNil())
close(done)
}, 60)
var _ = AfterSuite(func() {
By("tearing down the test environment")
err := testEnv.Stop()
Expect(err).ToNot(HaveOccurred())
})

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Some files were not shown because too many files have changed in this diff Show More